/*****************************************************************************
 *
 *				ReadChunk: read a minimum number of bytes
 *
 *****************************************************************************/
static int ReadChunk(unsigned int reader_index, unsigned char *buffer,
	int buffer_length, int min_length)
{
	int fd = serialDevice[reader_index].fd;
# ifndef S_SPLINT_S
	fd_set fdset;
# endif
	struct timeval t;
	int i, rv = 0;
	int already_read;
	char debug_header[] = "<- 123456 ";

	(void)snprintf(debug_header, sizeof(debug_header), "<- %06X ",
		reader_index);

	already_read = 0;
	while (already_read < min_length)
	{
		/* use select() to, eventually, timeout */
		FD_ZERO(&fdset);
		FD_SET(fd, &fdset);
		t.tv_sec = serialDevice[reader_index].ccid.readTimeout / 1000;
		t.tv_usec = (serialDevice[reader_index].ccid.readTimeout - t.tv_sec*1000)*1000;

		i = select(fd+1, &fdset, NULL, NULL, &t);
		if (i == -1)
		{
			DEBUG_CRITICAL2("select: %s", strerror(errno));
			return -1;
		}
		else
			if (i == 0)
			{
				DEBUG_COMM2("Timeout! (%d ms)", serialDevice[reader_index].ccid.readTimeout);
				return -1;
			}

		rv = read(fd, buffer + already_read, buffer_length - already_read);
		if (rv < 0)
		{
			DEBUG_COMM2("read error: %s", strerror(errno));
			return -1;
		}

		DEBUG_XXD(debug_header, buffer + already_read, rv);

		already_read += rv;
		DEBUG_COMM3("read: %d, to read: %d", already_read,
			min_length);
	}

	return already_read;
} /* ReadChunk */
/*****************************************************************************
 *
 *				CloseSerial: close the port
 *
 *****************************************************************************/
status_t CloseSerial(unsigned int reader_index)
{
	unsigned int reader = reader_index;

	/* device not opened */
	if (NULL == serialDevice[reader_index].device)
		return STATUS_UNSUCCESSFUL;

	DEBUG_COMM2("Closing serial device: %s", serialDevice[reader_index].device);

	/* Decrement number of opened slot */
	(*serialDevice[reader_index].nb_opened_slots)--;

	/* release the allocated ressources for the last slot only */
	if (0 == *serialDevice[reader_index].nb_opened_slots)
	{
		DEBUG_COMM("Last slot closed. Release resources");

		(void)close(serialDevice[reader].fd);
		serialDevice[reader].fd = -1;

		free(serialDevice[reader].device);
		serialDevice[reader].device = NULL;
	}

	return STATUS_SUCCESS;
} /* CloseSerial */
/*****************************************************************************
 *
 *				get_bytes: get n bytes
 *
 *****************************************************************************/
int get_bytes(unsigned int reader_index, unsigned char *buffer, int length)
{
	int offset = serialDevice[reader_index].buffer_offset;
	int offset_last = serialDevice[reader_index].buffer_offset_last;

	DEBUG_COMM3("available: %d, needed: %d", offset_last-offset,
		length);
	/* enough data are available */
	if (offset + length <= offset_last)
	{
		DEBUG_COMM("data available");
		memcpy(buffer, serialDevice[reader_index].buffer + offset, length);
		serialDevice[reader_index].buffer_offset += length;
	}
	else
	{
		int present, rv;

		/* copy available data */
		present = offset_last - offset;

		if (present > 0)
		{
			DEBUG_COMM2("some data available: %d", present);
			memcpy(buffer, serialDevice[reader_index].buffer + offset,
				present);
		}

		/* get fresh data */
		DEBUG_COMM2("get more data: %d", length - present);
		rv = ReadChunk(reader_index, serialDevice[reader_index].buffer,
			sizeof(serialDevice[reader_index].buffer), length - present);
		if (rv < 0)
			return STATUS_COMM_ERROR;

		/* fill the buffer */
		memcpy(buffer + present, serialDevice[reader_index].buffer,
			length - present);
		serialDevice[reader_index].buffer_offset = length - present;
		serialDevice[reader_index].buffer_offset_last = rv;
		DEBUG_COMM3("offset: %d, last_offset: %d",
			serialDevice[reader_index].buffer_offset,
			serialDevice[reader_index].buffer_offset_last);
	}

	return STATUS_SUCCESS;
} /* get_bytes */
unsigned int t1_build(t1_state_t * t1, unsigned char *block,
	unsigned char dad, unsigned char pcb,
	ct_buf_t *bp, size_t *lenp)
{
	unsigned int len;
	char more = FALSE;

	len = bp ? ct_buf_avail(bp) : 0;
	if (len > t1->ifsc) {
		pcb |= T1_MORE_BLOCKS;
		len = t1->ifsc;
		more = TRUE;
	}

	/* Add the sequence number */
	switch (t1_block_type(pcb)) {
	case T1_R_BLOCK:
		pcb |= t1->nr << T1_R_SEQ_SHIFT;
		break;
	case T1_I_BLOCK:
		pcb |= t1->ns << T1_I_SEQ_SHIFT;
		t1->more = more;
		DEBUG_COMM2("more bit: %d", more);
		break;
	}

	block[0] = dad;
	block[1] = pcb;
	block[2] = len;

	if (len)
		memcpy(block + 3, ct_buf_head(bp), len);
	if (lenp)
		*lenp = len;

	len = t1_compute_checksum(t1, block, len + 3);

	/* memorize the last sent block */
	/* only 4 bytes since we are only interesed in R-blocks */
	memcpy(t1->previous_block, block, 4);

	return len;
}
Exemple #5
0
/*****************************************************************************
 *
 *					OpenUSBByName
 *
 ****************************************************************************/
status_t OpenUSBByName(unsigned int reader_index, /*@null@*/ char *device)
{
	unsigned int alias;
	struct libusb_device_handle *dev_handle;
	char infofile[FILENAME_MAX];
#ifndef __APPLE__
	unsigned int device_vendor, device_product;
#endif
	int interface_number = -1;
	int i;
	static int previous_reader_index = -1;
	libusb_device **devs, *dev;
	ssize_t cnt;
	list_t plist, *values, *ifdVendorID, *ifdProductID, *ifdFriendlyName;
	int rv;
	int claim_failed = FALSE;
	int return_value = STATUS_SUCCESS;

	DEBUG_COMM3("Reader index: %X, Device: %s", reader_index, device);

#ifndef __APPLE__
	/* device name specified */
	if (device)
	{
		char *dirname;

		/* format: usb:%04x/%04x, vendor, product */
		if (strncmp("usb:", device, 4) != 0)
		{
			DEBUG_CRITICAL2("device name does not start with \"usb:\": %s",
				device);
			return STATUS_UNSUCCESSFUL;
		}

		if (sscanf(device, "usb:%x/%x", &device_vendor, &device_product) != 2)
		{
			DEBUG_CRITICAL2("device name can't be parsed: %s", device);
			return STATUS_UNSUCCESSFUL;
		}

		/* format usb:%04x/%04x:libudev:%d:%s
		 * with %d set to
		 * 01 (or whatever the interface number is)
		 * and %s set to
		 * /dev/bus/usb/008/004
		 */
		if ((dirname = strstr(device, "libudev:")) != NULL)
		{
			/* convert the interface number */
			interface_number = atoi(dirname + 8 /* "libudev:" */);
			DEBUG_COMM2("interface_number: %d", interface_number);
		}
	}
#endif

	/* is the reader_index already used? */
	if (usbDevice[reader_index].dev_handle != NULL)
	{
		DEBUG_CRITICAL2("USB driver with index %X already in use",
			reader_index);
		return STATUS_UNSUCCESSFUL;
	}

	/* Info.plist full patch filename */
	(void)snprintf(infofile, sizeof(infofile), "%s/%s/Contents/Info.plist",
		PCSCLITE_HP_DROPDIR, BUNDLE);
	DEBUG_INFO2("Using: %s", infofile);

	rv = bundleParse(infofile, &plist);
	if (rv)
		return STATUS_UNSUCCESSFUL;

#define GET_KEY(key, values) \
	rv = LTPBundleFindValueWithKey(&plist, key, &values); \
	if (rv) \
	{ \
		DEBUG_CRITICAL2("Value/Key not defined for " key " in %s", infofile); \
		return_value = STATUS_UNSUCCESSFUL; \
		goto end1; \
	} \
	else \
		DEBUG_INFO2(key ": %s", (char *)list_get_at(values, 0));

	/* general driver info */
	GET_KEY("ifdManufacturerString", values)
	GET_KEY("ifdProductString", values)
	GET_KEY("Copyright", values)

	if (NULL == ctx)
	{
		rv = libusb_init(&ctx);
		if (rv != 0)
		{
			DEBUG_CRITICAL2("libusb_init failed: %d", rv);
			return_value = STATUS_UNSUCCESSFUL;
			goto end1;
		}
	}

	cnt = libusb_get_device_list(ctx, &devs);
	if (cnt < 0)
	{
		DEBUG_CRITICAL("libusb_get_device_list() failed\n");
		return_value = STATUS_UNSUCCESSFUL;
		goto end1;
	}

#define GET_KEYS(key, values) \
	rv = LTPBundleFindValueWithKey(&plist, key, values); \
	if (rv) \
	{ \
		DEBUG_CRITICAL2("Value/Key not defined for " key " in %s", infofile); \
		return_value = STATUS_UNSUCCESSFUL; \
		goto end2; \
	}

	GET_KEYS("ifdVendorID", &ifdVendorID)
	GET_KEYS("ifdProductID", &ifdProductID);
	GET_KEYS("ifdFriendlyName", &ifdFriendlyName)

	/* The 3 lists do not have the same size */
	if  ((list_size(ifdVendorID) != list_size(ifdProductID))
		|| (list_size(ifdVendorID) != list_size(ifdFriendlyName)))
	{
		DEBUG_CRITICAL2("Error parsing %s", infofile);
		return_value = STATUS_UNSUCCESSFUL;
		goto end1;
	}

	/* for any supported reader */
	for (alias=0; alias<list_size(ifdVendorID); alias++)
	{
		unsigned int vendorID, productID;
		char *friendlyName;

		vendorID = strtoul(list_get_at(ifdVendorID, alias), NULL, 0);
		productID = strtoul(list_get_at(ifdProductID, alias), NULL, 0);
		friendlyName = list_get_at(ifdFriendlyName, alias);

#ifndef __APPLE__
		/* the device was specified but is not the one we are trying to find */
		if (device
			&& (vendorID != device_vendor || productID != device_product))
			continue;
#else
		/* Leopard puts the friendlyname in the device argument */
		if (device && strcmp(device, friendlyName))
			continue;
#endif

		/* for every device */
		i = 0;
		while ((dev = devs[i++]) != NULL)
		{
			struct libusb_device_descriptor desc;
			struct libusb_config_descriptor *config_desc;
			uint8_t bus_number = libusb_get_bus_number(dev);
			uint8_t device_address = libusb_get_device_address(dev);

			int r = libusb_get_device_descriptor(dev, &desc);
			if (r < 0)
			{
				DEBUG_INFO3("failed to get device descriptor for %d/%d",
					bus_number, device_address);
				continue;
			}

			if (desc.idVendor == vendorID && desc.idProduct == productID)
			{
				int already_used;
				const struct libusb_interface *usb_interface = NULL;
				int interface;
				int num = 0;
				const unsigned char *device_descriptor;
#if defined(USE_COMPOSITE_AS_MULTISLOT) || defined(__APPLE__)
				int readerID = (vendorID << 16) + productID;
#endif

#ifdef USE_COMPOSITE_AS_MULTISLOT
				static int static_interface = 1;

				/* simulate a composite device as when libudev is used */
				if ((GEMALTOPROXDU == readerID)
					|| (GEMALTOPROXSU == readerID))
				{
						/*
						 * We can't talk to the two CCID interfaces
						 * at the same time (the reader enters a
						 * dead lock). So we simulate a multi slot
						 * reader. By default multi slot readers
						 * can't use the slots at the same time. See
						 * TAG_IFD_SLOT_THREAD_SAFE
						 *
						 * One side effect is that the two readers
						 * are seen by pcscd as one reader so the
						 * interface name is the same for the two.
						 *
	* So we have:
	* 0: Gemalto Prox-DU [Prox-DU Contact_09A00795] (09A00795) 00 00
	* 1: Gemalto Prox-DU [Prox-DU Contact_09A00795] (09A00795) 00 01
	* instead of
	* 0: Gemalto Prox-DU [Prox-DU Contact_09A00795] (09A00795) 00 00
	* 1: Gemalto Prox-DU [Prox-DU Contactless_09A00795] (09A00795) 01 00
						 */

					/* the CCID interfaces are 1 and 2 */
					interface_number = static_interface;
				}
#endif
				/* is it already opened? */
				already_used = FALSE;

				DEBUG_COMM3("Checking device: %d/%d",
					bus_number, device_address);
				for (r=0; r<CCID_DRIVER_MAX_READERS; r++)
				{
					if (usbDevice[r].dev_handle)
					{
						/* same bus, same address */
						if (usbDevice[r].bus_number == bus_number
							&& usbDevice[r].device_address == device_address)
							already_used = TRUE;
					}
				}

				/* this reader is already managed by us */
				if (already_used)
				{
					if ((previous_reader_index != -1)
						&& usbDevice[previous_reader_index].dev_handle
						&& (usbDevice[previous_reader_index].bus_number == bus_number)
						&& (usbDevice[previous_reader_index].device_address == device_address)
						&& usbDevice[previous_reader_index].ccid.bCurrentSlotIndex < usbDevice[previous_reader_index].ccid.bMaxSlotIndex)
					{
						/* we reuse the same device
						 * and the reader is multi-slot */
						usbDevice[reader_index] = usbDevice[previous_reader_index];
						/* the other slots do not have the same data rates */
						if ((GEMCOREPOSPRO == usbDevice[reader_index].ccid.readerID)
							|| (GEMCORESIMPRO == usbDevice[reader_index].ccid.readerID))
						{
							usbDevice[reader_index].ccid.arrayOfSupportedDataRates = SerialCustomDataRates;
							usbDevice[reader_index].ccid.dwMaxDataRate = 125000;
						}

						*usbDevice[reader_index].nb_opened_slots += 1;
						usbDevice[reader_index].ccid.bCurrentSlotIndex++;
						usbDevice[reader_index].ccid.dwSlotStatus =
							IFD_ICC_PRESENT;
						DEBUG_INFO2("Opening slot: %d",
							usbDevice[reader_index].ccid.bCurrentSlotIndex);
						goto end;
					}
					else
					{
						/* if an interface number is given by HAL we
						 * continue with this device. */
						if (-1 == interface_number)
						{
							DEBUG_INFO3("USB device %d/%d already in use."
								" Checking next one.",
								bus_number, device_address);
							continue;
						}
					}
				}

				DEBUG_COMM3("Trying to open USB bus/device: %d/%d",
					bus_number, device_address);

				r = libusb_open(dev, &dev_handle);
				if (r < 0)
				{
					DEBUG_CRITICAL4("Can't libusb_open(%d/%d): %d",
						bus_number, device_address, r);

					continue;
				}

again:
#ifdef __APPLE__
				/* Some early Gemalto Ezio CB+ readers have
				 * bDeviceClass, bDeviceSubClass and bDeviceProtocol set
				 * to 0xFF (proprietary) instead of 0x00.
				 *
				 * So on Mac OS X the reader configuration is not done
				 * by the OS/kernel and we do it ourself.
				 */
				if (GEMALTO_EZIO_CBP == readerID)
				{
					r = libusb_set_configuration(dev_handle, 1);
					if (r < 0)
					{
						(void)libusb_close(dev_handle);
						DEBUG_CRITICAL4("Can't set configuration on %d/%d: %d",
							bus_number, device_address, r);
						continue;
					}
				}
#endif

				r = libusb_get_active_config_descriptor(dev, &config_desc);
				if (r < 0)
				{
					(void)libusb_close(dev_handle);
					DEBUG_CRITICAL4("Can't get config descriptor on %d/%d: %d",
						bus_number, device_address, r);
					continue;
				}

				usb_interface = get_ccid_usb_interface(config_desc, &num);
				if (usb_interface == NULL)
				{
					(void)libusb_close(dev_handle);
					if (0 == num)
						DEBUG_CRITICAL3("Can't find a CCID interface on %d/%d",
							bus_number, device_address);
					interface_number = -1;
					continue;
				}

				device_descriptor = get_ccid_device_descriptor(usb_interface);
				if (NULL == device_descriptor)
				{
					(void)libusb_close(dev_handle);
					DEBUG_CRITICAL3("Unable to find the device descriptor for %d/%d",
						bus_number, device_address);
					return_value = STATUS_UNSUCCESSFUL;
					goto end2;
				}

				interface = usb_interface->altsetting->bInterfaceNumber;
				if (interface_number >= 0 && interface != interface_number)
				{
					/* an interface was specified and it is not the
					 * current one */
					DEBUG_INFO3("Found interface %d but expecting %d",
						interface_number, interface);
					DEBUG_INFO3("Wrong interface for USB device %d/%d."
						" Checking next one.", bus_number, device_address);

					/* check for another CCID interface on the same device */
					num++;

					goto again;
				}

				r = libusb_claim_interface(dev_handle, interface);
				if (r < 0)
				{
					(void)libusb_close(dev_handle);
					DEBUG_CRITICAL4("Can't claim interface %d/%d: %d",
						bus_number, device_address, r);
					claim_failed = TRUE;
					interface_number = -1;
					continue;
				}

				DEBUG_INFO4("Found Vendor/Product: %04X/%04X (%s)",
					desc.idVendor, desc.idProduct, friendlyName);
				DEBUG_INFO3("Using USB bus/device: %d/%d",
					bus_number, device_address);

				/* check for firmware bugs */
				if (ccid_check_firmware(&desc))
				{
					(void)libusb_close(dev_handle);
					return_value = STATUS_UNSUCCESSFUL;
					goto end2;
				}

#ifdef USE_COMPOSITE_AS_MULTISLOT
				/* use the next interface for the next "slot" */
				static_interface++;

				/* reset for a next reader */
				if (static_interface > 2)
					static_interface = 1;
#endif

				/* Get Endpoints values*/
				(void)get_end_points(config_desc, &usbDevice[reader_index], num);

				/* store device information */
				usbDevice[reader_index].dev_handle = dev_handle;
				usbDevice[reader_index].bus_number = bus_number;
				usbDevice[reader_index].device_address = device_address;
				usbDevice[reader_index].interface = interface;
				usbDevice[reader_index].real_nb_opened_slots = 1;
				usbDevice[reader_index].nb_opened_slots = &usbDevice[reader_index].real_nb_opened_slots;
				usbDevice[reader_index].polling_transfer = NULL;

				/* CCID common informations */
				usbDevice[reader_index].ccid.real_bSeq = 0;
				usbDevice[reader_index].ccid.pbSeq = &usbDevice[reader_index].ccid.real_bSeq;
				usbDevice[reader_index].ccid.readerID =
					(desc.idVendor << 16) + desc.idProduct;
				usbDevice[reader_index].ccid.dwFeatures = dw2i(device_descriptor, 40);
				usbDevice[reader_index].ccid.wLcdLayout =
					(device_descriptor[51] << 8) + device_descriptor[50];
				usbDevice[reader_index].ccid.bPINSupport = device_descriptor[52];
				usbDevice[reader_index].ccid.dwMaxCCIDMessageLength = dw2i(device_descriptor, 44);
				usbDevice[reader_index].ccid.dwMaxIFSD = dw2i(device_descriptor, 28);
				usbDevice[reader_index].ccid.dwDefaultClock = dw2i(device_descriptor, 10);
				usbDevice[reader_index].ccid.dwMaxDataRate = dw2i(device_descriptor, 23);
				usbDevice[reader_index].ccid.bMaxSlotIndex = device_descriptor[4];
				usbDevice[reader_index].ccid.bCurrentSlotIndex = 0;
				usbDevice[reader_index].ccid.readTimeout = DEFAULT_COM_READ_TIMEOUT;
				usbDevice[reader_index].ccid.arrayOfSupportedDataRates = get_data_rates(reader_index, config_desc, num);
				usbDevice[reader_index].ccid.bInterfaceProtocol = usb_interface->altsetting->bInterfaceProtocol;
				usbDevice[reader_index].ccid.bNumEndpoints = usb_interface->altsetting->bNumEndpoints;
				usbDevice[reader_index].ccid.dwSlotStatus = IFD_ICC_PRESENT;
				usbDevice[reader_index].ccid.bVoltageSupport = device_descriptor[5];
				usbDevice[reader_index].ccid.sIFD_serial_number = NULL;
				usbDevice[reader_index].ccid.gemalto_firmware_features = NULL;
				if (desc.iSerialNumber)
				{
					unsigned char serial[128];
					int ret;

					ret = libusb_get_string_descriptor_ascii(dev_handle,
							desc.iSerialNumber, serial,
							sizeof(serial));
					if (ret > 0)
						usbDevice[reader_index].ccid.sIFD_serial_number
							= strdup((char *)serial);
				}

				usbDevice[reader_index].ccid.sIFD_iManufacturer = NULL;
				if (desc.iManufacturer)
				{
					unsigned char iManufacturer[128];
					int ret;

					ret = libusb_get_string_descriptor_ascii(dev_handle,
							desc.iManufacturer, iManufacturer,
							sizeof(iManufacturer));
					if (ret > 0)
						usbDevice[reader_index].ccid.sIFD_iManufacturer
							= strdup((char *)iManufacturer);
				}

				usbDevice[reader_index].ccid.IFD_bcdDevice = desc.bcdDevice;
				goto end;
			}
		}
	}
end:
	if (usbDevice[reader_index].dev_handle == NULL)
	{
		/* does not work for libusb <= 1.0.8 */
		/* libusb_exit(ctx); */
		if (claim_failed)
			return STATUS_COMM_ERROR;
		return STATUS_NO_SUCH_DEVICE;
	}

	/* memorise the current reader_index so we can detect
	 * a new OpenUSBByName on a multi slot reader */
	previous_reader_index = reader_index;

end2:
	/* free the libusb allocated list & devices */
	libusb_free_device_list(devs, 1);

end1:
	/* free bundle list */
	bundleRelease(&plist);

	return return_value;
} /* OpenUSBByName */
Exemple #6
0
/*****************************************************************************
 *
 *					ccid_open_hack_post
 *
 ****************************************************************************/
int ccid_open_hack_post(unsigned int reader_index)
{
	_ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
	RESPONSECODE return_value = IFD_SUCCESS;

	switch (ccid_descriptor->readerID)
	{
		case GEMPCKEY:
		case GEMPCTWIN:
			/* Reader announces TPDU but can do APDU (EMV in fact) */
			if (DriverOptions & DRIVER_OPTION_GEMPC_TWIN_KEY_APDU)
			{
				unsigned char cmd[] = { 0x1F, 0x02 };
				unsigned char res[10];
				unsigned int length_res = sizeof(res);

				if (CmdEscape(reader_index, cmd, sizeof(cmd), res, &length_res, 0) == IFD_SUCCESS)
				{
					ccid_descriptor->dwFeatures &= ~CCID_CLASS_EXCHANGE_MASK;
					ccid_descriptor->dwFeatures |= CCID_CLASS_SHORT_APDU;
				}
			}
			break;

		case VEGAALPHA:
		case GEMPCPINPAD:
			/* load the l10n strings in the pinpad memory */
			{
#define L10N_HEADER_SIZE 5
#define L10N_STRING_MAX_SIZE 16
#define L10N_NB_STRING 10

				unsigned char cmd[L10N_HEADER_SIZE + L10N_NB_STRING * L10N_STRING_MAX_SIZE];
				unsigned char res[20];
				unsigned int length_res = sizeof(res);
				int offset, i, j;

				const char *fr[] = {
					"Entrer PIN",
					"Nouveau PIN",
					"Confirmer PIN",
					"PIN correct",
					"PIN Incorrect !",
					"Delai depasse",
					"* essai restant",
					"Inserer carte",
					"Erreur carte",
					"PIN bloque" };

				const char *de[] = {
					"PIN eingeben",
					"Neue PIN",
					"PIN bestatigen",
					"PIN korrect",
					"Falsche PIN !",
					"Zeit abgelaufen",
					"* Versuche ubrig",
					"Karte einstecken",
					"Fehler Karte",
					"PIN blockiert" };

				const char *es[] = {
					"Introducir PIN",
					"Nuevo PIN",
					"Confirmar PIN",
					"PIN OK",
					"PIN Incorrecto !",
					"Tiempo Agotado",
					"* ensayos quedan",
					"Introducir Tarj.",
					"Error en Tarjeta",
					"PIN bloqueado" };

				const char *it[] = {
					"Inserire PIN",
					"Nuovo PIN",
					"Confermare PIN",
					"PIN Corretto",
					"PIN Errato !",
					"Tempo scaduto",
					"* prove rimaste",
					"Inserire Carta",
					"Errore Carta",
					"PIN ostruito"};

				const char *pt[] = {
					"Insira PIN",
					"Novo PIN",
					"Conf. novo PIN",
					"PIN OK",
					"PIN falhou!",
					"Tempo expirou",
					"* tentiv. restam",
					"Introduza cartao",
					"Erro cartao",
					"PIN bloqueado" };

				const char *nl[] = {
					"Inbrengen code",
					"Nieuwe code",
					"Bevestig code",
					"Code aanvaard",
					"Foute code",
					"Time out",
					"* Nog Pogingen",
					"Kaart inbrengen",
					"Kaart fout",
					"Kaart blok" };

				const char *tr[] = {
					"PIN Giriniz",
					"Yeni PIN",
					"PIN Onayala",
					"PIN OK",
					"Yanlis PIN",
					"Zaman Asimi",
					"* deneme kaldi",
					"Karti Takiniz",
					"Kart Hatasi",
					"Kart Kilitli" };

				const char *en[] = {
					"Enter PIN",
					"New PIN",
					"Confirm PIN",
					"PIN OK",
					"Incorrect PIN!",
					"Time Out",
					"* retries left",
					"Insert Card",
					"Card Error",
					"PIN blocked" };

				const char *lang;
				const char **l10n;

#ifdef __APPLE__
				CFArrayRef cfa;
				CFStringRef slang;

				/* Get the complete ordered list */
				cfa = CFLocaleCopyPreferredLanguages();

				/* Use the first/preferred language
				 * As the driver is run as root we get the language
				 * selected during install */
				slang = CFArrayGetValueAtIndex(cfa, 0);

				/* CFString -> C string */
				lang = CFStringGetCStringPtr(slang, kCFStringEncodingMacRoman);
#else
				/* The other Unixes just use the LANG env variable */
				lang = getenv("LANG");
#endif
				DEBUG_COMM2("Using lang: %s", lang);
				if (NULL == lang)
					l10n = en;
				else
				{
					if (0 == strncmp(lang, "fr", 2))
						l10n = fr;
					else if (0 == strncmp(lang, "de", 2))
						l10n = de;
					else if (0 == strncmp(lang, "es", 2))
						l10n = es;
					else if (0 == strncmp(lang, "it", 2))
						l10n = it;
					else if (0 == strncmp(lang, "pt", 2))
						l10n = pt;
					else if (0 == strncmp(lang, "nl", 2))
						l10n = nl;
					else if (0 == strncmp(lang, "tr", 2))
						l10n = tr;
					else
						l10n = en;
				}

#ifdef __APPLE__
				/* Release the allocated array */
				CFRelease(cfa);
#endif
				offset = 0;
				cmd[offset++] = 0xB2;	/* load strings */
				cmd[offset++] = 0xA0;	/* address of the memory */
				cmd[offset++] = 0x00;	/* address of the first byte */
				cmd[offset++] = 0x4D;	/* magic value */
				cmd[offset++] = 0x4C;	/* magic value */

				/* for each string */
				for (i=0; i<L10N_NB_STRING; i++)
				{
					/* copy the string */
					for (j=0; l10n[i][j]; j++)
						cmd[offset++] = l10n[i][j];

					/* pad with " " */
					for (; j<L10N_STRING_MAX_SIZE; j++)
						cmd[offset++] = ' ';
				}

				(void)sleep(1);
				if (IFD_SUCCESS == CmdEscape(reader_index, cmd, sizeof(cmd), res, &length_res, DEFAULT_COM_READ_TIMEOUT))
				{
					DEBUG_COMM("l10n string loaded successfully");
				}
				else
				{
					DEBUG_COMM("Failed to load l10n strings");
					return_value = IFD_COMMUNICATION_ERROR;
				}

				if (DriverOptions & DRIVER_OPTION_DISABLE_PIN_RETRIES)
				{
					/* disable VERIFY from reader */
					const unsigned char cmd2[] = {0xb5, 0x00};
					length_res = sizeof(res);
					if (IFD_SUCCESS == CmdEscape(reader_index, cmd2, sizeof(cmd2), res, &length_res, DEFAULT_COM_READ_TIMEOUT))
					{
						DEBUG_COMM("Disable SPE retry counter successful");
					}
					else
					{
						DEBUG_CRITICAL("Failed to disable SPE retry counter");
					}
				}
			}
			break;

		case HPSMARTCARDKEYBOARD:
		case HP_CCIDSMARTCARDKEYBOARD:
		case FUJITSUSMARTKEYB:
			/* the Secure Pin Entry is bogus so disable it
			 * https://web.archive.org/web/20120320001756/http://martinpaljak.net/2011/03/19/insecure-hp-usb-smart-card-keyboard/
			 *
			 * The problem is that the PIN code entered using the Secure
			 * Pin Entry function is also sent to the host.
			 */
			ccid_descriptor->bPINSupport = 0;
			break;
		case HID_AVIATOR:
			/* The chip advertises pinpad but actually doesn't have one */
			ccid_descriptor->bPINSupport = 0;
			/* Firmware uses chaining */
			ccid_descriptor->dwFeatures &= ~CCID_CLASS_EXCHANGE_MASK;
			ccid_descriptor->dwFeatures |= CCID_CLASS_EXTENDED_APDU;
			break;

#if 0
		/* SCM SCR331-DI contactless */
		case SCR331DI:
		/* SCM SCR331-DI-NTTCOM contactless */
		case SCR331DINTTCOM:
		/* SCM SDI010 contactless */
		case SDI010:
			/* the contactless reader is in the second slot */
			if (ccid_descriptor->bCurrentSlotIndex > 0)
			{
				unsigned char cmd1[] = { 0x00 };
				/*  command: 00 ??
				 * response: 06 10 03 03 00 00 00 01 FE FF FF FE 01 ?? */
				unsigned char cmd2[] = { 0x02 };
				/*  command: 02 ??
				 * response: 00 ?? */

				unsigned char res[20];
				unsigned int length_res = sizeof(res);

				if ((IFD_SUCCESS == CmdEscape(reader_index, cmd1, sizeof(cmd1), res, &length_res, 0))
					&& (IFD_SUCCESS == CmdEscape(reader_index, cmd2, sizeof(cmd2), res, &length_res, 0)))
				{
					DEBUG_COMM("SCM SCR331-DI contactless detected");
				}
				else
				{
					DEBUG_COMM("SCM SCR331-DI contactless init failed");
				}

				/* hack since the contactless reader do not share dwFeatures */
				ccid_descriptor->dwFeatures &= ~CCID_CLASS_EXCHANGE_MASK;
				ccid_descriptor->dwFeatures |= CCID_CLASS_SHORT_APDU;

				ccid_descriptor->dwFeatures |= CCID_CLASS_AUTO_IFSD;
			}
			break;
#endif
		case CHERRY_KC1000SC:
			if ((0x0100 == ccid_descriptor->IFD_bcdDevice)
				&& (ccid_descriptor->dwFeatures & CCID_CLASS_EXCHANGE_MASK) == CCID_CLASS_SHORT_APDU)
			{
				/* firmware 1.00 is bogus
				 * With a T=1 card and case 2 APDU (data from card to
				 * host) the maximum size returned by the reader is 128
				 * byes. The reader is then using chaining as with
				 * extended APDU.
				 */
				ccid_descriptor->dwFeatures &= ~CCID_CLASS_EXCHANGE_MASK;
				ccid_descriptor->dwFeatures |= CCID_CLASS_EXTENDED_APDU;
			}
			break;

		case ElatecTWN4:
		case SCM_SCL011:
			/* restore default timeout (modified in ccid_open_hack_pre()) */
			ccid_descriptor->readTimeout = DEFAULT_COM_READ_TIMEOUT;
			break;
	}

	/* Gemalto readers may report additional information */
	if (GET_VENDOR(ccid_descriptor->readerID) == VENDOR_GEMALTO)
		set_gemalto_firmware_features(reader_index);

	return return_value;
} /* ccid_open_hack_post */
/*****************************************************************************
 *
 *				set_ccid_descriptor: init ccid descriptor
 *				depending on reader type specified in device.
 *
 *				return: STATUS_UNSUCCESSFUL,
 *						STATUS_SUCCESS,
 *						-1 (Reader already used)
 *
 *****************************************************************************/
static status_t set_ccid_descriptor(unsigned int reader_index,
	const char *reader_name, const char *dev_name)
{
	int readerID;
	int i;
	int already_used = FALSE;
	static int previous_reader_index = -1;

	readerID = GEMPCTWIN;
	if (0 == strcasecmp(reader_name,"GemCorePOSPro"))
		readerID = GEMCOREPOSPRO;
	else if (0 == strcasecmp(reader_name,"GemCoreSIMPro"))
		readerID = GEMCORESIMPRO;
	else if (0 == strcasecmp(reader_name,"GemPCPinPad"))
		readerID = GEMPCPINPAD;

	/* check if the same channel is not already used to manage multi-slots readers*/
	for (i = 0; i < CCID_DRIVER_MAX_READERS; i++)
	{
		if (serialDevice[i].device
			&& strcmp(serialDevice[i].device, dev_name) == 0)
		{
			already_used = TRUE;

			DEBUG_COMM2("%s already used. Multi-slot reader?", dev_name);
			break;
		}
	}

	/* this reader is already managed by us */
	if (already_used)
	{
		if ((previous_reader_index != -1)
			&& serialDevice[previous_reader_index].device
			&& (strcmp(serialDevice[previous_reader_index].device, dev_name) == 0)
			&& serialDevice[previous_reader_index].ccid.bCurrentSlotIndex < serialDevice[previous_reader_index].ccid.bMaxSlotIndex)
		{
			/* we reuse the same device and the reader is multi-slot */
			serialDevice[reader_index] = serialDevice[previous_reader_index];

			*serialDevice[reader_index].nb_opened_slots += 1;
			serialDevice[reader_index].ccid.bCurrentSlotIndex++;
			serialDevice[reader_index].ccid.dwSlotStatus = IFD_ICC_PRESENT;
			DEBUG_INFO2("Opening slot: %d",
					serialDevice[reader_index].ccid.bCurrentSlotIndex);
			switch (readerID)
			{
				case GEMCOREPOSPRO:
				case GEMCORESIMPRO:
					serialDevice[reader_index].ccid.arrayOfSupportedDataRates = SerialCustomDataRates;
					serialDevice[reader_index].ccid.dwMaxDataRate = 125000;
					break;

				/* GemPC Twin or GemPC Card */
				default:
					serialDevice[reader_index].ccid.arrayOfSupportedDataRates = SerialTwinDataRates;
					serialDevice[reader_index].ccid.dwMaxDataRate = 344086;
					break;
			}
			goto end;
		}
		else
		{
			DEBUG_CRITICAL2("Trying to open too many slots on %s", dev_name);
			return STATUS_UNSUCCESSFUL;
		}

	}

	/* Common to all readers */
	serialDevice[reader_index].ccid.real_bSeq = 0;
	serialDevice[reader_index].ccid.pbSeq = &serialDevice[reader_index].ccid.real_bSeq;
	serialDevice[reader_index].real_nb_opened_slots = 1;
	serialDevice[reader_index].nb_opened_slots = &serialDevice[reader_index].real_nb_opened_slots;
	serialDevice[reader_index].ccid.bCurrentSlotIndex = 0;

	serialDevice[reader_index].ccid.dwMaxCCIDMessageLength = 271;
	serialDevice[reader_index].ccid.dwMaxIFSD = 254;
	serialDevice[reader_index].ccid.dwFeatures = 0x00010230;
	serialDevice[reader_index].ccid.dwDefaultClock = 4000;

	serialDevice[reader_index].buffer_offset = 0;
	serialDevice[reader_index].buffer_offset_last = 0;

	serialDevice[reader_index].ccid.readerID = readerID;
	serialDevice[reader_index].ccid.bPINSupport = 0x0;
	serialDevice[reader_index].ccid.dwMaxDataRate = 344086;
	serialDevice[reader_index].ccid.bMaxSlotIndex = 0;
	serialDevice[reader_index].ccid.arrayOfSupportedDataRates = SerialTwinDataRates;
	serialDevice[reader_index].ccid.dwSlotStatus = IFD_ICC_PRESENT;
	serialDevice[reader_index].ccid.bVoltageSupport = 0x07;	/* 1.8V, 3V and 5V */
	serialDevice[reader_index].ccid.gemalto_firmware_features = NULL;
	serialDevice[reader_index].echo = TRUE;

	/* change some values depending on the reader */
	switch (readerID)
	{
		case GEMCOREPOSPRO:
			serialDevice[reader_index].ccid.bMaxSlotIndex = 4;	/* 5 slots */
			serialDevice[reader_index].ccid.arrayOfSupportedDataRates = SerialExtendedDataRates;
			serialDevice[reader_index].echo = FALSE;
			serialDevice[reader_index].ccid.dwMaxDataRate = 500000;
			break;

		case GEMCORESIMPRO:
			serialDevice[reader_index].ccid.bMaxSlotIndex = 1; /* 2 slots */
			serialDevice[reader_index].ccid.arrayOfSupportedDataRates = SerialExtendedDataRates;
			serialDevice[reader_index].echo = FALSE;
			serialDevice[reader_index].ccid.dwMaxDataRate = 500000;
			break;

		case GEMPCPINPAD:
			serialDevice[reader_index].ccid.bPINSupport = 0x03;
			serialDevice[reader_index].ccid.arrayOfSupportedDataRates = SerialExtendedDataRates;
			serialDevice[reader_index].ccid.dwMaxDataRate = 500000;
			break;
	}

end:
	/* memorise the current reader_index so we can detect
	 * a new OpenSerialByName on a multi slot reader */
	previous_reader_index = reader_index;

	/* we just created a secondary slot on a multi-slot reader */
	if (already_used)
		return STATUS_SECONDARY_SLOT;

	return STATUS_SUCCESS;
} /* set_ccid_descriptor  */
/*****************************************************************************
 *
 *				ReadSerial: Receive bytes from the card reader
 *
 *****************************************************************************/
status_t ReadSerial(unsigned int reader_index,
	unsigned int *length, unsigned char *buffer)
{
	unsigned char c;
	int rv;
	int echo;
	int to_read;
	int i;

	/* we get the echo first */
	echo = serialDevice[reader_index].echo;

start:
	DEBUG_COMM("start");
	if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS)
		return rv;

	if (c == RDR_to_PC_NotifySlotChange)
		goto slot_change;

	if (c == SYNC)
		goto sync;

	if (c >= 0x80)
	{
		DEBUG_COMM2("time request: 0x%02X", c);
		goto start;
	}

	DEBUG_CRITICAL2("Got 0x%02X", c);
	return STATUS_COMM_ERROR;

slot_change:
	DEBUG_COMM("slot change");
	if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS)
		return rv;

	if (c == CARD_ABSENT)
	{
		DEBUG_COMM("Card removed");
	}
	else
		if (c == CARD_PRESENT)
		{
			DEBUG_COMM("Card inserted");
		}
		else
		{
			DEBUG_COMM2("Unknown card movement: %d", c);
		}
	goto start;

sync:
	DEBUG_COMM("sync");
	if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS)
		return rv;

	if (c == CTRL_ACK)
		goto ack;

	if (c == CTRL_NAK)
		goto nak;

	DEBUG_CRITICAL2("Got 0x%02X instead of ACK/NAK", c);
	return STATUS_COMM_ERROR;

nak:
	DEBUG_COMM("nak");
	if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS)
		return rv;

	if (c != (SYNC ^ CTRL_NAK))
	{
		DEBUG_CRITICAL2("Wrong LRC: 0x%02X", c);
		return STATUS_COMM_ERROR;
	}
	else
	{
		DEBUG_COMM("NAK requested");
		return STATUS_COMM_NAK;
	}

ack:
	DEBUG_COMM("ack");
	/* normal CCID frame */
	if ((rv = get_bytes(reader_index, buffer, 5)) != STATUS_SUCCESS)
		return rv;

	/* total frame size */
	to_read = 10+dw2i(buffer, 1);

	if ((to_read < 10) || (to_read > (int)*length))
	{
		DEBUG_CRITICAL2("Wrong value for frame size: %d", to_read);
		return STATUS_COMM_ERROR;
	}

	DEBUG_COMM2("frame size: %d", to_read);
	if ((rv = get_bytes(reader_index, buffer+5, to_read-5)) != STATUS_SUCCESS)
		return rv;

	DEBUG_XXD("frame: ", buffer, to_read);

	/* lrc */
	DEBUG_COMM("lrc");
	if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS)
		return rv;

	DEBUG_COMM2("lrc: 0x%02X", c);
	for (i=0; i<to_read; i++)
		c ^= buffer[i];

	if (c != (SYNC ^ CTRL_ACK))
		DEBUG_CRITICAL2("Wrong LRC: 0x%02X", c);

	if (echo)
	{
		echo = FALSE;
		goto start;
	}

	/* length of data read */
	*length = to_read;

	return STATUS_SUCCESS;
} /* ReadSerial */
/*
 * Send an APDU through T=1
 */
int t1_transceive(t1_state_t * t1, unsigned int dad,
		const void *snd_buf, size_t snd_len,
		void *rcv_buf, size_t rcv_len)
{
	ct_buf_t sbuf, rbuf, tbuf;
	unsigned char sdata[T1_BUFFER_SIZE], sblk[5];
	unsigned int slen, retries, resyncs, sent_length = 0;
	size_t last_send = 0;

	if (snd_len == 0)
		return -1;

	/* we can't talk to a dead card / reader. Reset it! */
	if (t1->state == DEAD)
	{
		DEBUG_CRITICAL("T=1 state machine is DEAD. Reset the card first.");
		return -1;
	}

	t1->state = SENDING;
	retries = t1->retries;
	resyncs = 3;

	/* Initialize send/recv buffer */
	ct_buf_set(&sbuf, (void *)snd_buf, snd_len);
	ct_buf_init(&rbuf, rcv_buf, rcv_len);

	/* Send the first block */
	slen = t1_build(t1, sdata, dad, T1_I_BLOCK, &sbuf, &last_send);

	while (1) {
		unsigned char pcb;
		int n;

		retries--;

		n = t1_xcv(t1, sdata, slen, sizeof(sdata));
		if (-2 == n)
		{
			DEBUG_COMM("Parity error");
			/* ISO 7816-3 Rule 7.4.2 */
			if (retries == 0)
				goto resync;

			/* ISO 7816-3 Rule 7.2 */
			if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB]))
			{
				DEBUG_COMM("Rule 7.2");
				slen = t1_rebuild(t1, sdata);
				continue;
			}

			slen = t1_build(t1, sdata,
					dad, T1_R_BLOCK | T1_EDC_ERROR,
					NULL, NULL);
			continue;
		}

		if (n < 0) {
			DEBUG_CRITICAL("fatal: transmit/receive failed");
			t1->state = DEAD;
			goto error;
		}

		if ((sdata[NAD] != swap_nibbles(dad)) /* wrong NAD */
			|| (sdata[LEN] == 0xFF))	/* length == 0xFF (illegal) */
		{
			DEBUG_COMM("R-BLOCK required");
			/* ISO 7816-3 Rule 7.4.2 */
			if (retries == 0)
				goto resync;

			/* ISO 7816-3 Rule 7.2 */
			if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB]))
			{
				DEBUG_COMM("Rule 7.2");
				slen = t1_rebuild(t1, sdata);
				continue;
			}

			slen = t1_build(t1, sdata,
				dad, T1_R_BLOCK | T1_OTHER_ERROR,
				NULL, NULL);
			continue;
		}

		if (!t1_verify_checksum(t1, sdata, n)) {
			DEBUG_COMM("checksum failed");
			/* ISO 7816-3 Rule 7.4.2 */
			if (retries == 0)
				goto resync;

			/* ISO 7816-3 Rule 7.2 */
			if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB]))
			{
				DEBUG_COMM("Rule 7.2");
				slen = t1_rebuild(t1, sdata);
				continue;
			}

			slen = t1_build(t1, sdata,
				dad, T1_R_BLOCK | T1_EDC_ERROR,
				NULL, NULL);
			continue;
		}

		pcb = sdata[PCB];
		switch (t1_block_type(pcb)) {
		case T1_R_BLOCK:
			if ((sdata[LEN] != 0x00)	/* length != 0x00 (illegal) */
				|| (pcb & 0x20)			/* b6 of pcb is set */
			   )
			{
				DEBUG_COMM("R-Block required");
				/* ISO 7816-3 Rule 7.4.2 */
				if (retries == 0)
					goto resync;

				/* ISO 7816-3 Rule 7.2 */
				if (T1_R_BLOCK == t1_block_type(t1->previous_block[1]))
				{
					DEBUG_COMM("Rule 7.2");
					slen = t1_rebuild(t1, sdata);
					continue;
				}

				slen = t1_build(t1, sdata,
						dad, T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
				continue;
			}

			if (((t1_seq(pcb) != t1->ns)	/* wrong sequence number & no bit more */
					&& ! t1->more)
			   )
			{
				DEBUG_COMM4("received: %d, expected: %d, more: %d",
					t1_seq(pcb), t1->ns, t1->more);

				/* ISO 7816-3 Rule 7.2 */
				if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB]))
				{
					DEBUG_COMM("Rule 7.2");
					slen = t1_rebuild(t1, sdata);
					continue;
				}

				DEBUG_COMM("R-Block required");
				/* ISO 7816-3 Rule 7.4.2 */
				if (retries == 0)
					goto resync;
				slen = t1_build(t1, sdata,
						dad, T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
				continue;
			}

			if (t1->state == RECEIVING) {
				/* ISO 7816-3 Rule 7.2 */
				if (T1_R_BLOCK == t1_block_type(t1->previous_block[1]))
				{
					DEBUG_COMM("Rule 7.2");
					slen = t1_rebuild(t1, sdata);
					continue;
				}

				DEBUG_COMM("");
				slen = t1_build(t1, sdata,
						dad, T1_R_BLOCK,
						NULL, NULL);
				break;
			}

			/* If the card terminal requests the next
			 * sequence number, it received the previous
			 * block successfully */
			if (t1_seq(pcb) != t1->ns) {
				ct_buf_get(&sbuf, NULL, last_send);
				sent_length += last_send;
				last_send = 0;
				t1->ns ^= 1;
			}

			/* If there's no data available, the ICC
			 * shouldn't be asking for more */
			if (ct_buf_avail(&sbuf) == 0)
				goto resync;

			slen = t1_build(t1, sdata, dad, T1_I_BLOCK,
					&sbuf, &last_send);
			break;

		case T1_I_BLOCK:
			/* The first I-block sent by the ICC indicates
			 * the last block we sent was received successfully. */
			if (t1->state == SENDING) {
				DEBUG_COMM("");
				ct_buf_get(&sbuf, NULL, last_send);
				last_send = 0;
				t1->ns ^= 1;
			}

			t1->state = RECEIVING;

			/* If the block sent by the card doesn't match
			 * what we expected it to send, reply with
			 * an R block */
			if (t1_seq(pcb) != t1->nr) {
				DEBUG_COMM("wrong nr");
				slen = t1_build(t1, sdata, dad,
						T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
				continue;
			}

			t1->nr ^= 1;

			if (ct_buf_put(&rbuf, sdata + 3, sdata[LEN]) < 0)
			{
				DEBUG_CRITICAL2("buffer overrun by %d bytes", sdata[LEN] - (rbuf.size - rbuf.tail));
				goto error;
			}

			if ((pcb & T1_MORE_BLOCKS) == 0)
				goto done;

			slen = t1_build(t1, sdata, dad, T1_R_BLOCK, NULL, NULL);
			break;

		case T1_S_BLOCK:
			if (T1_S_IS_RESPONSE(pcb) && t1->state == RESYNCH) {
				/* ISO 7816-3 Rule 6.2 */
				DEBUG_COMM("S-Block answer received");
				/* ISO 7816-3 Rule 6.3 */
				t1->state = SENDING;
				sent_length = 0;
				last_send = 0;
				resyncs = 3;
				retries = t1->retries;
				ct_buf_init(&rbuf, rcv_buf, rcv_len);
				slen = t1_build(t1, sdata, dad, T1_I_BLOCK,
						&sbuf, &last_send);
				continue;
			}

			if (T1_S_IS_RESPONSE(pcb))
			{
				/* ISO 7816-3 Rule 7.4.2 */
				if (retries == 0)
					goto resync;

				/* ISO 7816-3 Rule 7.2 */
				if (T1_R_BLOCK == t1_block_type(t1->previous_block[PCB]))
				{
					DEBUG_COMM("Rule 7.2");
					slen = t1_rebuild(t1, sdata);
					continue;
				}

				DEBUG_CRITICAL("wrong response S-BLOCK received");
				slen = t1_build(t1, sdata,
						dad, T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
				continue;
			}

			ct_buf_init(&tbuf, sblk, sizeof(sblk));

			DEBUG_COMM("S-Block request received");
			switch (T1_S_TYPE(pcb)) {
			case T1_S_RESYNC:
				if (sdata[LEN] != 0)
				{
					DEBUG_COMM2("Wrong length: %d", sdata[LEN]);
					slen = t1_build(t1, sdata, dad,
						T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
					continue;
				}

				DEBUG_COMM("Resync requested");
				/* the card is not allowed to send a resync. */
				goto resync;

			case T1_S_ABORT:
				if (sdata[LEN] != 0)
				{
					DEBUG_COMM2("Wrong length: %d", sdata[LEN]);
					slen = t1_build(t1, sdata, dad,
						T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
					continue;
				}

				/* ISO 7816-3 Rule 9 */
				DEBUG_CRITICAL("abort requested");
				break;

			case T1_S_IFS:
				if (sdata[LEN] != 1)
				{
					DEBUG_COMM2("Wrong length: %d", sdata[LEN]);
					slen = t1_build(t1, sdata, dad,
						T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
					continue;
				}

				DEBUG_CRITICAL2("CT sent S-block with ifs=%u", sdata[DATA]);
				if (sdata[DATA] == 0)
					goto resync;
				t1->ifsc = sdata[DATA];
				ct_buf_putc(&tbuf, sdata[DATA]);
				break;

			case T1_S_WTX:
				if (sdata[LEN] != 1)
				{
					DEBUG_COMM2("Wrong length: %d", sdata[LEN]);
					slen = t1_build(t1, sdata, dad,
						T1_R_BLOCK | T1_OTHER_ERROR,
						NULL, NULL);
					continue;
				}

				DEBUG_COMM2("CT sent S-block with wtx=%u", sdata[DATA]);
				t1->wtx = sdata[DATA];
				ct_buf_putc(&tbuf, sdata[DATA]);
				break;

			default:
				DEBUG_CRITICAL2("T=1: Unknown S block type 0x%02x", T1_S_TYPE(pcb));
				goto resync;
			}

			slen = t1_build(t1, sdata, dad,
				T1_S_BLOCK | T1_S_RESPONSE | T1_S_TYPE(pcb),
				&tbuf, NULL);
		}

		/* Everything went just splendid */
		retries = t1->retries;
		continue;

resync:
		/* the number or resyncs is limited, too */
		/* ISO 7816-3 Rule 6.4 */
		if (resyncs == 0)
			goto error;

		/* ISO 7816-3 Rule 6 */
		resyncs--;
		t1->ns = 0;
		t1->nr = 0;
		slen = t1_build(t1, sdata, dad, T1_S_BLOCK | T1_S_RESYNC, NULL,
				NULL);
		t1->state = RESYNCH;
		t1->more = FALSE;
		retries = 1;
		continue;
	}

done:
	return ct_buf_avail(&rbuf);

error:
	t1->state = DEAD;
	return -1;
}