/* * Copyright (c) 2005 Mark Fullmer * Copyright (c) 2009 Mark Fullmer and the Ohio State University * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: acr30.c 28 2009-11-29 23:09:09Z maf $ */ #include #include #include #include #include #include #include #include #include #include #include "str.h" #include "xerr.h" #include "acr30.h" #if HAVE_STRINGS_H #include #endif #if HAVE_STRING_H #include #endif /* ACR30 debug format buffer */ #define DBG_FMT_BUF_LEN 1024 /* enable verbose debugging */ #define ACR30_DEBUG /* * * embedded driver for ACR30S based readers. * http://www.acs.com.hk/drivers/eng/REF_ACx30.pdf included in doc * * Supports normal commands only. * * external: * acr30_open() - create context / open serial port * acr30_reset() - reset ARC30S * acr30_powerdown() - power down SC. Use reset to power up. * acr30_get_acr_stat() - get status of ACR30 * acr30_transaction() - perform SC transaction * acr30_close() - free context / close serial port * * internal: * acr30_read() - read output from SC * acr30_process_resp() - process SC response * acr30_tx_encode() - encode SC transaction * acr30_rx_decode() - decode SC transaction * acr30_checksw1sw2() - check ACR30S SW1SW2 field in transactions * not the same as SC SW1SW2 code. * acr30_comm_debug() - i/o debugging * acr30_ATR() - display SC ATR * */ /* * function: acr30_open() * * create context for ACR30S SC reader. Open serial port, * reset device, wait for card to be inserted. * * call acr30_close() to free resources. * * arguments: * * dev - serial port device name * debug - debugging level * * returns: allocated acr30_ctx, or 0L on failure. * */ struct acr30_ctx *acr30_open(char *dev, int debug) { struct termios t; struct acr30_ctx *acr30ctx; int ret; ret = -1; /* fail */ if (!(acr30ctx = (struct acr30_ctx*)malloc(sizeof *acr30ctx))) { xerr_warn("malloc(acr30ctx)"); goto acr30_open_out; } bzero(acr30ctx, (sizeof *acr30ctx)); acr30ctx->fd = -1; acr30ctx->debug = debug; if ((acr30ctx->fd = open(dev, O_RDWR)) < 0) { xerr_warn("open(%s)", dev); goto acr30_open_out; } if (tcgetattr(acr30ctx->fd, &t) < 0) { xerr_warn("tcgetattr()"); goto acr30_open_out; } cfmakeraw(&t); cfsetspeed(&t, B9600); t.c_cc[VTIME] = ACR30_TMOUT_READ; t.c_cc[VMIN] = 0; /* t.c_cflag = CS8|CREAD|CRTSCTS|HUPCL; */ if (tcsetattr(acr30ctx->fd, TCSANOW, &t) < 0) { xerr_warn("tcsetattr()"); goto acr30_open_out; } usleep(ACR30_OPEN_DELAY); /* * reset on open may have failed due to trash in the input buffer or * the serial driver misses the first few bytes when after the * open() to the ACR30S i/o port (PL2303 driver on Mac 10.5). * * power down the SC, flush the buffer, get the card status, reset * again. * */ if (acr30ctx->debug) xerr_warnx("Power cycling ACR30"); if (acr30_powerdown(acr30ctx) < 0) { xerr_warnx("acr30_powerdown(): failed."); goto acr30_open_out; } if (acr30_flush(acr30ctx) < 0) { xerr_warnx("acr30_flush(): failed."); goto acr30_open_out; } if (acr30_reset(acr30ctx) < 0) { xerr_warnx("acr30_reset(): failed."); goto acr30_open_out; } if (acr30_get_acr_stat(acr30ctx) < 0) { xerr_warnx("acr30_get_acr_stat(): failed."); goto acr30_open_out; } if (acr30_reset(acr30ctx) < 0) { xerr_warnx("acr30_reset(): failed."); goto acr30_open_out; } /* wait for card to be inserted */ while (!acr30ctx->card_inserted) { usleep(ACR30_INSERT_DELAY); if (acr30_get_acr_stat(acr30ctx) < 0) { xerr_warnx("acr30_get_acr_stat(): failed."); goto acr30_open_out; } if (acr30_reset(acr30ctx) < 0) { xerr_warnx("acr30_reset(): failed."); goto acr30_open_out; } } /* card not inserted */ ret = 0; /* success */ acr30_open_out: if (ret == -1) { if (acr30ctx) { if (acr30ctx->fd != -1) close(acr30ctx->fd); free (acr30ctx); acr30ctx = (struct acr30_ctx*)0L; } /* acr30ctx */ } /* ret == -1 */ return acr30ctx; } /* acr30_open */ /* * function: acr30_close() * * free context for acr30_ctx created with acr30_open. Close serial * port. * * arguments: * acr30ctx - context allocated with acr30_open() * * returns: 0 - success * <0 - failure * */ int acr30_close(struct acr30_ctx *acr30ctx) { if (acr30ctx) { if (acr30ctx->fd != -1) close(acr30ctx->fd); free (acr30ctx); return 0; } /* acr30ctx */ xerr_warnx("acr30_close(): *acr30ctx == 0L"); return -1; } /* acr30_close */ /* * function: acr30_reset() * * reset SC reader * * arguments: * acr30ctx - context allocated with acr30_open() * * returns: 0 - success * <0 - failure * */ int acr30_reset(struct acr30_ctx *acr30ctx) { int r, i; acr30ctx->tx.header = ACR30_HEADER_START; acr30ctx->tx.instruction = ACR30_CMD_RESET; acr30ctx->tx.data_len = 0; acr30_tx_encode(&acr30ctx->tx); #ifdef ACR30_DEBUG if (acr30ctx->debug) acr30_comm_debug(acr30ctx, 1); #endif /* ACR30_DEBUG */ if (write(acr30ctx->fd, acr30ctx->tx.encoded, acr30ctx->tx.encoded_len) < 0) { xerr_warn("write()"); return -1; } usleep(ACR30_RESET_DELAY); /* reset can take a while... */ for (i = 0; i < 2; ++i) { /* load decode buffer with 1 or more responses */ if (acr30_read(acr30ctx) < 0) { xerr_warnx("acr30_read(): failed."); close(acr30ctx->fd); return -1; } #ifdef ACR30_DEBUG if (acr30ctx->debug) acr30_comm_debug(acr30ctx, 0); #endif /* ACR30_DEBUG */ if ((r = acr30_process_resp(acr30ctx)) < 0) { xerr_warnx("acr30_process_resp(): failed."); return -1; } if (r == 1) { xerr_warnx("acr30_process_resp(): nothing to decode buf_len=%d", acr30ctx->buf_len); continue; } /* this is not fatal */ if (acr30_checksw1sw2(&acr30ctx->rx, 0x90, 0x00, acr30ctx->debug) < 0) { return 1; } else { if (acr30ctx->rx.data_len > ACR30_ATR_LEN) xerr_errx(1, "fatal: acr30ctx->rx.data_len > ACR30_ATR_LEN"); bcopy(acr30ctx->rx.data, acr30ctx->ATR, acr30ctx->rx.data_len); acr30ctx->ATR_len = acr30ctx->rx.data_len; /* display ATR */ if (acr30ctx->debug) { acr30_ATR(acr30ctx); } return 0; } /* got reply */ } /* for RESET retry */ xerr_warnx("Timeout waiting for RESET"); return -1; } /* acr30_reset */ /* * function: acr30_get_acr_stat() * * issue GET_ACR_STAT. Update card_inserted flag * * arguments: * acr30ctx - context allocated with acr30_open() * * returns: 0 - success * <0 - failure * */ int acr30_get_acr_stat(struct acr30_ctx *acr30ctx) { int r; acr30ctx->tx.header = ACR30_HEADER_START; acr30ctx->tx.instruction = ACR30_CMD_GET_ACR_STAT; acr30ctx->tx.data_len = 0; acr30_tx_encode(&acr30ctx->tx); #ifdef ACR30_DEBUG if (acr30ctx->debug) acr30_comm_debug(acr30ctx, 1); #endif /* ACR30_DEBUG */ if (write(acr30ctx->fd, acr30ctx->tx.encoded, acr30ctx->tx.encoded_len) < 0) { xerr_warn("write()"); return -1; } usleep(ACR30_STAT_DELAY); /* load decode buffer with 1 or more responses */ if (acr30_read(acr30ctx) < 0) { xerr_warnx("acr30_read(): failed."); close(acr30ctx->fd); return -1; } #ifdef ACR30_DEBUG if (acr30ctx->debug) acr30_comm_debug(acr30ctx, 0); #endif /* ACR30_DEBUG */ if ((r = acr30_process_resp(acr30ctx)) < 0) { xerr_warnx("acr30_process_resp(): failed."); return -1; } if (r == 1) { xerr_warnx("acr30_process_resp(): nothing to decode buf_len=%d", acr30ctx->buf_len); return -1; } if (acr30_checksw1sw2(&acr30ctx->rx, 0x90, 0x00, acr30ctx->debug) < 0) { xerr_warnx("Unexpected reply to ACR_GET_STAT"); return -1; } else { if (acr30ctx->rx.data_len > ACR30_STAT_LEN) xerr_errx(1, "fatal: acr30ctx->rx.data_len > ACR30_STAT_LEN"); bcopy(acr30ctx->rx.data, acr30ctx->stat, acr30ctx->rx.data_len); acr30ctx->stat_len = acr30ctx->rx.data_len; if (acr30ctx->stat_len != ACR30_STAT_LEN) { xerr_warnx("Unable to decode ACR30_STAT buffer, len=%d", acr30ctx->stat_len); } else { if (acr30ctx->stat[ACR30_STAT_C_STAT] & ACR30_STAT_FLAG_CARD_INSERTED) acr30ctx->card_inserted = 1; } return 0; } /* got reply */ xerr_warnx("Timeout waiting for GET_ACR_STAT"); return -1; } /* acr30_get_acr_stat */ /* * function: acr30_powerdown() * * power down SC in reader * * arguments: * acr30ctx - context allocated with acr30_open() * * returns: 0 - success * <0 - failure * */ int acr30_powerdown(struct acr30_ctx *acr30ctx) { acr30ctx->tx.header = ACR30_HEADER_START; acr30ctx->tx.instruction = ACR30_CMD_POWER_OFF; acr30ctx->tx.data_len = 0; acr30_tx_encode(&acr30ctx->tx); #ifdef ACR30_DEBUG if (acr30ctx->debug) acr30_comm_debug(acr30ctx, 1); #endif /* ACR30_DEBUG */ if (write(acr30ctx->fd, acr30ctx->tx.encoded, acr30ctx->tx.encoded_len) < 0) { xerr_warn("write()"); return -1; } return 0; } /* acr30_powerdown */ /* * function: acr30_transaction() * * perform ACR30 transaction. * * arguments: * acr30ctx - context allocated with acr30_open() * udelay - delay between transaction send and looking for reply * SW1,SW2 - expected return code from ACR30. Failure will be returned * if SW1SW2 do not match the transaction. * le - length of data expected in reply. Set to 255 to ignore. * Failure will be returned if le does not match transaction. * * Data will be encoded as part of the transaction. * * Example for sending MCU command: * * SW1 = 0x00 * SW2 = 0x00 * udelay = 10000 * le = 1 * * acr30ctx->tx.header = ACR30_HEADER_START; * acr30ctx->tx.instruction = ACR30_CMD_MCU; * acr30ctx->tx.data_len = buf_len * acr30ctx->tx_buf = buf * * where buf is : * * i = 0; * * buf[i++] = CLA * buf[i++] = INS * buf[i++] = P1 * buf[i++] = P2 * buf[i++] = LC * buf[i++] = * buf[i++] = LE * * buf_len = i; * * returns: 0 - success * <0 - failure * */ int acr30_transaction(struct acr30_ctx *acr30ctx, useconds_t udelay, u_char SW1, u_char SW2, u_char le) { int r; /* encode bitstram */ acr30_tx_encode(&acr30ctx->tx); #ifdef ACR30_DEBUG if (acr30ctx->debug) acr30_comm_debug(acr30ctx, 1); #endif /* ACR30_DEBUG */ if (write(acr30ctx->fd, acr30ctx->tx.encoded, acr30ctx->tx.encoded_len) < 0) { xerr_warn("write()"); return -1; } usleep(udelay); /* load decode buffer with 1 or more responses */ if (acr30_read(acr30ctx) < 0) { xerr_warnx("acr30_read(): failed."); return -1; } #ifdef ACR30_DEBUG if (acr30ctx->debug) acr30_comm_debug(acr30ctx, 0); #endif /* ACR30_DEBUG */ if ((r = acr30_process_resp(acr30ctx)) < 0) { xerr_warnx("acr30_process_resp(): failed."); return -1; } if (r == 1) { xerr_warnx("acr30_process_resp(): nothing to decode buf_len=%d", acr30ctx->buf_len); return -1; } /* check the reader return codes */ if (acr30_checksw1sw2(&acr30ctx->rx, SW1, SW2, acr30ctx->debug) < 0) { return -1; } /* verify data format (data + SW1SW2) le=255=unknown */ if (le == 255) { if (acr30ctx->rx.data_len < 2) { xerr_warnx("rx.data_len=%d < 2.", acr30ctx->rx.data_len); return -1; } } else if ((acr30ctx->rx.data_len != 2) && (acr30ctx->rx.data_len != (le+2))) { xerr_warnx("rx.data_len=%d not %d", acr30ctx->rx.data_len, (le+2)); return -1; } /* rx.data_len check */ return 0; } /* acr30_transaction */ /* * function: acr30_tx_encode() * * Encode data in acr30_tx* for presentation to ACR30S * * Extended length transactions (> 255) are not supported * * arguments: * acr30ctx - context allocated with acr30_open() * * set: * acr30_tx->header * acr30_tx->instruction * acr30_tx->data_len * acr30_tx->data * * returns: 0 - success * <0 - failure * */ int acr30_tx_encode(struct acr30_tx *acr30_tx) { int i; acr30_tx->encoded[0] = ASCII_STX; acr30_tx->encoded[1] = chr_hex_l(acr30_tx->header); acr30_tx->encoded[2] = chr_hex_r(acr30_tx->header); acr30_tx->encoded[3] = chr_hex_l(acr30_tx->instruction); acr30_tx->encoded[4] = chr_hex_r(acr30_tx->instruction); /* length limited to < 255, not protocol * allows extended data length, not implemented */ acr30_tx->encoded[5] = chr_hex_l(acr30_tx->data_len); acr30_tx->encoded[6] = chr_hex_r(acr30_tx->data_len); acr30_tx->csum = 0; for (i = 0; i < acr30_tx->data_len; ++i) { acr30_tx->encoded[i*2+7] = chr_hex_l(acr30_tx->data[i]); acr30_tx->encoded[i*2+8] = chr_hex_r(acr30_tx->data[i]); acr30_tx->csum ^= acr30_tx->data[i]; } acr30_tx->csum ^= (acr30_tx->header ^ acr30_tx->instruction ^\ acr30_tx->data_len); acr30_tx->encoded[i*2+7] = chr_hex_l(acr30_tx->csum); acr30_tx->encoded[i*2+8] = chr_hex_r(acr30_tx->csum); acr30_tx->encoded[i*2+9] = ASCII_ETX; acr30_tx->encoded_len = i*2+10; return 0; } /* acr30_tx_encode */ /* * function: acr30_checksw1sw2() * * Check response from ACR30. * * arguments: * acr30_rx - receive structure filled in acr30_read() & acr30_process_resp() * SW1 - expected value of SW1 * SW2 - expected value of SW2 * verbose - set true to display status of failed check with xerr_warnx * * returns: 0 - success - SW1 and SW2 matched ACR30 reply * <0 - failure - SW1 or SW2 did not match ACR30 reply * */ int acr30_checksw1sw2(struct acr30_rx *rx, u_char SW1, u_char SW2, int verbose) { if ((rx->SW1 == SW1) && (rx->SW2 == SW2)) { return 0; /* good */ } xerr_warnx("response: SW1=%2.2X,SW2=%2.2X expecting: SW1=%2.2X, SW2=%2.2X", (int)rx->SW1, (int)rx->SW2, (int)SW1, (int)SW2); if ((rx->SW1 == 0x60) && (rx->SW2 == 0x02)) xerr_info("No card in reader."); if ((rx->SW1 == 0x60) && (rx->SW2 == 0x04)) xerr_info("Card not powered up."); if ((rx->SW1 == 0x60) && (rx->SW2 == 0x20)) xerr_info("Card failure."); if ((rx->SW1 == 0x60) && (rx->SW2 == 0x22)) xerr_info("Short circuit at card connector."); if ((rx->SW1 == 0x67) && (rx->SW2 == 0x12)) xerr_info("APDU aborted."); return -1; /* fail */ } /* acr30_checksw1sw2 */ /* * function: acr30_rx_decode() * * Decode response from ACR30. The ACR30 encodes each byte as two * ASCII hex digits + start, end, and error packet bytes. Decode * response into binary. Verify checksum, consume decoded bytes. * * arguments: * acr30ctx - context allocated with acr30_open() * * returns: 0 - success * <0 - failure * */ int acr30_rx_decode(struct acr30_ctx *acr30ctx) { int r_pos, w_pos, i, flag_etx; u_char decoded, csum; /* i rx * ---------- * 0 0x01 * 1 header * 2 header * 3 SW1 * 4 SW1 * 5 SW2 * 6 SW2 * 7 data_len (1) * 8 data_len (1) * 9 data * 10 data * 11 csum * 12 csum * * buf_len = 13 */ /* consume leading trash in response bytes */ while (1) { /* complete response? */ flag_etx = 0; /* transmission error? */ if ((acr30ctx->buf_len == 2) && (acr30ctx->buf[0] == ASCII_ENQ) && (acr30ctx->buf[1] == ASCII_ENQ)) { xerr_warnx("acr30_rx_decode(): transmission error"); return -2; } /* sanity check */ if (acr30ctx->buf_len > ACR30_DECODE_MAX) { xerr_warnx("acr30_rx_decode(): buf_len=%d > ACR30_DECODE_MAX=%d", acr30ctx->buf_len, ACR30_DECODE_MAX); return -1; } /* sanity check */ if (acr30ctx->buf_len < ACR30_DECODE_MIN) { xerr_warnx("acr30_rx_decode(): buf_len=%d < ACR30_DECODE_MIN=%d", acr30ctx->buf_len, ACR30_DECODE_MIN); return -1; } /* response must begin with ASCII_STX */ if (acr30ctx->buf[0] != ASCII_STX) { if (acr30ctx->debug) xerr_warnx("acr30_rx_decode(): buf[0]=%2.2X, expecting %2.2X", (int)acr30ctx->buf[0], (int)ASCII_STX); /* consume bytes until STX then retry */ for (i = 0; i < acr30ctx->buf_len; ++i) if (acr30ctx->buf[i] == ASCII_STX) break; acr30ctx->buf_len -= i; /* anything left? */ if (acr30ctx->buf_len < 1) { xerr_warnx("acr30_rx_decode(): underrun"); return -1; } if (acr30ctx->debug) xerr_warnx("Skipped %d bytes, %d remaining", i, acr30ctx->buf_len); bcopy(acr30ctx->buf+i, acr30ctx->buf, acr30ctx->buf_len); continue; /* again */ } else { /* ready to start decode */ break; } } /* while consuming leading garbage */ r_pos = 1; /* read position */ w_pos = 0; /* write position */ /* read two bytes in ASCII, convert to 1 byte of binary */ for (; r_pos < acr30ctx->buf_len; r_pos += 2) { /* complete response? */ if (acr30ctx->buf[r_pos] == ASCII_ETX) { flag_etx = 1; break; } /* expecting ASCII 0-9, A-F) */ for (i = 0; i < 2; ++i) { /* valid? */ if (chr_ishex(acr30ctx->buf[r_pos+i])) { xerr_warnx("acr30_rx_decode(): buf[%d]=%2.2X, not HEX.", r_pos+1, (int)acr30ctx->buf[r_pos+i]); return -1; } if (i == 0) { decoded = (chr_hex_decode(acr30ctx->buf[r_pos+i])<<4); } else { decoded |= chr_hex_decode(acr30ctx->buf[r_pos+i]); } } /* foreach 2 bytes in decode buffer */ switch (r_pos) { case 1: acr30ctx->rx.header = decoded; break; case 3: acr30ctx->rx.SW1 = decoded; break; case 5: acr30ctx->rx.SW2 = decoded; break; case 7: acr30ctx->rx.data_len = decoded; break; default: /* data followed by checksum */ if (w_pos == acr30ctx->rx.data_len) { /* checksum follows data */ acr30ctx->rx.csum = decoded; } else { if (w_pos >= ACR30_MAX_DLEN) { /* never */ xerr_errx(1, "acr30_rx_decode(): fatal, w_pos >= ACR30_MAX_DLEN."); } if (w_pos >= acr30ctx->rx.data_len) { /* only on corrupted rx */ xerr_errx(1, "acr30_rx_decode(): fatal, w_pos >= acr30ctx->rx.data_len."); } acr30ctx->rx.data[w_pos++] = decoded; } break; } /* switch */ } /* decode reply */ if (!flag_etx) { xerr_warnx( "acr30_rx_decode(): incomplete response buf_len=%d", acr30ctx->buf_len); return -1; } /* validate checksum */ csum = 0; csum ^= acr30ctx->rx.header; csum ^= acr30ctx->rx.SW1; csum ^= acr30ctx->rx.SW2; csum ^= acr30ctx->rx.data_len; for (i = 0; i < acr30ctx->rx.data_len; ++i) { csum ^= acr30ctx->rx.data[i]; } if (csum != acr30ctx->rx.csum) { xerr_warnx("acr30_rx_decode(): Checksum failure, csum=%2.2X,rx_csum=%x", csum, acr30ctx->rx.csum); return -1; } /* consume bytes decoded */ acr30ctx->buf_len -= (r_pos+1); bcopy(acr30ctx->buf+r_pos+1, acr30ctx->buf, acr30ctx->buf_len); /* return bytes left */ return acr30ctx->buf_len; } /* acr30_rx_decode */ /* * function: acr30_read() * * Read upto buf length bytes from fd. Last byte(s) must be ETX or ENQ ENQ * timeout after ACR30_TMOUT_READ microseconds. Warn if a full response is * not read with one syscall as this implies the delay after sending the * SC command should be increased. * * arguments: * acr30ctx - context allocated with acr30_open() * * returns: 0 - success * <0 - failure * */ int acr30_read(struct acr30_ctx *acr30ctx) { int n, bytes_left; while (1) { /* bytes left in read buffer */ bytes_left = (ACR30_READ_BUF_LEN - acr30ctx->buf_len); /* buffer full? */ if (bytes_left == 0) { xerr_warnx("acr30_read(): read buffer full "); return -1; } /* read bytes at buf[buf_len] */ if ((n = read(acr30ctx->fd, acr30ctx->buf+acr30ctx->buf_len, bytes_left)) < 0) { xerr_warn("acr30_read(): read()"); return -1; } /* timeout? */ if (n == 0) { xerr_warnx("acr30_read(): timeout"); return -1; } acr30ctx->buf_len += n; /* complete response when character is ETX */ if (acr30ctx->buf[acr30ctx->buf_len-1] == ASCII_ETX) return 0; /* complete response when last 2 chars are ENQ ENQ -- xmit error */ if ((acr30ctx->buf_len >= 2) && (acr30ctx->buf[acr30ctx->buf_len-1] == ASCII_ETX) && (acr30ctx->buf[acr30ctx->buf_len-2] == ASCII_ETX)) { return 0; } if (acr30ctx->debug) xerr_warnx("read()=%d, increase cmd timeout.", n); } /* forever */ return -1; /* never */ } /* acr30_read */ /* * function: acr30_process_resp() * * process response to ACR30 command, handle async generated responses. * * Picks off common responses such as CARD_INSERTED and updates internal * state flags. Leave anything else to application. * * arguments: * acr30ctx - context allocated with acr30_open() * * returns: 0 - success * <0 - failure * */ int acr30_process_resp(struct acr30_ctx *acr30ctx) { int n, next; next = 0; /* while more responses to decode */ while (acr30ctx->buf_len) { /* decode */ if ((n = acr30_rx_decode(acr30ctx)) < 0) { xerr_warnx("acr30_rx_decode(): failed."); return -1; } /* * The ACR30 can send messages such as card inserted async, * handle internally */ /* * powerup reset: SW1=0xFF, SW2=0x00 * card inserted: SW1=0xFF, SW2=0x01 * card removed: SW1=0xFF, SW2=0x02 */ /* power up reset */ if ((acr30ctx->rx.SW1 == 0xFF) && (acr30ctx->rx.SW2 == 0x00)) { acr30ctx->reset = 1; continue; /* again */ } /* card inserted */ if ((acr30ctx->rx.SW1 == 0xFF) && (acr30ctx->rx.SW2 == 0x01)) { acr30ctx->card_inserted = 1; if (acr30ctx->debug) xerr_warnx("Card inserted."); continue; /* again */ } /* card removed */ if ((acr30ctx->rx.SW1 == 0xFF) && (acr30ctx->rx.SW2 == 0x02)) { acr30ctx->card_inserted = 0; if (acr30ctx->debug) xerr_warnx("Card removed."); continue; /* again */ } /* anything else is handled by the application */ return 0; } /* while more messages to decode */ /* no application level decoded messages */ return 1; } /* acr30_process_resp */ #define CHKLEN(I,MAX)\ if (I >= MAX)\ xerr_errx(1, "acr30_comm_debug(): fatal, fmt_buf overrun."); /* * function: acr30_ATR() * * decode and display ATR as printable ASCII. Non printable * bytes are displayed as HEX. * * arguments: * acr30ctx - context allocated with acr30_open() * * returns: 0 - success * <0 - failure * */ void acr30_ATR(struct acr30_ctx *acr30ctx) { int i, j, buf_len; char fmt_buf[DBG_FMT_BUF_LEN+1]; u_char *buf; j = 0; buf = acr30ctx->ATR; buf_len = acr30ctx->ATR_len; for (i = 0; i < buf_len; ++i) { if (isprint((int)buf[i])) { fmt_buf[j++] = buf[i]; CHKLEN(j,DBG_FMT_BUF_LEN); } else { fmt_buf[j++] = '0'; CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = 'x'; CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = chr_hex_l(buf[i]); CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = chr_hex_r(buf[i]); CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = ' '; CHKLEN(j,DBG_FMT_BUF_LEN); } } /* for */ xerr_info("ATR(): %s", fmt_buf); } /* acr30_ATR */ /* * function: acr30_comm_debug() * * decode and display ACR30 communications i/o * * arguments: * acr30ctx - context allocated with acr30_open() * * returns: 0 - success * <0 - failure * */ void acr30_comm_debug(struct acr30_ctx *acr30ctx, int txrx) { int i, j, first, buf_len; u_char *buf; char fmt_buf[DBG_FMT_BUF_LEN+1]; if (txrx == 1) { buf = acr30ctx->tx.encoded; buf_len = acr30ctx->tx.encoded_len; } else { buf = acr30ctx->buf; buf_len = acr30ctx->buf_len; } first = 0; j = 0; for (i = 0; i < buf_len; ++i) { if (buf[i] == 0x2) { fmt_buf[j++] = 'S'; CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = 'T'; CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = 'X'; CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = ' '; CHKLEN(j,DBG_FMT_BUF_LEN); } else if (buf[i] == 0x3) { fmt_buf[j++] = 'E'; CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = 'T'; CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = 'X'; CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = ' '; CHKLEN(j,DBG_FMT_BUF_LEN); } else if ( ((buf[i] >= '0') && (buf[i] <= '9')) || ((buf[i] >= 'A') && (buf[i] <= 'F'))) { fmt_buf[j++] = buf[i]; CHKLEN(j,DBG_FMT_BUF_LEN); if (first == 0){ first = 1; } else { first = 0; fmt_buf[j++] = ' '; CHKLEN(j,DBG_FMT_BUF_LEN); } } else { fmt_buf[j++] = '0'; CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = 'x'; CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = chr_hex_l(buf[i]); CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = chr_hex_r(buf[i]); CHKLEN(j,DBG_FMT_BUF_LEN); fmt_buf[j++] = ' '; CHKLEN(j,DBG_FMT_BUF_LEN); } } fmt_buf[j] = 0; if (txrx == 1) xerr_info("acr30_comm_debug(TX): %s", fmt_buf); else xerr_info("acr30_comm_debug(RX): %s", fmt_buf); } /* acr30_comm_debug */ /* * function: acr30_flush() * * flush input buffer and serial port input. * * arguments: * acr30ctx - context allocated with acr30_open() * * returns: 0 - success * <0 - failure * */ int acr30_flush(struct acr30_ctx *acr30ctx) { struct termios t; acr30ctx->buf_len = 0; if (tcgetattr(acr30ctx->fd, &t) < 0) { xerr_warn("tcgetattr()"); return -1; } if (tcsetattr(acr30ctx->fd, TCSAFLUSH, &t) < 0) { xerr_warn("tcsetattr(TCSAFLUSH)"); return -1; } return 0; } /* acr30_flush */