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(&param->FI, 
&param->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

Reply via email to