The i.MX SmartCard interface is part of the SoC, its a 'in system' interface. It only depends on the existence of a i.MX specific kernel driver.
Signed-off-by: Juergen Beisert <j...@pengutronix.de> --- src/ifd/Makefile.am | 2 src/ifd/ifd-imx.c | 828 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ifd/init.c | 1 src/ifd/internal.h | 1 4 files changed, 831 insertions(+), 1 deletion(-) Index: openct-0.6.20/src/ifd/Makefile.am =================================================================== --- openct-0.6.20.orig/src/ifd/Makefile.am +++ openct-0.6.20/src/ifd/Makefile.am @@ -13,7 +13,7 @@ libifd_la_SOURCES = \ ifd-etoken.c ifd-etoken64.c ifd-eutron.c ifd-gempc.c ifd-ikey2k.c \ ifd-ikey3k.c ifd-kaan.c ifd-pertosmart1030.c ifd-pertosmart1038.c \ ifd-smartboard.c ifd-smph.c ifd-starkey.c ifd-towitoko.c cardman.h \ - ifd-cyberjack.c ifd-rutoken.c ifd-epass3k.c \ + ifd-cyberjack.c ifd-rutoken.c ifd-epass3k.c ifd-imx.c \ \ proto-gbp.c proto-sync.c proto-t0.c proto-t1.c \ proto-trans.c proto-escape.c \ Index: openct-0.6.20/src/ifd/ifd-imx.c =================================================================== --- /dev/null +++ openct-0.6.20/src/ifd/ifd-imx.c @@ -0,0 +1,828 @@ +/* + * Driver for 'in system' i.MX SmartCard interface + * + * License: GNU LESSER GENERAL PUBLIC LICENSE + * Copyright (C) 2010 Juergen Beisert <j...@pengutronix.de> + */ + +#include <unistd.h> +#include <sys/ioctl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> + +#include "internal.h" +#include "atr.h" + +#include <linux/simcard.h> + +#define DEFAULT_BLOCK_WAITING_TIME 20000 /* FIXME: Default when T=0 ? */ + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +struct imx_slot_info { + struct sim_param param; /** current protocol/parameter settings */ + unsigned char_waiting_time; /** CWT */ + unsigned block_waiting_time; /** BWT */ + unsigned period; /** current clock period in [ns] */ + bool slot_powered; /** true, if the slot is powered */ + bool card_present; /** true, if a card is detected */ + bool card_checked; /** true, if the following information about the card is valid */ + bool low_reset_cap; /** true, if the SmartCard uses a low active reset */ +}; + +/* ------------- the i.MX specific routines ------------------------- */ + +static struct imx_slot_info *imx_get_slot_info(struct ifd_reader *reader, int slot) +{ + return (struct imx_slot_info*)reader->slot[0].reader_data; +} + +static void imx_set_slot_info(struct ifd_reader *reader, void *slot_info, int slot) +{ + reader->slot[0].reader_data = slot_info; +} + +static int imx_get_interface_params(int fd, void *param) +{ + return ioctl(fd, SIMIOC_GET_PARAM, param); +} + +static int imx_set_interface_params(int fd, void *param) +{ + return ioctl(fd, SIMIOC_SET_PARAM, param); +} + +static int imxsim_switch_off_socket(struct ifd_reader *reader) +{ + struct imx_slot_info *info = imx_get_slot_info(reader, 0); + int rc; + + ifd_debug(3, "called"); + + rc = ioctl(reader->device->fd, SIMIOC_POWER, (void*)SIM_POWER_OFF); + if (rc != 0) { + ct_error("Cannot switch off SmartCard socket: %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; /* FIXME: useful return value */ + } + info->slot_powered = false; + + return 0; +} + +static int imxsim_handle_low_reset(struct ifd_reader *reader) +{ + int rc; + + ifd_debug(3, "called"); + + rc = ioctl(reader->device->fd, SIMIOC_WARM_RESET, (void*)SIM_WARM_RESET_LOW); + if (rc != 0) { + ct_error("Cannot change reset line's logic level to 'low': %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; /* FIXME: useful return value */ + } + + sleep(1); + + rc = ioctl(reader->device->fd, SIMIOC_WARM_RESET, (void*)SIM_WARM_RESET_HIGH); + if (rc != 0) { + ct_error("Cannot change reset line's logic level to 'high': %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; /* FIXME: useful return value */ + } + + return 0; +} + +static int imxsim_handle_high_reset(struct ifd_reader *reader) +{ + int rc; + + ifd_debug(3, "called"); + + rc = ioctl(reader->device->fd, SIMIOC_WARM_RESET, (void*)SIM_WARM_RESET_HIGH); + if (rc != 0) { + ct_error("Cannot change reset line's logic level to 'high': %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; /* FIXME: useful return value */ + } + + sleep(1); + + rc = ioctl(reader->device->fd, SIMIOC_WARM_RESET, (void*)SIM_WARM_RESET_LOW); + if (rc != 0) { + ct_error("Cannot change reset line's logic level to 'low': %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; /* FIXME: useful return value */ + } + + return 0; +} + +/** @param timeout Timeout value in [ms] */ +static int imx_read_one_char(ifd_device_t *dev, unsigned char *buffer, long timeout) +{ + struct timeval begin; + ssize_t n; + int wait; + + gettimeofday(&begin, NULL); + + do { + /* poll for next char */ + n = read(dev->fd, buffer, 1); + if (n < 0) { + ct_error("%s: failed to read from device: %m", dev->name); + return IFD_ERROR_COMM_ERROR; + } + if (n == 1) + return 1; /* return happy */ + + wait = timeout - ifd_time_elapsed(&begin); + + } while (wait > 0); + + return 0; /* timeout */ +} + +/** @param timeout Timeout value in [ms] + * + * Wait the defined 'block waiting time' for the first character, after it, + * wait the defined 'char waiting time' until the first timeout. + * + * TODO: If the guard time is larger than BTW, wait the guard time instead + * TODO: Check the given 'timeout' value if it is already the BWT + */ +static int imx_recv(struct ifd_reader *reader, unsigned int dad, unsigned char *buffer, size_t len, long timeout) +{ + struct imx_slot_info *info = imx_get_slot_info(reader, 0); /* TODO slot number */ + int rc; + size_t rec_chars = 0; + + rc = imx_read_one_char(reader->device, buffer, info->block_waiting_time); + if (rc < 0) + return rc; + if (rc == 0) + return rec_chars; /* timeout */ + + rec_chars++; + buffer++; + + while (len > rec_chars) { + rc = imx_read_one_char(reader->device, buffer, info->char_waiting_time); + if (rc < 0) + return rc; + if (rc == 0) + break; /* timeout */ + rec_chars++; + buffer++; + } + + return rec_chars; +} + +/** @return length of ATR (positive number) or negative error value + * + * This routine depends on the correct setup of 'block_waiting_time' and 'char_waiting_time' + */ +static int imxsim_get_atr(struct ifd_reader *reader, void *atr, size_t size) +{ + int rc; + + ifd_debug(3, "called"); + + do { + rc = ioctl(reader->device->fd, SIMIOC_GET_ATR, NULL); + if (rc != 0) { + switch (errno) { + case EAGAIN: + /* TODO use a timeout value */ + break; + + case ENODEV: + return IFD_ERROR_TIMEOUT; + + default: + ct_error("Unexpected error when waiting for ATR: %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; /* FIXME: useful return value */ + } + } else { + return imx_recv(reader, 0, atr, size, 1000); + } + sleep(1); + } while (rc != 0); + + return IFD_ERROR_TIMEOUT; +} + +/** @return the TB3 or TB4 value if present, default CWI/BWI else */ +static unsigned search_T1_TBx(struct ifd_atr_info *atr_info, int protocol) +{ + if (atr_info->prot[2] == protocol) { + if (atr_info->TB[2] != -1) + return atr_info->TB[2]; + } else if (atr_info->prot[3] == protocol) { + if (atr_info->TB[3] != -1) + return atr_info->TB[3]; + } + + /* default is CWI = 13, BWI = 4 */ + return 0x4D; +} + +struct communication_params { + unsigned fi:4; + unsigned di_min:4; + unsigned di_max:4; +}; + +/* list of FI/DI pairs the i.MX interface logic supports */ +static const struct communication_params imx_par[] = { + { .fi = 0, .di_min = 1, .di_max = 1 }, + { .fi = 1, .di_min = 1, .di_max = 1 }, + { .fi = 3, .di_min = 1, .di_max = 2 }, + { .fi = 4, .di_min = 1, .di_max = 1 }, + { .fi = 5, .di_min = 1, .di_max = 3 }, + { .fi = 6, .di_min = 1, .di_max = 1 }, + { .fi = 9, .di_min = 1, .di_max = 6 }, + { .fi = 10, .di_min = 1, .di_max = 6 }, /* FIXME 8 is possible, but not 7 */ + { .fi = 11, .di_min = 1, .di_max = 6 }, + { .fi = 12, .di_min = 1, .di_max = 6 }, /* FIXME 8 is possible, but not 7 */ + { .fi = 13, .di_min = 2, .di_max = 6 }, +}; + +/** @return 'true' if the requested DI is supported for the specified FI */ +static bool imx_di_supported(const struct communication_params *fi, unsigned char di) +{ + if (di >= fi->di_min && di <= fi->di_max) + return true; + + return false; +} + +/** @return 'true' if the requested FI/DI is supported by the i.MX hardware */ +static bool imx_fidi_supported(unsigned char fi, unsigned char di) +{ + unsigned u; + + for (u = 0; u < ARRAY_SIZE(imx_par); u++) { + if (imx_par[u].fi == fi && imx_di_supported(&imx_par[u], di)) + return true; + } + + return false; +} + +/** @return 'true' if a different FI/DI pair was found, similar to the given one */ +static bool imx_fidi_negotiate(unsigned char *fi, unsigned char *di) +{ + unsigned u; + + if (*fi > 13) + return false; + + for (u = 0; u < ARRAY_SIZE(imx_par); u++) { + if (*fi == imx_par[u].fi) { + if (*di < imx_par[u].di_min) + break; /* TODO selecting a slower FI/DI pair instead? */ + if (*di > imx_par[u].di_max) + *di = imx_par[u].di_max; + return true; + } + } + + return false; +} + +/* note: a value of '0' means 'not defined' */ +static const unsigned Fv[16] = { + 372, 372, 558, 744, 1116, 1488, 1860, 0, + 0, 512, 768, 1024, 1536, 2048, 0, 0 +}; +/* note: a value of '0' means 'not defined' */ +static const unsigned Iv[16] = { + 0, 1, 2, 4, 8, 16, 32, 0, + 12, 20, 0, 0, 0, 0, 0, 0 +}; + +/** @return character waiting time in [ms] */ +static unsigned imx_calculate_cwt(struct imx_slot_info *info, struct ifd_atr_info *atr_info, int protocol) +{ + unsigned long long u; + + if (protocol == IFD_PROTOCOL_T0) { + /* Use the WWT for the T=0 protocol. Default WI is 10 */ + u = 10UL; + if (atr_info != NULL) { + if (atr_info->TC[1] != -1) /* TC2 defines the WI if present */ + u = atr_info->TC[1]; + } + u *= 960; + if (info->param.FI >= ARRAY_SIZE(Fv) || Fv[info->param.FI] == 0) + ifd_debug(2, "Warning: Illegal value FI=%u", info->param.FI); + else + u *= Fv[info->param.FI]; + u *= info->period; /* now in [ns] */ + u /= 1000 * 1000; /* now in [ms] */ + } else { + /* do a specific calculation for T=1 protocol */ + if (atr_info != NULL) + u = search_T1_TBx(atr_info, protocol) & 0x0F; + else + u = 13UL; /* CWI default */ + + u = 11 + (1 << u); + if (info->param.FI >= ARRAY_SIZE(Fv) || Fv[info->param.FI] == 0) + ifd_debug(2, "Warning: Illegal value FI=%u", info->param.FI); + else + u *= Fv[info->param.FI]; + if (info->param.DI >= ARRAY_SIZE(Iv) || Iv[info->param.DI] == 0) + ifd_debug(2, "Warning: Illegal value DI=%u", info->param.DI); + else + u /= Iv[info->param.DI]; + u *= info->period; /* now in [ns] */ + u /= 1000 * 1000; /* now in [ms] */ + } + return u; +} + +/** @return block waiting time in [ms] */ +static unsigned imx_calculate_bwt(struct imx_slot_info *info, struct ifd_atr_info *atr_info, int protocol) +{ + unsigned long long u; + + if (protocol != IFD_PROTOCOL_T1) { + /* TODO: What default value should be used here? */ + return DEFAULT_BLOCK_WAITING_TIME; + } + + if (atr_info != NULL) + u = search_T1_TBx(atr_info, protocol) >> 4; + else + u = 4UL; /* BWI default */ + + u = 11 + (1 << u); + u *= 960; + u *= 372; + u *= info->period; /* now in [ns] */ + u /= 1000 * 1000; /* now in [ms] */ + + return u; +} + +/** + * Re-calculate timings based on kernel driver parameters + * Ensure some important defaults, if the kernel driver does not set them up yet + */ +static void imx_reset_timings(struct imx_slot_info *info) +{ + /* if no other frequency is known, expect at least 1 MHz */ + info->period = 1000000000U / (info->param.card_clock != 0 ? info->param.card_clock : 1000000); + ifd_debug(2, "New clock period is %u ns", info->period); + /* use defaults, if nothing else is defined */ + if (info->param.FI == 0) + info->param.FI = 1; + if (info->param.DI == 0) + info->param.DI = 1; + info->char_waiting_time = imx_calculate_cwt(info, NULL, IFD_PROTOCOL_T0); + ifd_debug(2, "New CWT is %u ms", info->char_waiting_time); + info->block_waiting_time = imx_calculate_bwt(info, NULL, IFD_PROTOCOL_T0); + ifd_debug(2, "New BWT is %u ms", info->block_waiting_time); +} + +/* ----------------------- openct adaptions ------------------------------- */ + +static int imx_open(struct ifd_reader *reader, const char *device_name) +{ + struct imx_slot_info *info; + + ifd_debug(3, "called, device=%s", device_name); + + info = malloc(sizeof(struct imx_slot_info)); + if (info == NULL) + return 1; + + reader->name = "i.MX SoC Interface"; + reader->nslots = 1; + imx_set_slot_info(reader, info, 0); + + info->card_checked = false; + info->low_reset_cap = false; + info->card_present = false; + info->slot_powered = false; + + reader->device = ifd_device_open(device_name); + if (reader->device == NULL) + goto on_error; + + imx_get_interface_params(reader->device->fd, &info->param); + imx_reset_timings(info); + + ifd_device_flush(reader->device); + + return 0; + +on_error: + if (info != NULL) + free(info); + imx_set_slot_info(reader, NULL, 0); + + return -1; +} + +static int imx_close(struct ifd_reader *reader) +{ + struct imx_slot_info *info = imx_get_slot_info(reader, 0); + + ifd_debug(3, "called"); + imxsim_switch_off_socket(reader); + ifd_device_close(reader->device); + if (info != NULL) + free(info); + imx_set_slot_info(reader, NULL, 0); + + /* FIXME must we free reader->slot[0].proto by ourselfs? */ + + return 0; +} + +/* called once */ +static int imx_activate(struct ifd_reader *reader) +{ + ifd_debug(3, "called"); + return 0; +} + +/* never called */ +static int imx_deactivate(struct ifd_reader *reader) +{ + ifd_debug(3, "called"); + return 0; +} + +static int imx_card_status(struct ifd_reader *reader, int slot, int *status) +{ + struct imx_slot_info *info = imx_get_slot_info(reader, slot); + int rc, res; + + rc = ioctl(reader->device->fd, SIMIOC_GET_PRESENSE, (void*)&res); + if (rc != 0) { + ct_error("Cannot detect SmartCard presense: %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; + } + + switch (res) { + case SIM_PRESENT_REMOVED: + if (info->slot_powered == true) { + ifd_debug(1, "SmartCard removed - power off"); + rc = ioctl(reader->device->fd, SIMIOC_POWER, (void*)SIM_POWER_OFF); + if (rc != 0) { + ct_error("Cannot power off SmartCard slot: %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; + } + } + *status &= ~IFD_CARD_PRESENT; /* no card inserted */ + if (info->card_present != false) + *status |= IFD_CARD_STATUS_CHANGED; + else + *status &= ~IFD_CARD_STATUS_CHANGED; + info->card_present = false; + info->card_checked = false; + info->slot_powered = false; + break; + + case SIM_PRESENT_DETECTED: + case SIM_PRESENT_OPERATIONAL: + *status |= IFD_CARD_PRESENT; /* card inserted */ + if (info->card_present != true) + *status |= IFD_CARD_STATUS_CHANGED; + else + *status &= ~IFD_CARD_STATUS_CHANGED; + info->card_present = true; + break; + + default: + ct_error("Unknown return value (%d) from the SmartCard kernel driver", res); + return IFD_ERROR_NOT_SUPPORTED; + } + + return 0; +} + +/** switch card on if not already done, reset this card and catch the ATR */ +static int imx_card_reset(struct ifd_reader *reader, int slot, void *atr, size_t size) +{ + struct imx_slot_info *info = imx_get_slot_info(reader, slot); + int rc; + + ifd_debug(3, "called"); + + /* if not powered yet, do it right now */ + if (info->slot_powered == false) { + rc = ioctl(reader->device->fd, SIMIOC_POWER, (void*)SIM_POWER_ON); + if (rc != 0) { + ct_error("Cannot switch on SmartCard socket: %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; + } + info->slot_powered = true; + } + + /* assert the reset to the card */ + if (info->card_checked == true) { + if (info->low_reset_cap == true) + rc = imxsim_handle_low_reset(reader); + else + rc = imxsim_handle_high_reset(reader); + + /* does the reset fail? */ + if (rc != 0) + return rc; + + /* reset to current default */ + imx_get_interface_params(reader->device->fd, &info->param); + imx_reset_timings(info); + + rc = imxsim_get_atr(reader, atr, size); + if (rc < 0) { + ct_error("No ATR from SmartCard after reset"); + imxsim_switch_off_socket(reader); + return IFD_ERROR_TIMEOUT; + } + } else { + /* we have to detect if this card needs a low or high active reset + * after power on we always start with a low reset + */ + rc = ioctl(reader->device->fd, SIMIOC_WARM_RESET, (void*)SIM_WARM_RESET_LOW); + if (rc != 0) { + ct_error("Cannot change reset line's logic level to 'low': %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; /* FIXME: useful return value */ + } + + /* reset to current default */ + imx_get_interface_params(reader->device->fd, &info->param); + imx_reset_timings(info); + + rc = imxsim_get_atr(reader, atr, size); + if (rc < 0) { + /* try again with reset high */ + rc = ioctl(reader->device->fd, SIMIOC_WARM_RESET, (void*)SIM_WARM_RESET_HIGH); + if (rc != 0) { + ct_error("Cannot change reset line's logic level to 'high': %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; /* FIXME: useful return value */ + } + + /* reset to current default */ + imx_get_interface_params(reader->device->fd, &info->param); + imx_reset_timings(info); + + rc = imxsim_get_atr(reader, atr, size); + if (rc < 0) { + ct_error("No ATR from SmartCard after reset"); + imxsim_switch_off_socket(reader); + return IFD_ERROR_TIMEOUT; + } + /* card with low active reset */ + info->card_checked = true; + info->low_reset_cap = true; + } else { + /* card with high active reset */ + info->card_checked = true; + info->low_reset_cap = false; + } + } + + return rc; /* return lenght of ATR */ +} + +static int imx_send(struct ifd_reader *reader, unsigned int dad, const unsigned char *buffer, size_t len) +{ + ssize_t rc; + + /* + * Our transmitter FIFO cannot write more than 16 bytes per call. + * So, loop until all bytes are sent + */ + do { + rc = write(reader->device->fd, buffer, len); + if (rc < 0) { + ct_error("Cannot write data to card reader: %s", strerror(errno)); + return IFD_ERROR_COMM_ERROR; + } + buffer += rc; + len -= rc; + } while (len != 0); + + return rc; +} + +/** @param ptslen full lenght of the PTS (includes the checksum) */ +static int imx_negotiate(struct ifd_reader *reader, unsigned int dad, struct sim_param *param, unsigned char *pts, size_t ptslen) +{ + struct imx_slot_info *info = imx_get_slot_info(reader, 0); + int rc, index; + + /* after reset or power up these are always the default params */ + rc = imx_get_interface_params(reader->device->fd, param); + if (rc != 0) { + ct_error("Cannot query the current communication parameter: %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; + } + imx_reset_timings(info); + + switch (pts[1] & 0x0F) { + case IFD_PROTOCOL_T0: + param->T = SIM_PROTOCOL_T0; + break; + case IFD_PROTOCOL_T1: + param->T = SIM_PROTOCOL_T1; + break; + } + + /* is more than the protocol to be changed? */ + if (ptslen > 3) { + index = 2; + if (pts[1] & 0x10) { /* clock parameters? */ +#if 0 + pts[index] = 0x11; /* for the slow protocol scanner */ +#endif + param->FI = pts[index] >> 4; + param->DI = pts[index] & 0x0F; + /* negotiate */ + if (!imx_fidi_supported(param->FI, param->DI)) { + /* not supported FI/DI pair. Search for a similar one */ + if (!imx_fidi_negotiate(¶m->FI, ¶m->DI)) { + /* we are out of luck, fall back to the default */ + ifd_debug(1, "Requested FI/DI not supported. Falling back to 1/1"); + param->FI = param->DI = 1; + } + pts[index] = (param->FI << 4) | param->DI; + } + index++; + } + if (pts[1] & 0x20) { /* guard time parameters? */ + param->N = pts[index]; + } + } + + /* re-calculate the checksum */ + pts[ptslen - 1] = 0x00; /* reset the LRC */ + for (index = 0; index < (ptslen - 1); index++) + pts[ptslen - 1] ^= pts[index]; + + /* TODO What about WWT, IFSC, BWI and CWI, LRC/CRC? */ + return 0; +} + +static int imx_set_protocol(struct ifd_reader *reader, int nslot, int proto) +{ + struct imx_slot_info *info = imx_get_slot_info(reader, 0); + struct ifd_atr_info atr_info; + unsigned char pts[7], answer[7]; + int rc, ptslen; + + ifd_debug(3, "called"); + + /* + * If we already using the requested protocol, avoid a new negotiation + * again and again. + */ + if (reader->slot[0].proto != NULL && info->param.T == proto) { + ifd_debug(3, "Protocol %d already active", proto); + return 0; + } + + if (proto != IFD_PROTOCOL_T0 && proto != IFD_PROTOCOL_T1) { + ct_error("%s: protocol not supported", reader->name); + return IFD_ERROR_NOT_SUPPORTED; + } + + /* + * if we change the protocols back and forth, free the last protocol used + */ + if (reader->slot[0].proto != NULL) { + ifd_protocol_free(reader->slot[0].proto); + reader->slot[0].proto = NULL; + } + + rc = ifd_atr_parse(&atr_info, reader->slot[0].atr, reader->slot[0].atr_len); + if (rc != 0) { + ct_error("Cannot parse the ATR"); + return IFD_ERROR_INVALID_ATR; /* FIXME useful? */ + } + + ptslen = ifd_build_pts(&atr_info, proto, pts, sizeof(pts)); + if (ptslen < 0) { + ct_error("Cannot build a PTS from ATR data"); + return IFD_ERROR_INVALID_ATR; /* FIXME useful? */ + } + + rc = imx_negotiate(reader, reader->slot[0].dad, &info->param, pts, ptslen); + if (rc < 0) + return IFD_ERROR_NOT_SUPPORTED; + +#define ONE_SECOND 1000L + + /* do the PTS exchange */ + rc = imx_send(reader, reader->slot[0].dad, pts, ptslen); + if (rc < 0) + return IFD_ERROR_COMM_ERROR; + + /* await the same amount of data we sent */ + ptslen = imx_recv(reader, reader->slot[0].dad, answer, ptslen, ONE_SECOND); + if (ptslen < 0) { + ct_error("No PTS answer received"); + return IFD_ERROR_COMM_ERROR; + } + + /* did the card responds as expected? */ + rc = ifd_pts_complete(answer, ptslen); + if (rc == 0) { + ct_error("Incomplete PTS answer received"); + return IFD_ERROR_COMM_ERROR; + } + + rc = ifd_verify_pts(&atr_info, proto, answer, ptslen); + if (rc != 0) { + ct_error("Verifying PTS failed: %s", ct_strerror(rc)); + return rc; + } + + /* setup the interface to the same settings than the card is using now */ + rc = imx_set_interface_params(reader->device->fd, &info->param); + if (rc != 0) { + ct_error("Cannot set the current communication parameter: %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; + } + /* + * If we changed the SmartCard clock, we must read back the params to + * know what base clock the card reader is now using (it could + * limit our settings!) + */ + rc = imx_get_interface_params(reader->device->fd, &info->param); + if (rc != 0) { + ct_error("Cannot query the current communication parameter: %s", strerror(errno)); + return IFD_ERROR_NOT_SUPPORTED; + } + /* recalculate all timing params based on current settings */ + info->period = 1000000000U / info->param.card_clock; + switch (info->param.T) { + case SIM_PROTOCOL_T0: + info->char_waiting_time = imx_calculate_cwt(info, &atr_info, IFD_PROTOCOL_T0); + info->block_waiting_time = imx_calculate_bwt(info, &atr_info, IFD_PROTOCOL_T0); + break; + case SIM_PROTOCOL_T1: + info->char_waiting_time = imx_calculate_cwt(info, &atr_info, IFD_PROTOCOL_T1); + info->block_waiting_time = imx_calculate_bwt(info, &atr_info, IFD_PROTOCOL_T1); + break; + } + ifd_debug(2, "New clock period is %u ns", info->period); + ifd_debug(2, "New CWT is %u ms", info->char_waiting_time); + ifd_debug(2, "New BWT is %u ms", info->block_waiting_time); + + /* use the negotiated/acknowledged protocol by now */ + reader->slot[0].proto = ifd_protocol_new(answer[1] & 0x0F, reader, reader->slot[0].dad); + if (reader->slot[0].proto == NULL) { + /* this should not happen, because we already checked for T0/1 */ + ct_error("No suitable protocol driver found"); + return IFD_ERROR_INCOMPATIBLE_DEVICE; + } + + return 0; +} + +/* + * Driver operations + */ +static struct ifd_driver_ops imx_driver = { + .open = imx_open, + .close = imx_close, + + .card_reset = imx_card_reset, + .change_parity = NULL, + .change_speed = NULL, + .set_protocol = imx_set_protocol, + .card_status = imx_card_status, + + .activate = imx_activate, + .deactivate = imx_deactivate, + + .send = imx_send, + .recv = imx_recv, + + .get_eventfd = NULL, /* makes no sense */ + .event = NULL, + .error = NULL, +}; + +/* + * Initialize this module + */ +void ifd_imx_register(void) +{ + ifd_driver_register("i.MX", &imx_driver); +} Index: openct-0.6.20/src/ifd/init.c =================================================================== --- openct-0.6.20.orig/src/ifd/init.c +++ openct-0.6.20/src/ifd/init.c @@ -38,6 +38,7 @@ int ifd_init(void) ifd_pertosmart_ac1030_register(); ifd_pertosmart_ac1038_register(); ifd_smartboard_register(); + ifd_imx_register(); ifd_smph_register(); ifd_starkey_register(); ifd_towitoko_register(); Index: openct-0.6.20/src/ifd/internal.h =================================================================== --- openct-0.6.20.orig/src/ifd/internal.h +++ openct-0.6.20/src/ifd/internal.h @@ -133,6 +133,7 @@ extern void ifd_kaan_register(void); extern void ifd_pertosmart_ac1030_register(void); extern void ifd_pertosmart_ac1038_register(void); extern void ifd_smartboard_register(void); +extern void ifd_imx_register(void); extern void ifd_smph_register(void); extern void ifd_starkey_register(void); extern void ifd_towitoko_register(void); -- _______________________________________________ opensc-devel mailing list opensc-devel@lists.opensc-project.org http://www.opensc-project.org/mailman/listinfo/opensc-devel