diff --git a/ccid/Makefile b/ccid/Makefile
index 95fc782..19217d6 100644
--- a/ccid/Makefile
+++ b/ccid/Makefile
@@ -22,8 +22,8 @@ TARGETS = ccid
all: $(TARGETS)
-ccid: ccid.h ccid.c usbstring.c usbstring.h
- $(CC) $(LIBPCSCLITE_CFLAGS) $(PTHREAD_CFLAGS) $(CFLAGS) usbstring.c ccid.c -o $@
+ccid: ccid.h ccid.c usbstring.c usbstring.h usb.c
+ $(CC) $(LIBPCSCLITE_CFLAGS) $(PTHREAD_CFLAGS) $(CFLAGS) usbstring.c ccid.c usb.c -o $@
install: $(TARGETS) installdirs
diff --git a/ccid/ccid.c b/ccid/ccid.c
index 2484717..87179ae 100644
--- a/ccid/ccid.c
+++ b/ccid/ccid.c
@@ -1,1791 +1,905 @@
-/*
- * Copyright (C) 2009 Frank Morgner
- *
- * This file is part of ccid.
- *
- * ccid is free software: you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version.
- *
- * ccid is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ccid. If not, see .
- */
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+#include
+#include
+#include
#include
-
-#include
-#include
-#include
-#include
-
+#include
#include
-#include
-#include
-#include
-//#include
-
-#ifdef AIO
-/* this aio code works with libaio-0.3.106 */
-#include
-#endif
-
-#include "usbstring.h"
#include "ccid.h"
-#define DRIVER_VENDOR_NUM 0x0D46 /* KOBIL Systems */
-#define DRIVER_ISO_PRODUCT_NUM 0x3010 /* KOBIL Class 3 Reader */
-#define DRIVER_PRODUCT_NUM 0x3010 /* KOBIL Class 3 Reader */
-static int vendorid = DRIVER_VENDOR_NUM;
-static int productid = DRIVER_PRODUCT_NUM;
-static int verbose = 0;
-static int debug = 0;
-static int dohid = 0;
-static int doint = 0;
+SCARDCONTEXT hcontext = 0;
+SCARDHANDLE hcard = 0;
+SCARD_READERSTATE rstate;
+DWORD dwActiveProtocol;
+char reader_name[MAX_READERNAME];
+int reader_num;
-
-/* NOTE: these IDs don't imply endpoint numbering; host side drivers
- * should use endpoint descriptors, or perhaps bcdDevice, to configure
- * such things. Other product IDs could have different policies.
- */
-
-/*-------------------------------------------------------------------------*/
-
-/* these descriptors are modified based on what controller we find */
-
-#define STRINGID_MFGR 1
-#define STRINGID_PRODUCT 2
-#define STRINGID_SERIAL 3
-#define STRINGID_CONFIG 4
-#define STRINGID_INTERFACE 5
-#define STRINGID_HID_INTERFACE 6
-
-static struct usb_device_descriptor
-device_desc = {
- .bLength = sizeof device_desc,
-
- //.bcdUSB = __constant_cpu_to_le16 (0x0110),
- .bDescriptorType = USB_DT_DEVICE,
-
- .bcdUSB = __constant_cpu_to_le16 (0x0200),
- .bDeviceClass = USB_CLASS_VENDOR_SPEC,
- .bDeviceSubClass = 0,
- .bDeviceProtocol = 0,
- // .bMaxPacketSize0 ... set by gadgetfs
- .idVendor = __constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
- .idProduct = __constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),
- .iManufacturer = STRINGID_MFGR,
- .iProduct = STRINGID_PRODUCT,
- .iSerialNumber = STRINGID_SERIAL,
- .bNumConfigurations = 1,
-};
-
-#define MAX_USB_POWER 1
-
-#define CONFIG_VALUE 3
-
-static struct usb_config_descriptor
-config = {
- .bLength = sizeof config,
- .bDescriptorType = USB_DT_CONFIG,
-
- /* must compute wTotalLength ... */
- .bNumInterfaces = 1,
- .bConfigurationValue = CONFIG_VALUE,
- .iConfiguration = STRINGID_CONFIG,
- .bmAttributes = USB_CONFIG_ATT_ONE
- | USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = (MAX_USB_POWER + 1) / 2,
-};
-
-static struct usb_interface_descriptor
-source_sink_intf = {
- .bLength = sizeof source_sink_intf,
- .bDescriptorType = USB_DT_INTERFACE,
-
- .bInterfaceClass = USB_CLASS_CSCID,
- .iInterface = STRINGID_INTERFACE,
-};
-static struct hid_class_descriptor
-hid_desc = {
- .bLength = sizeof hid_desc,
- .bDescriptorType = 0x21,
- .bcdHID = __constant_cpu_to_le16(0x101),
- .bCountryCode = 0,
- .bNumDescriptors = 0,
-};
-
-/* Full speed configurations are used for full-speed only devices as
- * well as dual-speed ones (the only kind with high speed support).
- */
-
-static struct usb_endpoint_descriptor
-fs_source_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- /* NOTE some controllers may need FS bulk max packet size
- * to be smaller. it would be a chip-specific option.
- */
- .wMaxPacketSize = __constant_cpu_to_le16 (64),
-};
-
-static struct usb_endpoint_descriptor
-fs_sink_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = __constant_cpu_to_le16 (64),
-};
-
-/* some devices can handle other result packet sizes */
-/*#define STATUS_MAXPACKET 16*/
-/*#define LOG2_STATUS_POLL_MSEC 5*/
-#define STATUS_MAXPACKET 8
-#define LOG2_STATUS_POLL_MSEC 3
-
-static struct usb_endpoint_descriptor
-fs_status_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_MAXPACKET),
- .bInterval = (1 << LOG2_STATUS_POLL_MSEC),
-};
-
-static struct usb_endpoint_descriptor
-fs_hid_status_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_MAXPACKET),
- .bInterval = (1 << LOG2_STATUS_POLL_MSEC),
-};
-
-static const struct usb_endpoint_descriptor *fs_eps [3] = {
- &fs_source_desc,
- &fs_sink_desc,
- &fs_status_desc,
-};
-
-
-/* High speed configurations are used only in addition to a full-speed
- * ones ... since all high speed devices support full speed configs.
- * Of course, not all hardware supports high speed configurations.
- */
-
-static struct usb_endpoint_descriptor
-hs_source_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = __constant_cpu_to_le16 (512),
-};
-
-static struct usb_endpoint_descriptor
-hs_sink_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = __constant_cpu_to_le16 (512),
- .bInterval = 1,
-};
-
-static struct usb_endpoint_descriptor
-hs_status_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_MAXPACKET),
- .bInterval = LOG2_STATUS_POLL_MSEC + 3,
-};
-
-static struct usb_endpoint_descriptor
-hs_hid_status_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_MAXPACKET),
- .bInterval = LOG2_STATUS_POLL_MSEC + 3,
-};
-
-static const struct usb_endpoint_descriptor *hs_eps [] = {
- &hs_source_desc,
- &hs_sink_desc,
- &hs_status_desc,
-};
-
-
-/*-------------------------------------------------------------------------*/
-
-/* 56 is the maximum for the KOBIL Class 3 Reader */
-static char serial [57];
-
-static struct usb_string stringtab [] = {
- { STRINGID_MFGR, "morgner@informatik.hu-berlin.de", },
- { STRINGID_PRODUCT, "CCID to PCSC Gadget", },
- { STRINGID_SERIAL, serial, },
- { STRINGID_CONFIG, "The Configuration", },
- { STRINGID_INTERFACE, "CCID to PCSC", },
- { STRINGID_HID_INTERFACE, "blakeks", },
-};
-
-static struct usb_gadget_strings strings = {
- .language = 0x0409, /* "en-us" */
- .strings = stringtab,
-};
-
-/*-------------------------------------------------------------------------*/
-
-/* kernel drivers could autoconfigure like this too ... if
- * they were willing to waste the relevant code/data space.
- */
-
-static int HIGHSPEED;
-static char *DEVNAME;
-static char *EP_IN_NAME, *EP_OUT_NAME, *EP_STATUS_NAME, *EP_HID_STATUS_NAME;
-
-/* gadgetfs currently has no chunking (or O_DIRECT/zerocopy) support
- * to turn big requests into lots of smaller ones; so this is "small".
- */
-#define USB_BUFSIZE (7 * 1024)
-
-static enum usb_device_speed current_speed;
-
-static inline int min(unsigned a, unsigned b)
+char* perform_initialization(int num)
{
- return (a < b) ? a : b;
-}
-
-static int autoconfig ()
-{
- struct stat statb;
-
- /* NetChip 2280 PCI device or dummy_hcd, high/full speed */
- if (stat (DEVNAME = "net2280", &statb) == 0 ||
- stat (DEVNAME = "dummy_udc", &statb) == 0) {
- HIGHSPEED = 1;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0100),
-
- fs_source_desc.bEndpointAddress
- = hs_source_desc.bEndpointAddress
- = USB_DIR_IN | 7;
- EP_IN_NAME = "ep-a";
- fs_sink_desc.bEndpointAddress = hs_sink_desc.bEndpointAddress
- = USB_DIR_OUT | 3;
- EP_OUT_NAME = "ep-b";
-
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress
- = hs_status_desc.bEndpointAddress
- = USB_DIR_IN | 11;
- EP_STATUS_NAME = "ep-f";
-
- if (dohid) {
- // TODO EP_HID_STATUS_NAME?
- EP_HID_STATUS_NAME = "ep-e";
- fs_hid_status_desc.bEndpointAddress
- = hs_hid_status_desc.bEndpointAddress
- = USB_DIR_IN | 10;
- }
-
- /* Intel PXA 2xx processor, full speed only */
- } else if (stat (DEVNAME = "pxa2xx_udc", &statb) == 0) {
- HIGHSPEED = 0;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0101),
-
- fs_source_desc.bEndpointAddress = USB_DIR_IN | 6;
- EP_IN_NAME = "ep6in-bulk";
- fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 7;
- EP_OUT_NAME = "ep7out-bulk";
-
- /* using bulk for this since the pxa interrupt endpoints
- * always use the no-toggle scheme (discouraged).
- */
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress = USB_DIR_IN | 11;
- EP_STATUS_NAME = "ep11in-bulk";
-
- if (dohid) {
- EP_HID_STATUS_NAME = "ep12in-bulk";
- fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 12;
- }
-#if 0
- /* AMD au1x00 processor, full speed only */
- } else if (stat (DEVNAME = "au1x00_udc", &statb) == 0) {
- HIGHSPEED = 0;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0102),
-
- fs_source_desc.bEndpointAddress = USB_DIR_IN | 2;
- EP_IN_NAME = "ep2in";
- fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 4;
- EP_OUT_NAME = "ep4out";
-
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
- EP_STATUS_NAME = "ep3in";
-
- /* Intel SA-1100 processor, full speed only */
- } else if (stat (DEVNAME = "sa1100", &statb) == 0) {
- HIGHSPEED = 0;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0103),
-
- fs_source_desc.bEndpointAddress = USB_DIR_IN | 2;
- EP_IN_NAME = "ep2in-bulk";
- fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 1;
- EP_OUT_NAME = "ep1out-bulk";
-
- source_sink_intf.bNumEndpoints = 2;
- ccid_desc.dwFeatures &= ~0x100000; // USB Wake up signaling not supported
- EP_STATUS_NAME = 0;
-#endif
-
- /* Toshiba TC86c001 PCI device, full speed only */
- } else if (stat (DEVNAME = "goku_udc", &statb) == 0) {
- HIGHSPEED = 0;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0104),
-
- fs_source_desc.bEndpointAddress = USB_DIR_IN | 2;
- EP_IN_NAME = "ep2-bulk";
- fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 1;
- EP_OUT_NAME = "ep1-bulk";
-
- if (dohid) {
- source_sink_intf.bNumEndpoints = 2;
- // USB Wake up signaling not supported
- ccid_desc.dwFeatures &= ~__constant_cpu_to_le32(0x100000);
-
- EP_HID_STATUS_NAME = "ep3-bulk";
- fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 3;
- } else {
- source_sink_intf.bNumEndpoints = 3;
-
- EP_STATUS_NAME = "ep3-bulk";
- fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
- }
-
- /* Samsung S3C24xx series, full speed only */
- } else if (stat (DEVNAME = "s3c2410_udc", &statb) == 0) {
- HIGHSPEED = 0;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0110),
-
- fs_source_desc.bEndpointAddress = USB_DIR_IN | 2;
- EP_IN_NAME = "ep2-bulk";
- fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 1;
- EP_OUT_NAME = "ep1-bulk";
-
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
- EP_STATUS_NAME = "ep3-bulk";
-
- if (dohid) {
- EP_HID_STATUS_NAME = "ep4-bulk";
- fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 4;
- }
-
- /* Renesas SH77xx processors, full speed only */
- } else if (stat (DEVNAME = "sh_udc", &statb) == 0) {
- HIGHSPEED = 0;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0105),
-
- fs_source_desc.bEndpointAddress = USB_DIR_IN | 2;
- EP_IN_NAME = "ep2in-bulk";
- fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 1;
- EP_OUT_NAME = "ep1out-bulk";
-
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
- EP_STATUS_NAME = "ep3in-bulk";
-
- if (dohid) {
- EP_HID_STATUS_NAME = "ep4in-bulk";
- fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 4;
- }
-
- /* OMAP 1610 and newer devices, full speed only, fifo mode 0 or 3 */
- } else if (stat (DEVNAME = "omap_udc", &statb) == 0) {
- HIGHSPEED = 0;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0106),
-
- fs_source_desc.bEndpointAddress = USB_DIR_IN | 1;
- EP_IN_NAME = "ep1in-bulk";
- fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 2;
- EP_OUT_NAME = "ep2out-bulk";
-
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
- EP_STATUS_NAME = "ep3in-int";
-
- if (dohid) {
- EP_HID_STATUS_NAME = "ep4in-bulk";
- fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 4;
- }
-
- /* Something based on Mentor USB Highspeed Dual-Role Controller */
- } else if (stat (DEVNAME = "musb_hdrc", &statb) == 0) {
- HIGHSPEED = 1;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0107),
-
- fs_source_desc.bEndpointAddress
- = hs_source_desc.bEndpointAddress
- = USB_DIR_IN | 1;
- EP_IN_NAME = "ep1in";
- fs_sink_desc.bEndpointAddress = hs_sink_desc.bEndpointAddress
- = USB_DIR_OUT | 1;
- EP_OUT_NAME = "ep1out";
-
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress
- = hs_status_desc.bEndpointAddress
- = USB_DIR_IN | 3;
- EP_STATUS_NAME = "ep3";
-
- /* Atmel AT91 processors, full speed only */
- } else if (stat (DEVNAME = "at91_udc", &statb) == 0) {
- HIGHSPEED = 0;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0106),
-
- fs_source_desc.bEndpointAddress = USB_DIR_IN | 1;
- EP_IN_NAME = "ep1";
- fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 2;
- EP_OUT_NAME = "ep2";
-
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
- EP_STATUS_NAME = "ep3-int";
-
- if (dohid) {
- EP_HID_STATUS_NAME = "ep4";
- fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 4;
- }
-
- /* Sharp LH740x processors, full speed only */
- } else if (stat (DEVNAME = "lh740x_udc", &statb) == 0) {
- HIGHSPEED = 0;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0106),
-
- fs_source_desc.bEndpointAddress = USB_DIR_IN | 1;
- EP_IN_NAME = "ep1in-bulk";
- fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 2;
- EP_OUT_NAME = "ep2out-bulk";
-
- if (dohid) {
- source_sink_intf.bNumEndpoints = 2;
- // USB Wake up signaling not supported
- ccid_desc.dwFeatures &= ~__constant_cpu_to_le32(0x100000);
-
- fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 3;
- EP_HID_STATUS_NAME = "ep3in-int";
- } else {
- source_sink_intf.bNumEndpoints = 3;
-
- fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
- EP_STATUS_NAME = "ep3in-int";
- }
-
- /* Atmel AT32AP700x processors, high/full speed */
- } else if (stat (DEVNAME = "atmel_usba_udc", &statb) == 0) {
- HIGHSPEED = 1;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0108);
-
- fs_source_desc.bEndpointAddress
- = hs_source_desc.bEndpointAddress
- = USB_DIR_IN | 1;
- EP_IN_NAME = "ep1in-bulk";
- fs_sink_desc.bEndpointAddress
- = hs_sink_desc.bEndpointAddress
- = USB_DIR_OUT | 2;
- EP_OUT_NAME = "ep2out-bulk";
-
- if (dohid) {
- source_sink_intf.bNumEndpoints = 2;
- // USB Wake up signaling not supported
- ccid_desc.dwFeatures &= ~__constant_cpu_to_le32(0x100000);
-
- fs_hid_status_desc.bEndpointAddress
- = hs_hid_status_desc.bEndpointAddress
- = USB_DIR_IN | 3;
- EP_HID_STATUS_NAME = "ep3in-int";
- } else {
- source_sink_intf.bNumEndpoints = 3;
-
- fs_status_desc.bEndpointAddress
- = hs_status_desc.bEndpointAddress
- = USB_DIR_IN | 3;
- EP_STATUS_NAME = "ep3in-int";
- }
-
- } else {
- DEVNAME = 0;
- return -ENODEV;
- }
- if (!doint) {
- source_sink_intf.bNumEndpoints = 2;
- // USB Wake up signaling not supported
- ccid_desc.dwFeatures &= ~__constant_cpu_to_le32(0x100000);
- }
- return 0;
-}
-
-#ifdef AIO
-
-static int iso;
-static int interval;
-static unsigned iosize;
-static unsigned bufsize = USB_BUFSIZE;
-
-/* This is almost the only place where usb needs to know whether we're
- * driving an isochronous stream or a bulk one.
- */
-static int iso_autoconfig ()
-{
- struct stat statb;
-
- /* ISO endpoints "must not be part of a default interface setting".
- * Never do it like this in "real" code! This uses the default
- * setting (alt 0) because it's the only one pxa supports.
- *
- * This code doesn't adjust the sample rate based on feedback.
- */
- device_desc.idProduct = __constant_cpu_to_le16(DRIVER_ISO_PRODUCT_NUM);
-
- /* NetChip 2280 PCI device or dummy_hcd, high/full speed */
- if (stat (DEVNAME = "net2280", &statb) == 0 ||
- stat (DEVNAME = "dummy_udc", &statb) == 0) {
- unsigned bInterval, wMaxPacketSize;
-
- HIGHSPEED = 1;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0100);
-
- /* this code won't use two or four uframe periods */
- if (bufsize > 1024) {
- interval = 0;
- bInterval = 1;
- /* "modprobe net2280 fifo_mode=1" may be needed */
- if (bufsize > (2 * 1024)) {
- wMaxPacketSize = min ((bufsize + 2)/3, 1024);
- bufsize = min (3 * wMaxPacketSize, bufsize);
- wMaxPacketSize |= 2 << 11;
- } else {
- wMaxPacketSize = min ((bufsize + 1)/2, 1024);
- wMaxPacketSize |= 1 << 11;
- }
- } else {
- bInterval = interval + 4;
- wMaxPacketSize = bufsize;
- }
-
- fs_source_desc.bEndpointAddress
- = hs_source_desc.bEndpointAddress
- = USB_DIR_IN | 7;
- fs_source_desc.bmAttributes
- = hs_source_desc.bmAttributes
- = USB_ENDPOINT_XFER_ISOC;
- fs_source_desc.wMaxPacketSize = min (bufsize, 1023);
- hs_source_desc.wMaxPacketSize = wMaxPacketSize;
- fs_source_desc.bInterval = interval + 1;
- hs_source_desc.bInterval = bInterval;
- EP_IN_NAME = "ep-a";
-
- fs_sink_desc.bEndpointAddress
- = hs_sink_desc.bEndpointAddress
- = USB_DIR_OUT | 3;
- fs_sink_desc.bmAttributes
- = hs_sink_desc.bmAttributes
- = USB_ENDPOINT_XFER_ISOC;
- fs_sink_desc.wMaxPacketSize = min (bufsize, 1023);
- hs_sink_desc.wMaxPacketSize = wMaxPacketSize;
- fs_sink_desc.bInterval = interval + 1;
- hs_sink_desc.bInterval = bInterval;
- EP_OUT_NAME = "ep-b";
-
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress
- = hs_status_desc.bEndpointAddress
- = USB_DIR_IN | 11;
- EP_STATUS_NAME = "ep-f";
-
- /* Intel PXA 2xx processor, full speed only */
- } else if (stat (DEVNAME = "pxa2xx_udc", &statb) == 0) {
- HIGHSPEED = 0;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0101),
-
- bufsize = min (bufsize, 256);
-
- fs_source_desc.bEndpointAddress = USB_DIR_IN | 3;
- fs_source_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC;
- fs_source_desc.wMaxPacketSize = bufsize;
- fs_source_desc.bInterval = interval;
- EP_IN_NAME = "ep3in-iso";
-
- fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 4;
- fs_sink_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC;
- fs_sink_desc.wMaxPacketSize = bufsize;
- fs_sink_desc.bInterval = interval;
- EP_OUT_NAME = "ep4out-iso";
-
- /* using bulk for this since the pxa interrupt endpoints
- * always use the no-toggle scheme (discouraged).
- */
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress = USB_DIR_IN | 11;
- EP_STATUS_NAME = "ep11in-bulk";
-
- /* OMAP 1610 and newer devices, full speed only, fifo mode 3 */
- } else if (stat (DEVNAME = "omap_udc", &statb) == 0) {
- HIGHSPEED = 0;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0102),
-
- fs_source_desc.bEndpointAddress = USB_DIR_IN | 7;
- fs_source_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC;
- fs_source_desc.wMaxPacketSize = min (bufsize, 256);
- fs_source_desc.bInterval = interval;
- EP_IN_NAME = "ep7in-iso";
-
- fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 8;
- fs_sink_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC;
- fs_sink_desc.wMaxPacketSize = min (bufsize, 256);
- fs_sink_desc.bInterval = interval;
- EP_OUT_NAME = "ep8out-iso";
-
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress = USB_DIR_IN | 9;
- EP_STATUS_NAME = "ep9in-int";
-
- /* Something based on Mentor USB Highspeed Dual-Role Controller;
- * assumes a core that doesn't include high bandwidth support.
- */
- } else if (stat (DEVNAME = "musb_hdrc", &statb) == 0) {
- unsigned bInterval, wMaxPacketSize;
-
- HIGHSPEED = 1;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0103);
-
- bInterval = interval + 4;
- wMaxPacketSize = bufsize;
-
- fs_source_desc.bEndpointAddress
- = hs_source_desc.bEndpointAddress
- = USB_DIR_IN | 1;
- fs_source_desc.bmAttributes
- = hs_source_desc.bmAttributes
- = USB_ENDPOINT_XFER_ISOC;
- fs_source_desc.wMaxPacketSize = min (bufsize, 1023);
- hs_source_desc.wMaxPacketSize = wMaxPacketSize;
- fs_source_desc.bInterval = interval + 1;
- hs_source_desc.bInterval = bInterval;
- EP_IN_NAME = "ep1in";
-
- fs_sink_desc.bEndpointAddress
- = hs_sink_desc.bEndpointAddress
- = USB_DIR_OUT | 1;
- fs_sink_desc.bmAttributes
- = hs_sink_desc.bmAttributes
- = USB_ENDPOINT_XFER_ISOC;
- fs_sink_desc.wMaxPacketSize = min (bufsize, 1023);
- hs_sink_desc.wMaxPacketSize = wMaxPacketSize;
- fs_sink_desc.bInterval = interval + 1;
- hs_sink_desc.bInterval = bInterval;
- EP_OUT_NAME = "ep1out";
-
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress
- = hs_status_desc.bEndpointAddress
- = USB_DIR_IN | 11;
- EP_STATUS_NAME = "ep3";
-
- /* Atmel AT91 processors, full speed only */
- } else if (stat (DEVNAME = "at91_udc", &statb) == 0) {
- HIGHSPEED = 0;
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0104),
-
- fs_source_desc.bEndpointAddress = USB_DIR_IN | 4;
- fs_source_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC;
- fs_source_desc.wMaxPacketSize = min (bufsize, 256);
- fs_source_desc.bInterval = interval;
- EP_IN_NAME = "ep4";
-
- fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 2;
- fs_sink_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC;
- fs_sink_desc.wMaxPacketSize = min (bufsize, 256);
- fs_sink_desc.bInterval = interval;
- EP_OUT_NAME = "ep5";
-
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
- EP_STATUS_NAME = "ep3-int";
-
- /* Atmel AT32AP700x processors, high/full speed */
- } else if (stat (DEVNAME = "atmel_usba_udc", &statb) == 0){
- HIGHSPEED = 1;
-
- device_desc.bcdDevice = __constant_cpu_to_le16 (0x0105);
-
- fs_source_desc.bEndpointAddress
- = hs_source_desc.bEndpointAddress
- = USB_DIR_IN | 5;
- fs_source_desc.bmAttributes
- = hs_source_desc.bmAttributes
- = USB_ENDPOINT_XFER_ISOC;
- fs_source_desc.wMaxPacketSize
- = hs_source_desc.wMaxPacketSize
- = __cpu_to_le16(min (bufsize, 1024));
- fs_source_desc.bInterval
- = hs_source_desc.bInterval
- = interval;
- EP_IN_NAME = "ep5in-iso";
-
- fs_sink_desc.bEndpointAddress
- = hs_sink_desc.bEndpointAddress
- = USB_DIR_OUT | 6;
- fs_sink_desc.bmAttributes
- = hs_sink_desc.bmAttributes
- = USB_ENDPOINT_XFER_ISOC;
- fs_sink_desc.wMaxPacketSize
- = hs_sink_desc.wMaxPacketSize
- = __cpu_to_le16(min (bufsize, 1024));
- fs_sink_desc.bInterval
- = hs_sink_desc.bInterval
- = interval;
- EP_OUT_NAME = "ep6out-iso";
-
- source_sink_intf.bNumEndpoints = 3;
- fs_status_desc.bEndpointAddress
- = hs_status_desc.bEndpointAddress
- = USB_DIR_IN | 3;
- EP_STATUS_NAME = "ep3in-int";
-
- } else {
- DEVNAME = 0;
- return -ENODEV;
- }
- if (verbose) {
- fprintf (stderr, "iso fs wMaxPacket %04x bInterval %02x\n",
- __le16_to_cpu(fs_sink_desc.wMaxPacketSize),
- fs_sink_desc.bInterval);
- if (HIGHSPEED)
- fprintf (stderr,
- "iso hs wMaxPacket %04x bInterval %02x\n",
- __le16_to_cpu(hs_sink_desc.wMaxPacketSize),
- hs_sink_desc.bInterval);
- }
- return 0;
-}
-
-#else
-#define iso 0
-#endif /* AIO */
-
-/*-------------------------------------------------------------------------*/
-
-/* full duplex data, with at least three threads: ep0, sink, and source */
-
-static pthread_t ep0;
-
-static pthread_t ccid_thread;
-static pthread_t hidthread;
-static int source_fd = -1;
-static int sink_fd = -1;
-static int status_fd = -1;
-static int hidstatus_fd = -1;
-
-// FIXME no result i/o yet
-
-static void close_fd (void *fd_ptr)
-{
- int result, fd;
-
- fd = *(int *)fd_ptr;
- *(int *)fd_ptr = -1;
-
- /* test the FIFO ioctls (non-ep0 code paths) */
- if (pthread_self () != ep0) {
- result = ioctl (fd, GADGETFS_FIFO_STATUS);
- if (result < 0) {
- /* ENODEV reported after disconnect */
- if (errno != ENODEV && errno != EOPNOTSUPP)
- perror ("get fifo result");
- } else {
- fprintf (stderr, "fd %d, unclaimed = %d\n",
- fd, result);
- if (result) {
- result = ioctl (fd, GADGETFS_FIFO_FLUSH);
- if (result < 0)
- perror ("fifo flush");
- }
- }
- }
-
- if (close (fd) < 0)
- perror ("close fd");
- else
- fprintf (stderr, "closed fd\n");
-}
-
-
-/* you should be able to open and configure endpoints
- * whether or not the host is connected
- */
-static int
-ep_config (char *name, const char *label,
- struct usb_endpoint_descriptor *fs,
- struct usb_endpoint_descriptor *hs
-)
-{
- int fd, result;
- char buf [USB_BUFSIZE];
-
- /* open and initialize with endpoint descriptor(s) */
- fd = open (name, O_RDWR);
- if (fd < 0) {
- result = -errno;
- fprintf (stderr, "%s open %s error %d (%s)\n",
- label, name, errno, strerror (errno));
- return result;
- }
-
- /* one (fs or ls) or two (fs + hs) sets of config descriptors */
- *(__u32 *)buf = 1; /* tag for this format */
- memcpy (buf + 4, fs, USB_DT_ENDPOINT_SIZE);
- if (HIGHSPEED)
- memcpy (buf + 4 + USB_DT_ENDPOINT_SIZE,
- hs, USB_DT_ENDPOINT_SIZE);
- result = write (fd, buf, 4 + USB_DT_ENDPOINT_SIZE
- + (HIGHSPEED ? USB_DT_ENDPOINT_SIZE : 0));
- if (result < 0) {
- result = -errno;
- fprintf (stderr, "%s config %s error %d (%s)\n",
- label, name, errno, strerror (errno));
- close (fd);
- return result;
- } else if (debug) {
- unsigned long id;
-
- id = pthread_self ();
- fprintf (stderr, "%s (%ld): fd %d\n", label, id, fd);
- }
- return fd;
-}
-
-#define source_open(name) \
- ep_config(name,__FUNCTION__, &fs_source_desc, &hs_source_desc)
-#define sink_open(name) \
- ep_config(name,__FUNCTION__, &fs_sink_desc, &hs_sink_desc)
-#define status_open(name) \
- ep_config(name,__FUNCTION__, &fs_status_desc, &hs_status_desc)
-#define hidstatus_open(name) \
- ep_config(name,__FUNCTION__, &fs_hid_status_desc, &hs_hid_status_desc)
-
-/* ccid thread, forwards ccid requests to pcsc and returns results */
-static void *ccid (void *param)
-{
-
- //int tmp;
- pthread_t interrupt_thread;
-
- void *interrupt (void *param)
- {
- status_fd = status_open (((char **) param)[0]);
- if (source_fd < 0) {
- /* an error concerning status endpoint is not fatal for in/out bulk*/
- /* transfer */
- if (debug)
- perror("status_fd");
- pthread_exit(0);
- }
- pthread_cleanup_push (close_fd, &status_fd);
-
- int result;
- RDR_to_PC_NotifySlotChange_t slotchange;
- do {
-
- /* original LinuxThreads cancelation didn't work right */
- /* so test for it explicitly. */
- pthread_testcancel ();
-
- slotchange = get_RDR_to_PC_NotifySlotChange();
- if (slotchange.bmSlotICCState) {
- /* don't bother host, when nothing changed */
- if (debug)
- fprintf(stderr, "interrupt loop: writing RDR_to_PC_NotifySlotChange... ");
- result = write(status_fd, &slotchange, sizeof slotchange);
- if (debug)
- fprintf(stderr, "done.\n");
- }
- sleep(10);
- } while (result >= 0);
-
- if (errno != ESHUTDOWN || result < 0) {
- perror ("interrupt loop aborted");
- pthread_cancel(ccid_thread);
- pthread_cancel(ep0);
- }
-
- pthread_cleanup_pop (1);
-
- return 0;
+ char *readers, *str;
+ DWORD size;
+ LONG r;
+
+ reader_num = num;
+ r = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hcontext);
+ if (r != SCARD_S_SUCCESS) {
+ fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r));
+ SCardReleaseContext(hcontext);
+ return NULL;
+ }
+ r = SCardListReaders(hcontext, NULL, NULL, &size);
+ if (size == 0)
+ r = SCARD_E_UNKNOWN_READER;
+ if (r != SCARD_S_SUCCESS) {
+ fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r));
+ SCardReleaseContext(hcontext);
+ return NULL;
}
- void close_interrupt ()
- {
- if (doint) {
- pthread_cancel(interrupt_thread);
- if (pthread_join (interrupt_thread, 0) != 0)
- perror ("can't join interrupt thread");
- fprintf (stderr, "cancled interrupt loop\n");
+ /* get all readers */
+ readers = (char *) malloc(size);
+ if (readers == NULL) {
+ fprintf(stderr, "pc/sc error: %s\n",
+ pcsc_stringify_error(SCARD_E_NO_MEMORY));
+ SCardReleaseContext(hcontext);
+ return NULL;
+ }
+ r = SCardListReaders(hcontext, NULL, readers, &size);
+ if (r != SCARD_S_SUCCESS) {
+ free(readers);
+ fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r));
+ SCardReleaseContext(hcontext);
+ return NULL;
+ }
+
+ /* name of reader number num */
+ str = readers;
+ for (size = 0; size < num; size++) {
+ /* go to the next name */
+ str += strlen(str) + 1;
+
+ /* no more readers available? */
+ if (strlen(str) == 0) {
+ free(readers);
+ fprintf(stderr, "pc/sc error: %s\n",
+ pcsc_stringify_error(SCARD_E_UNKNOWN_READER));
+ SCardReleaseContext(hcontext);
+ return NULL;
+ }
+ }
+ strncpy(reader_name, str, MAX_READERNAME);
+ free(readers);
+
+ rstate.dwCurrentState = SCARD_STATE_UNAWARE;
+ rstate.dwEventState = SCARD_STATE_UNAWARE;
+ rstate.szReader = reader_name;
+
+ return reader_name;
+}
+
+
+int perform_shutdown()
+{
+ SCardDisconnect(hcard, SCARD_UNPOWER_CARD);
+ hcard = 0;
+ rstate.dwCurrentState = SCARD_STATE_UNAWARE;
+ rstate.dwEventState = SCARD_STATE_UNAWARE;
+ return SCardReleaseContext(hcontext);
+}
+
+__u8 get_bError(LONG pcsc_result)
+{
+ switch (pcsc_result) {
+ case SCARD_S_SUCCESS : /**< No error was encountered. */
+ // Command not supported
+ return 0;
+ case SCARD_E_CANCELLED : /**< The action was cancelled by an SCardCancel request. */
+ fprintf(stderr, "CMD_ABORTED\n");
+ return 0xFF;
+ case SCARD_E_INVALID_HANDLE : /**< The supplied handle was invalid. */
+ case SCARD_E_NO_SMARTCARD : /**< The operation requires a Smart Card, but no Smart Card is currently in the device. */
+ case SCARD_E_UNKNOWN_CARD : /**< The specified smart card name is not recognized. */
+ case SCARD_E_NOT_READY : /**< The reader or smart card is not ready to accept commands. */
+ case SCARD_W_UNRESPONSIVE_CARD : /**< The smart card is not responding to a reset. */
+ case SCARD_W_UNPOWERED_CARD : /**< Power has been removed from the smart card, so that further communication is not possible. */
+ case SCARD_W_REMOVED_CARD : /**< The smart card has been removed, so further communication is not possible. */
+ fprintf(stderr, "ICC_MUTE\n");
+ return 0xFE;
+ case SCARD_E_SHARING_VIOLATION : /**< The smart card cannot be accessed because of other connections outstanding. */
+ fprintf(stderr, "CMD_SLOT_BUSY\n");
+ return 0xE0;
+ case SCARD_E_PROTO_MISMATCH : /**< The requested protocols are incompatible with the protocol currently in use with the smart card. */
+ fprintf(stderr, "ICC_PROTOCOL_NOT_SUPPORTED\n");
+ return 0xF6;
+ // case SCARD_F_INTERNAL_ERROR : /**< An internal consistency check failed. */
+ // case SCARD_E_INVALID_PARAMETER : /**< One or more of the supplied parameters could not be properly interpreted. */
+ // case SCARD_E_INVALID_TARGET : /**< Registry startup information is missing or invalid. */
+ // case SCARD_E_NO_MEMORY : /**< Not enough memory available to complete this command. */
+ // case SCARD_F_WAITED_TOO_LONG : /**< An internal consistency timer has expired. */
+ // case SCARD_E_INSUFFICIENT_BUFFER : /**< The data buffer to receive returned data is too small for the returned data. */
+ // case SCARD_E_UNKNOWN_READER : /**< The specified reader name is not recognized. */
+ // case SCARD_E_TIMEOUT : /**< The user-specified timeout value has expired. */
+ // case SCARD_E_CANT_DISPOSE : /**< The system could not dispose of the media in the requested manner. */
+ // case SCARD_E_INVALID_VALUE : /**< One or more of the supplied parameters values could not be properly interpreted. */
+ // case SCARD_E_SYSTEM_CANCELLED : /**< The action was cancelled by the system, presumably to log off or shut down. */
+ // case SCARD_F_COMM_ERROR : /**< An internal communications error has been detected. */
+ // case SCARD_F_UNKNOWN_ERROR : /**< An internal error has been detected, but the source is unknown. */
+ // case SCARD_E_INVALID_ATR : /**< An ATR obtained from the registry is not a valid ATR string. */
+ // case SCARD_E_NOT_TRANSACTED : /**< An attempt was made to end a non-existent transaction. */
+ // case SCARD_E_READER_UNAVAILABLE : /**< The specified reader is not currently available for use. */
+ // case SCARD_W_UNSUPPORTED_CARD : /**< The reader cannot communicate with the card, due to ATR string configuration conflicts. */
+ // case SCARD_W_RESET_CARD : /**< The smart card has been reset, so any shared state information is invalid. */
+ // case SCARD_E_PCI_TOO_SMALL : /**< The PCI Receive buffer was too small. */
+ // case SCARD_E_READER_UNSUPPORTED : /**< The reader driver does not meet minimal requirements for support. */
+ // case SCARD_E_DUPLICATE_READER : /**< The reader driver did not produce a unique reader name. */
+ // case SCARD_E_CARD_UNSUPPORTED : /**< The smart card does not meet minimal requirements for support. */
+ // case SCARD_E_NO_SERVICE : /**< The Smart card resource manager is not running. */
+ // case SCARD_E_SERVICE_STOPPED : /**< The Smart card resource manager has shut down. */
+ // case SCARD_E_NO_READERS_AVAILABLE : /**< Cannot find a smart card reader. */
+ default:
+ fprintf(stderr, "HW_ERROR\n");
+ return 0xFB;
+ }
+}
+
+__u8 get_bStatus(LONG pcsc_result)
+{
+ __u8 bStatus = 0;
+
+ if (rstate.dwEventState & SCARD_STATE_PRESENT) {
+ if (rstate.dwEventState & SCARD_STATE_MUTE ||
+ rstate.dwEventState & SCARD_STATE_UNPOWERED) {
+ // inactive
+ fprintf(stderr, "card inactive\n");
+ bStatus = 1;
+ } else {
+ // active
+ /*fprintf(stderr, "card active\n");*/
+ bStatus = 0;
+ }
+ } else {
+ // absent
+ /*fprintf(stderr, "card absent\n");*/
+ bStatus = 2;
+ if (hcard != 0) {
+ pcsc_result = SCardDisconnect(hcard, SCARD_UNPOWER_CARD);
+ hcard = 0;
}
}
- void close_pcsc()
- {
- int result = perform_shutdown();
- if (result != SCARD_S_SUCCESS)
- fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(result));
- else if (verbose)
- fprintf(stderr, "closed connection to %s\n", reader_name);
+ if (pcsc_result != SCARD_S_SUCCESS) {
+ bStatus |= (1<<6);
+ fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(pcsc_result));
}
- char **names = (char **) param;
- char *source_name = names[0];
- char *sink_name = names[1];
- char *status_name = names[2];
- int result;
-
- source_fd = source_open (source_name);
- if (source_fd < 0) {
- if (debug)
- perror("source_fd");
- goto error;
- }
- pthread_cleanup_push (close_fd, &source_fd);
-
- sink_fd = sink_open (sink_name);
- if (sink_fd < 0) {
- if (debug)
- perror("sink_fd");
- goto error;
- }
- pthread_cleanup_push (close_fd, &sink_fd);
-
- char * reader_name = perform_initialization(reader_num);
- if (reader_name == NULL) {
- if (debug)
- perror("perform_initialization");
- goto error;
- }
- pthread_cleanup_push (close_pcsc, NULL);
- if (verbose)
- fprintf (stderr, "connected to %s\n", reader_name);
-
- if (doint) {
- static char * interruptnames[1];
- interruptnames[0] = status_name;
- if (pthread_create (&interrupt_thread, NULL, interrupt, (void *)
- interruptnames) != 0) {
- perror ("can't create interrupt thread");
- goto error;
- }
- } else if (verbose) {
- fprintf (stderr, "interrupt pipe disabled\n");
- }
- pthread_cleanup_push (close_interrupt, NULL);
-
- __u8 *outbuf = NULL;
- pthread_cleanup_push (free, outbuf);
- size_t bufsize = 512;
- __u8 *inbuf = (__u8 *) malloc(bufsize);
- pthread_cleanup_push (free, inbuf);
- if (inbuf == NULL) {
- if (debug)
- perror("malloc");
- goto error;
- }
-
- do {
-
- /* original LinuxThreads cancelation didn't work right
- * so test for it explicitly.
- */
- pthread_testcancel ();
-
- if (debug)
- fprintf(stderr, "reading %lu bytes... ", (long unsigned) bufsize);
- result = read(sink_fd, inbuf, bufsize);
- if (result < 0) break;
- if (debug)
- fprintf(stderr, "got %d, done.\n", result);
- if (!result) break;
-
- result = parse_ccid(inbuf, &outbuf);
- if (result < 0) break;
-
- if (debug)
- fprintf(stderr, "writing %d bytes... ", result);
- result = write(source_fd, outbuf, result);
- if (debug)
- fprintf(stderr, "done.\n");
- } while (result >= 0);
-
- if (errno != ESHUTDOWN || result < 0) {
- perror ("ccid loop aborted");
- pthread_cancel(ep0);
- }
-
- pthread_cleanup_pop (1);
- pthread_cleanup_pop (1);
- pthread_cleanup_pop (1);
- pthread_cleanup_pop (1);
- pthread_cleanup_pop (1);
- pthread_cleanup_pop (1);
-
- fflush (stdout);
- fflush (stderr);
-
- return 0;
-
-error:
- pthread_cancel(ep0);
- pthread_exit(0);
-
+ return bStatus;
}
-static void *hid (void *param)
+RDR_to_PC_SlotStatus_t get_RDR_to_PC_SlotStatus(__u8 bSlot, __u8 bSeq,
+ LONG pcsc_result)
{
+ RDR_to_PC_SlotStatus_t result;
- char **names = (char **) param;
- char *status_name = names[0];
- int result = 0;
+ result.bMessageType = 0x81;
+ result.dwLength = __constant_cpu_to_le32(0);
+ result.bSlot = bSlot;
+ result.bSeq = bSeq;
+ result.bStatus = get_bStatus(pcsc_result);
+ result.bError = get_bError(pcsc_result);
+ result.bClockStatus = 0;
- hidstatus_fd = hidstatus_open (status_name);
- if (hidstatus_fd < 0) {
- if (debug)
- perror("hidstatus_fd");
- goto error;
- }
- pthread_cleanup_push (close_fd, &hidstatus_fd);
-
- __u8 *outbuf = NULL;
- pthread_cleanup_push (free, outbuf);
- size_t bufsize = 512;
- __u8 *inbuf = (__u8 *) malloc(bufsize);
- pthread_cleanup_push (free, inbuf);
- if (inbuf == NULL) {
- if (debug)
- perror("malloc");
- goto error;
- }
-
- do {
-
- /* original LinuxThreads cancelation didn't work right
- * so test for it explicitly.
- */
- pthread_testcancel ();
-
- } while (result >= 0);
-
- if (errno != ESHUTDOWN || result < 0) {
- perror ("hid loop aborted");
- pthread_cancel(ep0);
- }
-
- pthread_cleanup_pop (1);
- pthread_cleanup_pop (1);
- pthread_cleanup_pop (1);
-
- fflush (stdout);
- fflush (stderr);
-
- return 0;
-
-error:
- pthread_cancel(ep0);
- pthread_exit(0);
+ return result;
}
-static void start_io ()
+RDR_to_PC_DataBlock_t get_RDR_to_PC_DataBlock(__u8 bSlot, __u8 bSeq,
+ LONG pcsc_result, __le32 dwLength)
{
- //int tmp;
- sigset_t allsig, oldsig;
+ RDR_to_PC_DataBlock_t result;
-#ifdef AIO
- /* iso uses the same API as bulk/interrupt. we queue one
- * (u)frame's worth of data per i/o request, and the host
- * polls that queue once per interval.
- */
- switch (current_speed) {
- case USB_SPEED_FULL:
- if (iso)
- iosize = __le16_to_cpup (&fs_source_desc
- .wMaxPacketSize);
- else
- iosize = bufsize;
- break;
- case USB_SPEED_HIGH:
- /* for iso, we updated bufsize earlier */
- iosize = bufsize;
- break;
- default:
- fprintf (stderr, "bogus link speed %d\n", current_speed);
- return;
- }
-#endif /* AIO */
+ result.bMessageType = 0x80;
+ result.dwLength = dwLength;
+ result.bSlot = bSlot;
+ result.bSeq = bSeq;
+ result.bStatus = get_bStatus(pcsc_result);
+ result.bError = get_bError(pcsc_result);
+ result.bChainParameter = 0;
- sigfillset (&allsig);
- errno = pthread_sigmask (SIG_SETMASK, &allsig, &oldsig);
- if (errno < 0) {
- perror ("set thread signal mask");
- return;
- }
+ return result;
+}
- /* is it true that the LSB requires programs to disconnect
- * from their controlling tty before pthread_create()?
- * why? this clearly doesn't ...
- */
+RDR_to_PC_SlotStatus_t
+perform_PC_to_RDR_GetSlotStatus(const PC_to_RDR_GetSlotStatus_t request)
+{
+ if ( request.bMessageType != 0x65 ||
+ request.dwLength != __constant_cpu_to_le32(0) ||
+ request.bSlot != 0 ||
+ request.abRFU1 != 0 ||
+ request.abRFU2 != 0)
+ fprintf(stderr, "warning: malformed PC_to_RDR_GetSlotStatus\n");
- static char *names[2];
- names[0] = EP_IN_NAME;
- names[1] = EP_OUT_NAME;
- names[2] = EP_STATUS_NAME;
- if (pthread_create (&ccid_thread, NULL, ccid, (void *) names) != 0) {
- perror ("can't create ccid thread");
- goto cleanup;
- }
- static char * hidnames[1];
- hidnames[0] = EP_HID_STATUS_NAME;
- if (dohid) {
- config.bNumInterfaces = 2;
- if (pthread_create (&hidthread, NULL, hid, (void *)
- hidnames) != 0) {
- perror ("can't create hid thread");
- goto cleanup;
+ return get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq,
+ SCardGetStatusChange(hcontext, 1, &rstate, 1));
+}
+
+RDR_to_PC_SlotStatus_t
+perform_PC_to_RDR_IccPowerOn(const PC_to_RDR_IccPowerOn_t request, char ** pATR)
+{
+ if ( request.bMessageType != 0x62 ||
+ request.dwLength != __constant_cpu_to_le32(0) ||
+ request.bSlot != 0 ||
+ !( request.bPowerSelect == 0 ||
+ request.bPowerSelect & ccid_desc.bVoltageSupport ) ||
+ request.abRFU != 0)
+ fprintf(stderr, "warning: malformed PC_to_RDR_IccPowerOn\n");
+
+ LONG pcsc_result;
+ if (hcard) {
+ pcsc_result = SCardReconnect(hcard, SCARD_SHARE_EXCLUSIVE,
+ SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD,
+ &dwActiveProtocol);
+ } else {
+ pcsc_result = SCardConnect(hcontext, reader_name,
+ SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1,
+ &hcard, &dwActiveProtocol);
+ }
+ if (pcsc_result == SCARD_S_SUCCESS)
+ pcsc_result = SCardGetStatusChange(hcontext, 1, &rstate, 1);
+
+ RDR_to_PC_SlotStatus_t result = get_RDR_to_PC_SlotStatus(request.bSlot,
+ request.bSeq, pcsc_result);
+
+ if (pcsc_result != SCARD_S_SUCCESS) {
+ *pATR = NULL;
+ result.dwLength = __constant_cpu_to_le32(0);
+ } else {
+ *pATR = (char*) rstate.rgbAtr;
+ result.dwLength = __cpu_to_le32(rstate.cbAtr);
+ }
+
+ return result;
+}
+RDR_to_PC_SlotStatus_t
+perform_PC_to_RDR_IccPowerOff(const PC_to_RDR_IccPowerOff_t request)
+{
+ if ( request.bMessageType != 0x63 ||
+ request.dwLength != __constant_cpu_to_le32(0) ||
+ request.bSlot != 0 ||
+ request.abRFU1 != 0 ||
+ request.abRFU2 != 0)
+ fprintf(stderr, "warning: malformed PC_to_RDR_IccPowerOff\n");
+
+ LONG result = SCardDisconnect(hcard, SCARD_UNPOWER_CARD);
+ hcard = 0;
+ if (result == SCARD_E_INVALID_HANDLE) {
+ result = SCardGetStatusChange(hcontext, 1, &rstate, 1);
+ }
+
+ return get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq, result);
+}
+
+RDR_to_PC_DataBlock_t
+perform_PC_to_RDR_XfrBlock(const PC_to_RDR_XfrBlock_t request, const __u8*
+ abDataIn, __u8** abDataOut)
+{
+ if ( request.bMessageType != 0x6F ||
+ request.bSlot != 0 ||
+ request.bBWI != 0)
+ fprintf(stderr, "warning: malformed PC_to_RDR_XfrBlock\n");
+
+ DWORD dwRecvLength = MAX_BUFFER_SIZE;
+ *abDataOut = (__u8 *) malloc(dwRecvLength);
+ if (*abDataOut == NULL) {
+ return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
+ SCARD_E_NO_MEMORY, __constant_cpu_to_le32(0));
+ }
+
+ LPCSCARD_IO_REQUEST pioSendPci;
+ if (dwActiveProtocol == SCARD_PROTOCOL_T0)
+ pioSendPci = SCARD_PCI_T0;
+ else
+ pioSendPci = SCARD_PCI_T1;
+ int pcsc_result = SCardTransmit(hcard, pioSendPci, abDataIn,
+ __le32_to_cpu(request.dwLength), NULL, *abDataOut, &dwRecvLength);
+
+ return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
+ pcsc_result, __cpu_to_le32(dwRecvLength));
+}
+
+
+RDR_to_PC_Parameters_t
+get_RDR_to_PC_Parameters(__u8 bSlot, __u8 bSeq, LONG pcsc_result, __u8
+ **abProtocolDataStructure)
+{
+ RDR_to_PC_Parameters_t result;
+
+ result.bMessageType = 0x82;
+ result.bSlot = bSlot;
+ result.bSeq = bSeq;
+ if (pcsc_result == SCARD_S_SUCCESS) {
+ if (dwActiveProtocol == SCARD_PROTOCOL_T0) {
+ result.bProtocolNum = 0;
+ *abProtocolDataStructure = (__u8 *) malloc(sizeof
+ (abProtocolDataStructure_T0_t));
+ if (*abProtocolDataStructure) {
+ fprintf (stderr, "T0\n");
+ result.dwLength = __constant_cpu_to_le32(sizeof
+ (abProtocolDataStructure_T0_t));
+ abProtocolDataStructure_T0_t * t0 =
+ *(abProtocolDataStructure_T0_t**) abProtocolDataStructure;
+ /* values taken from ISO 7816-3 defaults
+ * FIXME analyze ATR to get values */
+ t0->bmFindexDindex = 1<<4| // index to table 7 ISO 7816-3 (Fi)
+ 1; // index to table 8 ISO 7816-3 (Di)
+ t0->bmTCCKST0 = 0<<1; // convention (direct)
+ t0->bGuardTimeT0 = 0xFF;
+ t0->bWaitingIntegerT0 = 0x10;
+ t0->bClockStop = 0; // (not allowed)
+ } else {
+ // error malloc
+ result.dwLength = __constant_cpu_to_le32(0);
+ *abProtocolDataStructure = NULL;
+ pcsc_result = SCARD_E_INSUFFICIENT_BUFFER;
}
} else {
- if (verbose)
- fprintf (stderr, "HID disabled.\n");
+ result.bProtocolNum = 1;
+ *abProtocolDataStructure = (__u8 *) malloc(sizeof
+ (abProtocolDataStructure_T1_t));
+ if (*abProtocolDataStructure) {
+ fprintf (stderr, "T1\n");
+ result.dwLength = __constant_cpu_to_le32(sizeof
+ (abProtocolDataStructure_T1_t));
+ abProtocolDataStructure_T1_t * t1 =
+ *(abProtocolDataStructure_T1_t**) abProtocolDataStructure;
+ /* values taken from OpenPGP-card
+ * FIXME analyze ATR to get values */
+ t1->bmFindexDindex = 1<<4| // index to table 7 ISO 7816-3 (Fi)
+ 3; // index to table 8 ISO 7816-3 (Di)
+ t1->bmTCCKST1 = 0| // checksum type (CRC)
+ 0<<1| // convention (direct)
+ 0x10;
+ t1->bGuardTimeT1 = 0xFF;
+ t1->bWaitingIntegersT1 = 4<<4| // BWI
+ 5; // CWI
+ t1->bClockStop = 0; // (not allowed)
+ t1->bIFSC = 0x80;
+ t1->bNadValue = 0; // see 7816-3 9.4.2.1 (only default value)
+ } else {
+ // error malloc
+ result.dwLength = __constant_cpu_to_le32(0);
+ *abProtocolDataStructure = NULL;
+ pcsc_result = SCARD_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ } else {
+ result.dwLength = __constant_cpu_to_le32(0);
+ *abProtocolDataStructure = NULL;
+ }
+ result.bStatus = get_bStatus(pcsc_result);
+ result.bError = get_bError(pcsc_result);
+
+ return result;
+}
+
+RDR_to_PC_Parameters_t
+perform_PC_to_RDR_GetParamters(const PC_to_RDR_GetParameters_t request,
+ __u8** abProtocolDataStructure)
+{
+ if ( request.bMessageType != 0x6C ||
+ request.dwLength != __constant_cpu_to_le32(0) ||
+ request.bSlot != 0)
+ fprintf(stderr, "warning: malformed PC_to_RDR_GetParamters\n");
+
+ LONG pcsc_result = SCardReconnect(hcard, SCARD_SHARE_EXCLUSIVE,
+ SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD,
+ &dwActiveProtocol);
+
+ return get_RDR_to_PC_Parameters(request.bSlot, request.bSeq,
+ pcsc_result, abProtocolDataStructure);
+}
+
+RDR_to_PC_DataBlock_t
+perform_PC_to_RDR_Secure(const PC_to_RDR_Secure_t request,
+ const __u8* abData, __u8** abDataOut)
+{
+ /* only short APDUs supported so Lc is always the fiths byte */
+ if ( request.bMessageType != 0x69 ||
+ request.bSlot != 0)
+ fprintf(stderr, "warning: malformed PC_to_RDR_Secure\n");
+
+ if (request.wLevelParameter != __constant_cpu_to_le16(0)) {
+ fprintf(stderr, "warning: Only APDUs, that begin and end with this command are supported.\n");
+ return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
+ SCARD_E_READER_UNSUPPORTED, __constant_cpu_to_le32(0));
+ }
+
+ printf(":");
+
+ __u8 PINMin, PINMax, bmPINLengthFormat, bmPINBlockString, bmFormatString;
+ __u8 *abPINApdu;
+ uint32_t apdulen;
+ abPINDataStucture_Verification_t *verify = NULL;
+ abPINDataStucture_Modification_t *modify = NULL;
+ switch (*abData) { // first byte of abData is bPINOperation
+ case 0x00:
+ // PIN Verification
+ verify = (abPINDataStucture_Verification_t *)
+ (abData + sizeof(__u8));
+ PINMin = verify->wPINMaxExtraDigit >> 8;
+ PINMax = verify->wPINMaxExtraDigit & 0x00ff;
+ bmPINLengthFormat = verify->bmPINLengthFormat;
+ bmPINBlockString = verify->bmPINBlockString;
+ bmFormatString = verify->bmFormatString;
+ abPINApdu = (__u8*) verify + sizeof(*verify);
+ apdulen = __le32_to_cpu(request.dwLength) - sizeof(*verify) - sizeof(__u8);
+ break;
+ case 0x01:
+ // PIN Modification
+ modify = (abPINDataStucture_Modification_t *)
+ (abData + sizeof(__u8));
+ PINMin = modify->wPINMaxExtraDigit >> 8;
+ PINMax = modify->wPINMaxExtraDigit & 0x00ff;
+ bmPINLengthFormat = modify->bmPINLengthFormat;
+ bmPINBlockString = modify->bmPINBlockString;
+ bmFormatString = modify->bmFormatString;
+ abPINApdu = (__u8*) modify + sizeof(*modify);
+ apdulen = __le32_to_cpu(request.dwLength) - sizeof(*modify) - sizeof(__u8);
+ break;
+ case 0x04:
+ // Cancel PIN function
+ default:
+ fprintf(stderr, "warning: unknown pin operation\n");
+ return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
+ SCARD_E_READER_UNSUPPORTED, __constant_cpu_to_le32(0));
+ }
+
+ // copy the apdu
+ __u8 *apdu = (__u8*) malloc(apdulen);
+ if (!apdu) {
+ return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
+ SCARD_E_NO_MEMORY, __constant_cpu_to_le32(0));
+ }
+ memcpy(apdu, abPINApdu, apdulen);
+
+ // TODO
+ char *pin = "1234";
+
+ __u8 *p;
+ /* if system units are bytes or bits */
+ uint8_t bytes = bmFormatString >> 7;
+ /* PIN position after format in the APDU command (relative to the first
+ * data after Lc). The position is based on the system units’ type
+ * indicator (maximum1111 for fifteen system units */
+ uint8_t pos = (bmFormatString >> 3) & 0xf;
+ /* Right or left justify data */
+ uint8_t right = (bmFormatString >> 2) & 1;
+ /* Bit wise for the PIN format type */
+ uint8_t type = bmFormatString & 2;
+
+ uint8_t pinlen = strnlen(pin, PINMax + 1);
+ if (pinlen > PINMax) {
+ fprintf(stderr, "warning: PIN was too long, "
+ "should be between %d and %d\n", PINMin, PINMax);
+ return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
+ SCARD_F_INTERNAL_ERROR, __constant_cpu_to_le32(0));
+ }
+
+ /* Size in bits of the PIN length inserted in the APDU command. */
+ uint8_t lenlen = bmPINBlockString >> 4;
+ /* PIN block size in bytes after justification and formatting. */
+ uint8_t blocksize = bmPINBlockString & 0xf;
+ /* PIN length position in the APDU command */
+ uint8_t lenshift = bmPINLengthFormat & 0xf;
+ if (lenlen) {
+ /* write PIN Length */
+ if (lenlen == 8) {
+ if (bytes) {
+ p = apdu + 5 + lenshift;
+ } else {
+ if (lenshift == 0)
+ p = apdu + 5;
+ if (lenshift == 8)
+ p = apdu + 5 + 1;
+ else {
+ fprintf(stderr, "warning: PIN Block too complex, aborting\n");
+ return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
+ SCARD_F_INTERNAL_ERROR,
+ __constant_cpu_to_le32(0));
+ }
+ }
+ *p = pinlen;
}
- /* give the other threads a chance to run before we report
- * success to the host.
- * FIXME better yet, use pthread_cond_timedwait() and
- * synchronize on ep config success.
- */
- sched_yield ();
-
-cleanup:
- errno = pthread_sigmask (SIG_SETMASK, &oldsig, 0);
- if (errno != 0) {
- perror ("restore sigmask");
- exit (-1);
- }
-}
-
-static void stop_io ()
-{
- if (!pthread_equal (ccid_thread, ep0)) {
- pthread_cancel (ccid_thread);
- if (pthread_join (ccid_thread, 0) != 0)
- perror ("can't join ccid thread");
- ccid_thread = ep0;
- fprintf (stderr, "cancled ccid\n");
- }
- if (!pthread_equal (ccid_thread, ep0)) {
- pthread_cancel (hidthread);
- if (pthread_join (hidthread, 0) != 0)
- perror ("can't join hid thread");
- hidthread = ep0;
- fprintf (stderr, "cancled hid\n");
- }
-}
-
-/*-------------------------------------------------------------------------*/
-
-static char *
-build_config (char *cp, const struct usb_endpoint_descriptor **ep, const struct usb_endpoint_descriptor *hid_ep)
-{
- struct usb_config_descriptor *c;
- int i;
-
- c = (struct usb_config_descriptor *) cp;
-
- memcpy (cp, &config, config.bLength);
- cp += config.bLength;
-
- memcpy (cp, &source_sink_intf, sizeof source_sink_intf);
- cp += sizeof source_sink_intf;
-
- // Append vendor class specification
- memcpy (cp, &ccid_desc, sizeof ccid_desc);
- cp += sizeof ccid_desc;
-
- for (i = 0; i < source_sink_intf.bNumEndpoints; i++) {
- memcpy (cp, ep [i], USB_DT_ENDPOINT_SIZE);
- cp += USB_DT_ENDPOINT_SIZE;
+ fprintf(stderr, "warning: PIN Block too complex, aborting\n");
+ return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
+ SCARD_F_INTERNAL_ERROR, __constant_cpu_to_le32(0));
}
- if (dohid) {
- struct usb_interface_descriptor hid_intf = {
- .bLength = sizeof hid_intf,
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 1,
+ uint8_t justify;
+ if (right)
+ justify = blocksize - pinlen;
+ else
+ justify = 0;
- .bInterfaceClass = USB_CLASS_HID,
- .iInterface = STRINGID_HID_INTERFACE,
- .bNumEndpoints = 1,
- };
- memcpy (cp, &hid_intf, sizeof hid_intf);
- cp += sizeof hid_intf;
- // Append vendor class specification
- memcpy (cp, &hid_desc, sizeof hid_desc);
- cp += sizeof hid_desc;
-
- for (i = 0; i < hid_intf.bNumEndpoints; i++) {
- memcpy (cp, &(hid_ep [i]), USB_DT_ENDPOINT_SIZE);
- cp += USB_DT_ENDPOINT_SIZE;
+ if (bytes) {
+ p = apdu + 5 + pos + justify;
+ } else {
+ if (pos == 0)
+ p = apdu + 5 + justify;
+ else if (pos == 8)
+ p = apdu + 5 + 1 + justify;
+ else {
+ fprintf(stderr, "warning: PIN Block too complex, aborting\n");
+ return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
+ SCARD_F_INTERNAL_ERROR,
+ __constant_cpu_to_le32(0));
}
}
- c->wTotalLength = __cpu_to_le16 (cp - (char *) c);
- return cp;
-}
-
-static int init_device (void)
-{
- char buf [4096], *cp = &buf [0];
- int fd;
- int result;
-
-#ifdef AIO
- if (iso)
- result = iso_autoconfig ();
- else
-#endif
- result = autoconfig ();
- if (result < 0) {
- fprintf (stderr, "?? don't recognize /dev/gadget %s device\n",
- iso ? "iso" : "bulk");
- return result;
- }
-
- fd = open (DEVNAME, O_RDWR);
- if (fd < 0) {
- perror (DEVNAME);
- return -errno;
- }
-
- *(__u32 *)cp = 0; /* tag for this format */
- cp += 4;
-
- /* write full then high speed configs */
- cp = build_config (cp, fs_eps, &fs_hid_status_desc);
- if (HIGHSPEED)
- cp = build_config (cp, hs_eps, &hs_hid_status_desc);
-
- device_desc.idVendor = __cpu_to_le16 (vendorid);
- device_desc.idProduct = __cpu_to_le16 (productid);
- if (verbose) {
- fprintf(stderr, "idVendor=%04X idProduct=%04X\n", vendorid,
- productid);
- }
- /* and device descriptor at the end */
- memcpy (cp, &device_desc, sizeof device_desc);
- cp += sizeof device_desc;
-
- result = write (fd, &buf [0], cp - &buf [0]);
- if (result < 0) {
- perror ("write dev descriptors");
- close (fd);
- return result;
- } else if (result != (cp - buf)) {
- fprintf (stderr, "dev init, wrote %d expected %ld\n",
- result, (long int) (cp - buf));
- close (fd);
- return -EIO;
- }
- return fd;
-}
-
-static void handle_control (int fd, struct usb_ctrlrequest *setup)
-{
- int result, tmp;
- __u8 buf [256];
- __u16 value, index, length;
-
- value = __le16_to_cpu(setup->wValue);
- index = __le16_to_cpu(setup->wIndex);
- length = __le16_to_cpu(setup->wLength);
-
- if ((setup->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
- goto special;
-
- switch (setup->bRequest) { /* usb 2.0 spec ch9 requests */
- case USB_REQ_GET_DESCRIPTOR:
- if (debug)
- fprintf(stderr, "USB_REQ_GET_DESCRIPTOR\n");
- if (setup->bRequestType != USB_DIR_IN)
- goto stall;
- switch (value >> 8) {
- case USB_DT_STRING:
- tmp = value & 0x0ff;
- if (debug)
- fprintf (stderr,
- "... get string %d lang %04x\n",
- tmp, index);
- if (tmp != 0 && index != strings.language) {
- fprintf (stderr, "wrong language\n");
- goto stall;
- }
- memset (buf, 0, 256); /* zero all the bytes */
- result = usb_gadget_get_string (&strings, tmp, buf);
- if (result < 0) {
- perror("usb_gadget_get_string");
- goto stall;
- }
- tmp = result;
- if (length < tmp)
- tmp = length;
- result = write (fd, buf, tmp);
- if (result < 0) {
- if (errno == EIDRM)
- fprintf (stderr, "string timeout\n");
- else
- perror ("write string data");
- } else if (result != tmp) {
- fprintf (stderr, "short string write, %d\n",
- result);
- }
- break;
- default:
- goto stall;
- }
- return;
- case USB_REQ_SET_CONFIGURATION:
- if (debug)
- fprintf (stderr, "USB_REQ_SET_CONFIGURATION #%d\n", value);
- if (setup->bRequestType != USB_DIR_OUT)
- goto stall;
-
- /* Kernel is normally waiting for us to finish reconfiguring
- * the device.
- *
- * Some hardware can't, notably older PXA2xx hardware. (With
- * racey and restrictive config change automagic. PXA 255 is
- * OK, most PXA 250s aren't. If it has a UDC CFR register,
- * it can handle deferred response for SET_CONFIG.) To handle
- * such hardware, don't write code this way ... instead, keep
- * the endpoints always active and don't rely on seeing any
- * config change events, either this or SET_INTERFACE.
- */
- switch (value) {
- case CONFIG_VALUE:
- if ( source_fd >= 0 || sink_fd >= 0 || status_fd >= 0 || hidstatus_fd >= 0 )
- stop_io ();
- start_io ();
- break;
- case 0:
- stop_io ();
- break;
- default:
- /* kernel bug -- "can't happen" */
- fprintf (stderr, "? illegal config\n");
- goto stall;
- }
-
- /* ... ack (a write would stall) */
- result = read (fd, &result, 0);
- if (result)
- perror ("ack SET_CONFIGURATION");
- return;
- case USB_REQ_GET_INTERFACE:
- if (debug)
- fprintf (stderr, "USB_REQ_GET_INTERFACE\n");
- if (setup->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
- || index != 0
- || length > 1)
- goto stall;
-
- /* only one altsetting in this driver */
- buf [0] = 0;
- result = write (fd, buf, length);
- if (result < 0) {
- if (errno == EIDRM)
- fprintf (stderr, "GET_INTERFACE timeout\n");
- else
- perror ("write GET_INTERFACE data");
- } else if (result != length) {
- fprintf (stderr, "short GET_INTERFACE write, %d\n",
- result);
- }
- return;
- case USB_REQ_SET_INTERFACE:
- if (debug)
- fprintf (stderr, "USB_REQ_SET_INTERFACE\n");
- if (setup->bRequestType != USB_RECIP_INTERFACE
- || index != 0
- || value != 0)
- goto stall;
-
- /* just reset toggle/halt for the interface's endpoints */
- result = 0;
- if (ioctl (source_fd, GADGETFS_CLEAR_HALT) < 0) {
- result = errno;
- perror ("reset source fd");
+ while (*pin) {
+ uint8_t c;
+ switch (type) {
+ case 0:
+ // binary
+ switch (*pin) {
+ case '0':
+ c = 0x00;
+ break;
+ case '1':
+ c = 0x01;
+ break;
+ case '2':
+ c = 0x02;
+ break;
+ case '3':
+ c = 0x03;
+ break;
+ case '4':
+ c = 0x04;
+ break;
+ case '5':
+ c = 0x05;
+ break;
+ case '6':
+ c = 0x06;
+ break;
+ case '7':
+ c = 0x07;
+ break;
+ case '8':
+ c = 0x08;
+ break;
+ case '9':
+ c = 0x09;
+ break;
+ default:
+ fprintf(stderr, "warning: PIN character %c not supported, aborting", *pin);
+ return get_RDR_to_PC_DataBlock(request.bSlot,
+ request.bSeq, SCARD_F_INTERNAL_ERROR,
+ __constant_cpu_to_le32(0));
}
- if (ioctl (sink_fd, GADGETFS_CLEAR_HALT) < 0) {
- result = errno;
- perror ("reset sink fd");
- }
- if (status_fd > 0) {
- if (ioctl (status_fd, GADGETFS_CLEAR_HALT) < 0) {
- result = errno;
- perror ("reset status fd");
- }
- }
- if (hidstatus_fd > 0) {
- if (ioctl (hidstatus_fd, GADGETFS_CLEAR_HALT) < 0) {
- result = errno;
- perror ("reset hidstatus fd");
- }
- }
- /* FIXME eventually reset the result endpoint too */
- if (result)
- goto stall;
-
- /* ... and ack (a write would stall) */
- result = read (fd, &result, 0);
- if (result)
- perror ("ack SET_INTERFACE");
- return;
- default:
- goto stall;
- }
-
-special:
- switch (setup->bRequestType) {
- case USB_REQ_CCID: {
- __u8 *outbuf = NULL;
- result = parse_ccid_control(setup, &outbuf);
- if (result < 0 || result > 256)
- goto stall;
- if (debug)
- fprintf(stderr, "writing %d bytes... ", result);
- result = write (fd, outbuf, result);
- if (debug)
- fprintf(stderr, "done.\n");
- free(outbuf);
- if (result < 0)
- goto stall;
- } return;
+ break;
+ case 1:
+ // BCD
+ fprintf(stderr, "warning: BCD format not supported, aborting");
+ return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
+ SCARD_F_INTERNAL_ERROR, __constant_cpu_to_le32(0));
+ case 2:
+ // ASCII
+ c = *pin;
+ break;
default:
- goto stall;
+ fprintf(stderr, "warning: unknown formatting, aborting");
+ return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
+ SCARD_F_INTERNAL_ERROR, __constant_cpu_to_le32(0));
}
+ *p = c;
+ p++;
+ pin++;
+ }
-stall:
- if (verbose) {
- fprintf(stderr, "%s:%d\n", __FILE__, __LINE__);
- fprintf (stderr, "... protocol stall %02x.%02x\n",
- setup->bRequestType, setup->bRequest);
- for (tmp = 0; tmp<5; tmp++) {
- printf("%d: %s\n", stringtab[tmp].id, stringtab[tmp].s);
+
+ DWORD dwRecvLength = MAX_BUFFER_SIZE;
+ *abDataOut = (__u8 *) malloc(dwRecvLength);
+ if (*abDataOut == NULL) {
+ return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
+ SCARD_E_NO_MEMORY, __constant_cpu_to_le32(0));
+ }
+
+ LPCSCARD_IO_REQUEST pioSendPci;
+ if (dwActiveProtocol == SCARD_PROTOCOL_T0)
+ pioSendPci = SCARD_PCI_T0;
+ else
+ pioSendPci = SCARD_PCI_T1;
+ int pcsc_result = SCardTransmit(hcard, pioSendPci, apdu, apdulen, NULL,
+ *abDataOut, &dwRecvLength);
+
+ return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
+ pcsc_result, __cpu_to_le32(dwRecvLength));
+}
+
+RDR_to_PC_NotifySlotChange_t
+get_RDR_to_PC_NotifySlotChange ()
+{
+ RDR_to_PC_NotifySlotChange_t result;
+ result.bMessageType = 0x50;
+ result.bmSlotICCState = 0; // no change
+
+ DWORD current = rstate.dwEventState;
+ if (SCARD_S_SUCCESS != SCardGetStatusChange(hcontext, 1, &rstate, 1)) {
+ fprintf(stderr, "state changed: error\n");
+ result.bmSlotICCState = 2; // changed (error)
+ } else if (!(current & rstate.dwEventState)) {
+ fprintf(stderr, "state changed\n");
+ result.bmSlotICCState = 2; // changed
+ }
+
+ return result;
+}
+
+RDR_to_PC_SlotStatus_t
+perform_unknown(const PC_to_RDR_GetSlotStatus_t request)
+{
+ RDR_to_PC_SlotStatus_t result;
+ switch (request.bMessageType) {
+ case 0x62:
+ case 0x6F:
+ case 0x69:
+ result.bMessageType = 0x80;
+ break;
+ case 0x63:
+ case 0x65:
+ case 0x6E:
+ case 0x6A:
+ case 0x71:
+ case 0x72:
+ result.bMessageType = 0x81;
+ break;
+ case 0x61:
+ case 0x6C:
+ case 0x6D:
+ result.bMessageType = 0x82;
+ break;
+ case 0x6B:
+ result.bMessageType = 0x83;
+ break;
+ case 0x73:
+ result.bMessageType = 0x84;
+ break;
+ default:
+ fprintf(stderr, "unknown message type\n");
+ result.bMessageType = 0;
+ }
+ result.dwLength = __constant_cpu_to_le32(0);
+ result.bSlot = request.bSlot,
+ result.bSeq = request.bSeq;
+ result.bStatus = get_bStatus(SCARD_F_UNKNOWN_ERROR);
+ result.bError = 0;
+ result.bClockStatus = 0;
+
+ return result;
+}
+
+int parse_ccid(const __u8* inbuf, __u8** outbuf)
+{
+ if (inbuf == NULL)
+ return 0;
+ int result = -1;
+ if (SCardIsValidContext(hcontext) != SCARD_S_SUCCESS) {
+ if (perform_initialization(reader_num) == NULL)
+ goto error;
+ }
+
+ switch (*inbuf) {
+ case 0x62:
+ { fprintf(stderr, "PC_to_RDR_IccPowerOn\n");
+
+ char* atr;
+ PC_to_RDR_IccPowerOn_t input =
+ *(PC_to_RDR_IccPowerOn_t*) inbuf;
+ RDR_to_PC_SlotStatus_t output =
+ perform_PC_to_RDR_IccPowerOn(input, &atr);
+
+ result = sizeof output + __le32_to_cpu(output.dwLength);
+ *outbuf = realloc(*outbuf, result);
+ if (*outbuf == NULL) {
+ result = -1;
+ break;
}
- }
- /* non-iso endpoints are stalled by issuing an i/o request
- * in the "wrong" direction. ep0 is special only because
- * the direction isn't fixed.
- */
- if (setup->bRequestType & USB_DIR_IN)
- result = read (fd, &result, 0);
- else
- result = write (fd, &result, 0);
- if (result != -1)
- fprintf (stderr, "can't stall ep0 for %02x.%02x\n",
- setup->bRequestType, setup->bRequest);
- else if (errno != EL2HLT)
- perror ("ep0 stall");
-}
+ memcpy(*outbuf, &output, sizeof output);
+ memcpy(*outbuf + sizeof output, atr,
+ __le32_to_cpu(output.dwLength));
+ } break;
-static void signothing (int sig, siginfo_t *info, void *ptr)
-{
- /* NOP */
- if (debug)
- fprintf (stderr, "%s %d\n", __FUNCTION__, sig);
-}
+ case 0x63:
+ { fprintf(stderr, "PC_to_RDR_IccPowerOff\n");
-static const char *speed (enum usb_device_speed s)
-{
- switch (s) {
- case USB_SPEED_LOW: return "low speed";
- case USB_SPEED_FULL: return "full speed";
- case USB_SPEED_HIGH: return "high speed";
- default: return "UNKNOWN speed";
- }
-}
+ PC_to_RDR_IccPowerOff_t input =
+ *(PC_to_RDR_IccPowerOff_t*) inbuf;
+ RDR_to_PC_SlotStatus_t output =
+ perform_PC_to_RDR_IccPowerOff(input);
-/*-------------------------------------------------------------------------*/
+ result = sizeof output;
+ *outbuf = realloc(*outbuf, result);
+ if (*outbuf == NULL) {
+ result = -1;
+ break;
+ }
-/* control thread, handles main event loop */
+ memcpy(*outbuf, &output, sizeof output);
+ } break;
-#define NEVENT 5
-#define LOGDELAY (15 * 60) /* seconds before stdout timestamp */
+ case 0x65:
+ { /*fprintf(stderr, "PC_to_RDR_GetSlotStatus\n");*/
-static void *ep0_thread (void *param)
-{
- int fd = *(int*) param;
- struct sigaction action;
- time_t now, last;
- struct pollfd ep0_poll;
+ PC_to_RDR_GetSlotStatus_t input =
+ *(PC_to_RDR_GetSlotStatus_t*) inbuf;
+ RDR_to_PC_SlotStatus_t output =
+ perform_PC_to_RDR_GetSlotStatus(input);
- ccid_thread = ep0 = pthread_self ();
- pthread_cleanup_push (close_fd, param);
- pthread_cleanup_push (stop_io, NULL);
+ result = sizeof output;
+ *outbuf = realloc(*outbuf, result);
+ if (*outbuf == NULL) {
+ result = -1;
+ break;
+ }
- /* REVISIT signal handling ... normally one pthread should
- * be doing sigwait() to handle all async signals.
- */
- action.sa_sigaction = signothing;
- sigfillset (&action.sa_mask);
- action.sa_flags = SA_SIGINFO;
- if (sigaction (SIGINT, &action, NULL) < 0) {
- perror ("SIGINT");
- return 0;
- }
- if (sigaction (SIGQUIT, &action, NULL) < 0) {
- perror ("SIGQUIT");
- return 0;
- }
+ memcpy(*outbuf, &output, sizeof output);
+ } break;
- ep0_poll.fd = fd;
- ep0_poll.events = POLLIN | POLLOUT | POLLHUP;
+ case 0x6F:
+ { fprintf(stderr, "PC_to_RDR_XfrBlock\n");
- /* event loop */
- last = 0;
- for (;;) {
- int tmp;
- struct usb_gadgetfs_event event [NEVENT];
- int connected = 0;
- int i, nevent;
+ __u8* rapdu;
+ PC_to_RDR_XfrBlock_t input = *(PC_to_RDR_XfrBlock_t*) inbuf;
+ RDR_to_PC_DataBlock_t output =
+ perform_PC_to_RDR_XfrBlock(input, inbuf + sizeof input,
+ &rapdu);
- /* Use poll() to test that mechanism, to generate
- * activity timestamps, and to make it easier to
- * tweak this code to work without pthreads. When
- * AIO is needed without pthreads, ep0 can be driven
- * instead using SIGIO.
- */
- tmp = poll(&ep0_poll, 1, -1);
- if (verbose) {
- time (&now);
- if ((now - last) > LOGDELAY) {
- char timebuf[26];
+ result = sizeof output + __le32_to_cpu(output.dwLength);
+ *outbuf = realloc(*outbuf, result);
+ if (*outbuf == NULL) {
+ free(rapdu);
+ result = -1;
+ break;
+ }
- last = now;
- ctime_r (&now, timebuf);
- printf ("\n** %s", timebuf);
- }
- }
- if (tmp < 0) {
- /* exit path includes EINTR exits */
- perror("poll");
- break;
- }
+ memcpy(*outbuf, &output, sizeof output);
+ memcpy(*outbuf + sizeof output, rapdu,
+ __le32_to_cpu(output.dwLength));
+ free(rapdu);
+ } break;
- tmp = read (fd, &event, sizeof event);
- if (tmp < 0) {
- if (errno == EAGAIN) {
- sleep (1);
- continue;
- }
- perror ("ep0 read after poll");
- goto done;
- }
- nevent = tmp / sizeof event [0];
- if (nevent != 1 && debug)
- fprintf (stderr, "read %d ep0 events\n",
- nevent);
+ case 0x6C:
+ { fprintf(stderr, "PC_to_RDR_GetParameters\n");
- for (i = 0; i < nevent; i++) {
- switch (event [i].type) {
- case GADGETFS_NOP:
- if (verbose)
- fprintf (stderr, "NOP\n");
- break;
- case GADGETFS_CONNECT:
- connected = 1;
- current_speed = event [i].u.speed;
- if (verbose)
- fprintf (stderr,
- "CONNECT %s\n",
- speed (event [i].u.speed));
- break;
- case GADGETFS_SETUP:
- connected = 1;
- handle_control (fd, &event [i].u.setup);
- break;
- case GADGETFS_DISCONNECT:
- connected = 0;
- current_speed = USB_SPEED_UNKNOWN;
- if (verbose)
- fprintf(stderr, "DISCONNECT\n");
- stop_io ();
- break;
- case GADGETFS_SUSPEND:
- // connected = 1;
- if (verbose)
- fprintf (stderr, "SUSPEND\n");
- break;
- default:
- fprintf (stderr,
- "* unhandled event %d\n",
- event [i].type);
- }
- }
- continue;
-done:
- fflush (stdout);
- if (connected)
- stop_io ();
- break;
- }
- if (verbose)
- fprintf (stderr, "ep0 done.\n");
+ __u8* abProtocolDataStructure;
+ PC_to_RDR_GetParameters_t input = *(PC_to_RDR_GetParameters_t*)
+ inbuf;
+ RDR_to_PC_Parameters_t output = perform_PC_to_RDR_GetParamters(
+ input, &abProtocolDataStructure );
- pthread_cleanup_pop (1);
- pthread_cleanup_pop (1);
+ result = sizeof output + __le32_to_cpu(output.dwLength);
+ *outbuf = realloc(*outbuf, result);
+ if (*outbuf == NULL) {
+ if (abProtocolDataStructure)
+ free(abProtocolDataStructure);
+ result = -1;
+ break;
+ }
- fflush (stdout);
- fflush (stderr);
- return 0;
-}
+ memcpy(*outbuf, &output, sizeof output);
+ memcpy(*outbuf + sizeof output, abProtocolDataStructure,
+ __le32_to_cpu(output.dwLength));
+ if (abProtocolDataStructure)
+ free(abProtocolDataStructure);
+ } break;
-/*-------------------------------------------------------------------------*/
+ case 0x69:
+ { fprintf(stderr, "PC_to_RDR_Secure\n");
-int
-main (int argc, char **argv)
-{
- int fd, c, i;
+ __u8* rapdu;
+ PC_to_RDR_Secure_t input = *(PC_to_RDR_Secure_t *) inbuf;
+ RDR_to_PC_DataBlock_t output =
+ perform_PC_to_RDR_Secure(input, inbuf + sizeof input,
+ &rapdu);
- /* random initial serial number */
- srand ((int) time (0));
- for (i = 0; i < sizeof serial - 1; ) {
- c = rand () % 127;
- if ((('a' <= c && c <= 'z') || ('0' <= c && c <= '9')))
- serial [i++] = c;
- }
+ result = sizeof output + __le32_to_cpu(output.dwLength);
+ *outbuf = realloc(*outbuf, result);
+ if (*outbuf == NULL) {
+ free(rapdu);
+ result = -1;
+ break;
+ }
- for (i=1; iwValue);
+ index = __le16_to_cpu(setup->wIndex);
+ length = __le16_to_cpu(setup->wLength);
+ if (setup->bRequestType == USB_REQ_CCID)
+ switch(setup->bRequest) {
+ case CCID_CONTROL_ABORT:
+ {
+ fprintf(stderr, "ABORT\n");
+ if (length != 0x00) {
+ fprintf(stderr, "warning: malformed ABORT\n");
+ }
+ result = SCardCancel(hcontext);
+ if (result != SCARD_S_SUCCESS)
+ fprintf(stderr, "pc/sc error: %s\n",
+ pcsc_stringify_error(result));
+ result = 0;
+ } break;
+ case CCID_CONTROL_GET_CLOCK_FREQUENCIES:
+ {
+ fprintf(stderr, "GET_CLOCK_FREQUENCIES\n");
+ if (value != 0x00) {
+ fprintf(stderr,
+ "warning: malformed GET_CLOCK_FREQUENCIES\n");
+ }
+
+ result = sizeof(__le32);
+ *outbuf = realloc(*outbuf, result);
+ if (*outbuf == NULL) {
+ result = -1;
+ break;
+ }
+ __le32 clock = ccid_desc.dwDefaultClock;
+ memcpy(*outbuf, &clock, sizeof (__le32));
+ } break;
+ case CCID_CONTROL_GET_DATA_RATES:
+ {
+ fprintf(stderr, "GET_DATA_RATES\n");
+ if (value != 0x00) {
+ fprintf(stderr, "warning: malformed GET_DATA_RATES\n");
+ }
+
+ result = sizeof (__le32);
+ *outbuf = realloc(*outbuf, result);
+ if (*outbuf == NULL) {
+ result = -1;
+ break;
+ }
+ __le32 drate = ccid_desc.dwDataRate;
+ memcpy(*outbuf, &drate, sizeof (__le32));
+ } break;
+ default:
+ printf("unknown status setup->bRequest == %d", setup->bRequest);
+ }
+
+ return result;
}
diff --git a/ccid/ccid.h b/ccid/ccid.h
index 6425757..89822d2 100644
--- a/ccid/ccid.h
+++ b/ccid/ccid.h
@@ -16,10 +16,15 @@
* You should have received a copy of the GNU General Public License along with
* ccid. If not, see .
*/
+#ifndef _VPCD_H_
+#define _VPCD_H_
+
+#include
#include
-#include
-#include
-#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
#ifndef USB_REQ_CCID
#define USB_REQ_CCID 0xA1
@@ -246,899 +251,42 @@ ccid_desc = {
0x2, // PIN Modification supported
.bMaxCCIDBusySlots = 0x01,
};
+char* perform_initialization(int num);
+int perform_shutdown();
-
-SCARDCONTEXT hcontext = 0;
-SCARDHANDLE hcard = 0;
-SCARD_READERSTATE rstate;
-DWORD dwActiveProtocol;
-char reader_name[MAX_READERNAME];
-int reader_num;
-
-char* perform_initialization(int num)
-{
- char *readers, *str;
- DWORD size;
- LONG r;
-
- reader_num = num;
- r = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hcontext);
- if (r != SCARD_S_SUCCESS) {
- fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r));
- SCardReleaseContext(hcontext);
- return NULL;
- }
- r = SCardListReaders(hcontext, NULL, NULL, &size);
- if (size == 0)
- r = SCARD_E_UNKNOWN_READER;
- if (r != SCARD_S_SUCCESS) {
- fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r));
- SCardReleaseContext(hcontext);
- return NULL;
- }
-
- /* get all readers */
- readers = (char *) malloc(size);
- if (readers == NULL) {
- fprintf(stderr, "pc/sc error: %s\n",
- pcsc_stringify_error(SCARD_E_NO_MEMORY));
- SCardReleaseContext(hcontext);
- return NULL;
- }
- r = SCardListReaders(hcontext, NULL, readers, &size);
- if (r != SCARD_S_SUCCESS) {
- free(readers);
- fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r));
- SCardReleaseContext(hcontext);
- return NULL;
- }
-
- /* name of reader number num */
- str = readers;
- for (size = 0; size < num; size++) {
- /* go to the next name */
- str += strlen(str) + 1;
-
- /* no more readers available? */
- if (strlen(str) == 0) {
- free(readers);
- fprintf(stderr, "pc/sc error: %s\n",
- pcsc_stringify_error(SCARD_E_UNKNOWN_READER));
- SCardReleaseContext(hcontext);
- return NULL;
- }
- }
- strncpy(reader_name, str, MAX_READERNAME);
- free(readers);
-
- rstate.dwCurrentState = SCARD_STATE_UNAWARE;
- rstate.dwEventState = SCARD_STATE_UNAWARE;
- rstate.szReader = reader_name;
-
- return reader_name;
-}
-
-
-int perform_shutdown()
-{
- SCardDisconnect(hcard, SCARD_UNPOWER_CARD);
- hcard = 0;
- rstate.dwCurrentState = SCARD_STATE_UNAWARE;
- rstate.dwEventState = SCARD_STATE_UNAWARE;
- return SCardReleaseContext(hcontext);
-}
-
-__u8 get_bError(LONG pcsc_result)
-{
- switch (pcsc_result) {
- case SCARD_S_SUCCESS : /**< No error was encountered. */
- // Command not supported
- return 0;
- case SCARD_E_CANCELLED : /**< The action was cancelled by an SCardCancel request. */
- fprintf(stderr, "CMD_ABORTED\n");
- return 0xFF;
- case SCARD_E_INVALID_HANDLE : /**< The supplied handle was invalid. */
- case SCARD_E_NO_SMARTCARD : /**< The operation requires a Smart Card, but no Smart Card is currently in the device. */
- case SCARD_E_UNKNOWN_CARD : /**< The specified smart card name is not recognized. */
- case SCARD_E_NOT_READY : /**< The reader or smart card is not ready to accept commands. */
- case SCARD_W_UNRESPONSIVE_CARD : /**< The smart card is not responding to a reset. */
- case SCARD_W_UNPOWERED_CARD : /**< Power has been removed from the smart card, so that further communication is not possible. */
- case SCARD_W_REMOVED_CARD : /**< The smart card has been removed, so further communication is not possible. */
- fprintf(stderr, "ICC_MUTE\n");
- return 0xFE;
- case SCARD_E_SHARING_VIOLATION : /**< The smart card cannot be accessed because of other connections outstanding. */
- fprintf(stderr, "CMD_SLOT_BUSY\n");
- return 0xE0;
- case SCARD_E_PROTO_MISMATCH : /**< The requested protocols are incompatible with the protocol currently in use with the smart card. */
- fprintf(stderr, "ICC_PROTOCOL_NOT_SUPPORTED\n");
- return 0xF6;
- // case SCARD_F_INTERNAL_ERROR : /**< An internal consistency check failed. */
- // case SCARD_E_INVALID_PARAMETER : /**< One or more of the supplied parameters could not be properly interpreted. */
- // case SCARD_E_INVALID_TARGET : /**< Registry startup information is missing or invalid. */
- // case SCARD_E_NO_MEMORY : /**< Not enough memory available to complete this command. */
- // case SCARD_F_WAITED_TOO_LONG : /**< An internal consistency timer has expired. */
- // case SCARD_E_INSUFFICIENT_BUFFER : /**< The data buffer to receive returned data is too small for the returned data. */
- // case SCARD_E_UNKNOWN_READER : /**< The specified reader name is not recognized. */
- // case SCARD_E_TIMEOUT : /**< The user-specified timeout value has expired. */
- // case SCARD_E_CANT_DISPOSE : /**< The system could not dispose of the media in the requested manner. */
- // case SCARD_E_INVALID_VALUE : /**< One or more of the supplied parameters values could not be properly interpreted. */
- // case SCARD_E_SYSTEM_CANCELLED : /**< The action was cancelled by the system, presumably to log off or shut down. */
- // case SCARD_F_COMM_ERROR : /**< An internal communications error has been detected. */
- // case SCARD_F_UNKNOWN_ERROR : /**< An internal error has been detected, but the source is unknown. */
- // case SCARD_E_INVALID_ATR : /**< An ATR obtained from the registry is not a valid ATR string. */
- // case SCARD_E_NOT_TRANSACTED : /**< An attempt was made to end a non-existent transaction. */
- // case SCARD_E_READER_UNAVAILABLE : /**< The specified reader is not currently available for use. */
- // case SCARD_W_UNSUPPORTED_CARD : /**< The reader cannot communicate with the card, due to ATR string configuration conflicts. */
- // case SCARD_W_RESET_CARD : /**< The smart card has been reset, so any shared state information is invalid. */
- // case SCARD_E_PCI_TOO_SMALL : /**< The PCI Receive buffer was too small. */
- // case SCARD_E_READER_UNSUPPORTED : /**< The reader driver does not meet minimal requirements for support. */
- // case SCARD_E_DUPLICATE_READER : /**< The reader driver did not produce a unique reader name. */
- // case SCARD_E_CARD_UNSUPPORTED : /**< The smart card does not meet minimal requirements for support. */
- // case SCARD_E_NO_SERVICE : /**< The Smart card resource manager is not running. */
- // case SCARD_E_SERVICE_STOPPED : /**< The Smart card resource manager has shut down. */
- // case SCARD_E_NO_READERS_AVAILABLE : /**< Cannot find a smart card reader. */
- default:
- fprintf(stderr, "HW_ERROR\n");
- return 0xFB;
- }
-}
-
-__u8 get_bStatus(LONG pcsc_result)
-{
- __u8 bStatus = 0;
-
- if (rstate.dwEventState & SCARD_STATE_PRESENT) {
- if (rstate.dwEventState & SCARD_STATE_MUTE ||
- rstate.dwEventState & SCARD_STATE_UNPOWERED) {
- // inactive
- fprintf(stderr, "card inactive\n");
- bStatus = 1;
- } else {
- // active
- /*fprintf(stderr, "card active\n");*/
- bStatus = 0;
- }
- } else {
- // absent
- /*fprintf(stderr, "card absent\n");*/
- bStatus = 2;
- if (hcard != 0) {
- pcsc_result = SCardDisconnect(hcard, SCARD_UNPOWER_CARD);
- hcard = 0;
- }
- }
-
- if (pcsc_result != SCARD_S_SUCCESS) {
- bStatus |= (1<<6);
- fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(pcsc_result));
- }
-
- return bStatus;
-}
-
+__u8 get_bError(LONG pcsc_result);
+__u8 get_bStatus(LONG pcsc_result);
RDR_to_PC_SlotStatus_t get_RDR_to_PC_SlotStatus(__u8 bSlot, __u8 bSeq,
- LONG pcsc_result)
-{
- RDR_to_PC_SlotStatus_t result;
-
- result.bMessageType = 0x81;
- result.dwLength = __constant_cpu_to_le32(0);
- result.bSlot = bSlot;
- result.bSeq = bSeq;
- result.bStatus = get_bStatus(pcsc_result);
- result.bError = get_bError(pcsc_result);
- result.bClockStatus = 0;
-
- return result;
-}
-
+ LONG pcsc_result);
RDR_to_PC_DataBlock_t get_RDR_to_PC_DataBlock(__u8 bSlot, __u8 bSeq,
- LONG pcsc_result, __le32 dwLength)
-{
- RDR_to_PC_DataBlock_t result;
+ LONG pcsc_result, __le32 dwLength);
+RDR_to_PC_SlotStatus_t perform_PC_to_RDR_GetSlotStatus(
+ const PC_to_RDR_GetSlotStatus_t request);
+RDR_to_PC_SlotStatus_t perform_PC_to_RDR_GetSlotStatus(
+ const PC_to_RDR_GetSlotStatus_t request);
+RDR_to_PC_SlotStatus_t perform_PC_to_RDR_IccPowerOn(
+ const PC_to_RDR_IccPowerOn_t request, char ** pATR);
+RDR_to_PC_SlotStatus_t perform_PC_to_RDR_IccPowerOff(
+ const PC_to_RDR_IccPowerOff_t request);
+RDR_to_PC_DataBlock_t perform_PC_to_RDR_XfrBlock(
+ const PC_to_RDR_XfrBlock_t request, const __u8*
+ abDataIn, __u8** abDataOut);
+RDR_to_PC_Parameters_t get_RDR_to_PC_Parameters(__u8 bSlot, __u8 bSeq,
+ LONG pcsc_result, __u8 **abProtocolDataStructure);
+RDR_to_PC_Parameters_t perform_PC_to_RDR_GetParamters(
+ const PC_to_RDR_GetParameters_t request,
+ __u8** abProtocolDataStructure);
+RDR_to_PC_DataBlock_t perform_PC_to_RDR_Secure(
+ const PC_to_RDR_Secure_t request, const __u8* abData,
+ __u8** abDataOut);
+RDR_to_PC_NotifySlotChange_t get_RDR_to_PC_NotifySlotChange ();
+RDR_to_PC_SlotStatus_t perform_unknown(
+ const PC_to_RDR_GetSlotStatus_t request);
- result.bMessageType = 0x80;
- result.dwLength = dwLength;
- result.bSlot = bSlot;
- result.bSeq = bSeq;
- result.bStatus = get_bStatus(pcsc_result);
- result.bError = get_bError(pcsc_result);
- result.bChainParameter = 0;
+int parse_ccid(const __u8* inbuf, __u8** outbuf);
+int parse_ccid_control(struct usb_ctrlrequest *setup, __u8 **outbuf);
- return result;
+#ifdef __cplusplus
}
-
-RDR_to_PC_SlotStatus_t
-perform_PC_to_RDR_GetSlotStatus(const PC_to_RDR_GetSlotStatus_t request)
-{
- if ( request.bMessageType != 0x65 ||
- request.dwLength != __constant_cpu_to_le32(0) ||
- request.bSlot != 0 ||
- request.abRFU1 != 0 ||
- request.abRFU2 != 0)
- fprintf(stderr, "warning: malformed PC_to_RDR_GetSlotStatus\n");
-
- return get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq,
- SCardGetStatusChange(hcontext, 1, &rstate, 1));
-}
-
-RDR_to_PC_SlotStatus_t
-perform_PC_to_RDR_IccPowerOn(const PC_to_RDR_IccPowerOn_t request, char ** pATR)
-{
- if ( request.bMessageType != 0x62 ||
- request.dwLength != __constant_cpu_to_le32(0) ||
- request.bSlot != 0 ||
- !( request.bPowerSelect == 0 ||
- request.bPowerSelect & ccid_desc.bVoltageSupport ) ||
- request.abRFU != 0)
- fprintf(stderr, "warning: malformed PC_to_RDR_IccPowerOn\n");
-
- LONG pcsc_result;
- if (hcard) {
- pcsc_result = SCardReconnect(hcard, SCARD_SHARE_EXCLUSIVE,
- SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD,
- &dwActiveProtocol);
- } else {
- pcsc_result = SCardConnect(hcontext, reader_name,
- SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1,
- &hcard, &dwActiveProtocol);
- }
- if (pcsc_result == SCARD_S_SUCCESS)
- pcsc_result = SCardGetStatusChange(hcontext, 1, &rstate, 1);
-
- RDR_to_PC_SlotStatus_t result = get_RDR_to_PC_SlotStatus(request.bSlot,
- request.bSeq, pcsc_result);
-
- if (pcsc_result != SCARD_S_SUCCESS) {
- *pATR = NULL;
- result.dwLength = __constant_cpu_to_le32(0);
- } else {
- *pATR = (char*) rstate.rgbAtr;
- result.dwLength = __cpu_to_le32(rstate.cbAtr);
- }
-
- return result;
-}
-RDR_to_PC_SlotStatus_t
-perform_PC_to_RDR_IccPowerOff(const PC_to_RDR_IccPowerOff_t request)
-{
- if ( request.bMessageType != 0x63 ||
- request.dwLength != __constant_cpu_to_le32(0) ||
- request.bSlot != 0 ||
- request.abRFU1 != 0 ||
- request.abRFU2 != 0)
- fprintf(stderr, "warning: malformed PC_to_RDR_IccPowerOff\n");
-
- LONG result = SCardDisconnect(hcard, SCARD_UNPOWER_CARD);
- hcard = 0;
- if (result == SCARD_E_INVALID_HANDLE) {
- result = SCardGetStatusChange(hcontext, 1, &rstate, 1);
- }
-
- return get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq, result);
-}
-
-RDR_to_PC_DataBlock_t
-perform_PC_to_RDR_XfrBlock(const PC_to_RDR_XfrBlock_t request, const __u8*
- abDataIn, __u8** abDataOut)
-{
- if ( request.bMessageType != 0x6F ||
- request.bSlot != 0 ||
- request.bBWI != 0)
- fprintf(stderr, "warning: malformed PC_to_RDR_XfrBlock\n");
-
- DWORD dwRecvLength = MAX_BUFFER_SIZE;
- *abDataOut = (__u8 *) malloc(dwRecvLength);
- if (*abDataOut == NULL) {
- return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
- SCARD_E_NO_MEMORY, __constant_cpu_to_le32(0));
- }
-
- LPCSCARD_IO_REQUEST pioSendPci;
- if (dwActiveProtocol == SCARD_PROTOCOL_T0)
- pioSendPci = SCARD_PCI_T0;
- else
- pioSendPci = SCARD_PCI_T1;
- int pcsc_result = SCardTransmit(hcard, pioSendPci, abDataIn,
- __le32_to_cpu(request.dwLength), NULL, *abDataOut, &dwRecvLength);
-
- return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
- pcsc_result, __cpu_to_le32(dwRecvLength));
-}
-
-
-RDR_to_PC_Parameters_t
-get_RDR_to_PC_Parameters(__u8 bSlot, __u8 bSeq, LONG pcsc_result, __u8
- **abProtocolDataStructure)
-{
- RDR_to_PC_Parameters_t result;
-
- result.bMessageType = 0x82;
- result.bSlot = bSlot;
- result.bSeq = bSeq;
- if (pcsc_result == SCARD_S_SUCCESS) {
- if (dwActiveProtocol == SCARD_PROTOCOL_T0) {
- result.bProtocolNum = 0;
- *abProtocolDataStructure = (__u8 *) malloc(sizeof
- (abProtocolDataStructure_T0_t));
- if (*abProtocolDataStructure) {
- fprintf (stderr, "T0\n");
- result.dwLength = __constant_cpu_to_le32(sizeof
- (abProtocolDataStructure_T0_t));
- abProtocolDataStructure_T0_t * t0 =
- *(abProtocolDataStructure_T0_t**) abProtocolDataStructure;
- /* values taken from ISO 7816-3 defaults
- * FIXME analyze ATR to get values */
- t0->bmFindexDindex = 1<<4| // index to table 7 ISO 7816-3 (Fi)
- 1; // index to table 8 ISO 7816-3 (Di)
- t0->bmTCCKST0 = 0<<1; // convention (direct)
- t0->bGuardTimeT0 = 0xFF;
- t0->bWaitingIntegerT0 = 0x10;
- t0->bClockStop = 0; // (not allowed)
- } else {
- // error malloc
- result.dwLength = __constant_cpu_to_le32(0);
- *abProtocolDataStructure = NULL;
- pcsc_result = SCARD_E_INSUFFICIENT_BUFFER;
- }
- } else {
- result.bProtocolNum = 1;
- *abProtocolDataStructure = (__u8 *) malloc(sizeof
- (abProtocolDataStructure_T1_t));
- if (*abProtocolDataStructure) {
- fprintf (stderr, "T1\n");
- result.dwLength = __constant_cpu_to_le32(sizeof
- (abProtocolDataStructure_T1_t));
- abProtocolDataStructure_T1_t * t1 =
- *(abProtocolDataStructure_T1_t**) abProtocolDataStructure;
- /* values taken from OpenPGP-card
- * FIXME analyze ATR to get values */
- t1->bmFindexDindex = 1<<4| // index to table 7 ISO 7816-3 (Fi)
- 3; // index to table 8 ISO 7816-3 (Di)
- t1->bmTCCKST1 = 0| // checksum type (CRC)
- 0<<1| // convention (direct)
- 0x10;
- t1->bGuardTimeT1 = 0xFF;
- t1->bWaitingIntegersT1 = 4<<4| // BWI
- 5; // CWI
- t1->bClockStop = 0; // (not allowed)
- t1->bIFSC = 0x80;
- t1->bNadValue = 0; // see 7816-3 9.4.2.1 (only default value)
- } else {
- // error malloc
- result.dwLength = __constant_cpu_to_le32(0);
- *abProtocolDataStructure = NULL;
- pcsc_result = SCARD_E_INSUFFICIENT_BUFFER;
- }
- }
- } else {
- result.dwLength = __constant_cpu_to_le32(0);
- *abProtocolDataStructure = NULL;
- }
- result.bStatus = get_bStatus(pcsc_result);
- result.bError = get_bError(pcsc_result);
-
- return result;
-}
-
-RDR_to_PC_Parameters_t
-perform_PC_to_RDR_GetParamters(const PC_to_RDR_GetParameters_t request,
- __u8** abProtocolDataStructure)
-{
- if ( request.bMessageType != 0x6C ||
- request.dwLength != __constant_cpu_to_le32(0) ||
- request.bSlot != 0)
- fprintf(stderr, "warning: malformed PC_to_RDR_GetParamters\n");
-
- LONG pcsc_result = SCardReconnect(hcard, SCARD_SHARE_EXCLUSIVE,
- SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD,
- &dwActiveProtocol);
-
- return get_RDR_to_PC_Parameters(request.bSlot, request.bSeq,
- pcsc_result, abProtocolDataStructure);
-}
-
-RDR_to_PC_DataBlock_t
-perform_PC_to_RDR_Secure(const PC_to_RDR_Secure_t request,
- const __u8* abData, __u8** abDataOut)
-{
- /* only short APDUs supported so Lc is always the fiths byte */
- if ( request.bMessageType != 0x69 ||
- request.bSlot != 0)
- fprintf(stderr, "warning: malformed PC_to_RDR_Secure\n");
-
- if (request.wLevelParameter != __constant_cpu_to_le16(0)) {
- fprintf(stderr, "warning: Only APDUs, that begin and end with this command are supported.\n");
- return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
- SCARD_E_READER_UNSUPPORTED, __constant_cpu_to_le32(0));
- }
-
- printf(":");
-
- __u8 PINMin, PINMax, bmPINLengthFormat, bmPINBlockString, bmFormatString;
- __u8 *abPINApdu;
- uint32_t apdulen;
- abPINDataStucture_Verification_t *verify = NULL;
- abPINDataStucture_Modification_t *modify = NULL;
- switch (*abData) { // first byte of abData is bPINOperation
- case 0x00:
- // PIN Verification
- verify = (abPINDataStucture_Verification_t *)
- (abData + sizeof(__u8));
- PINMin = verify->wPINMaxExtraDigit >> 8;
- PINMax = verify->wPINMaxExtraDigit & 0x00ff;
- bmPINLengthFormat = verify->bmPINLengthFormat;
- bmPINBlockString = verify->bmPINBlockString;
- bmFormatString = verify->bmFormatString;
- abPINApdu = (__u8*) verify + sizeof(*verify);
- apdulen = __le32_to_cpu(request.dwLength) - sizeof(*verify) - sizeof(__u8);
- break;
- case 0x01:
- // PIN Modification
- modify = (abPINDataStucture_Modification_t *)
- (abData + sizeof(__u8));
- PINMin = modify->wPINMaxExtraDigit >> 8;
- PINMax = modify->wPINMaxExtraDigit & 0x00ff;
- bmPINLengthFormat = modify->bmPINLengthFormat;
- bmPINBlockString = modify->bmPINBlockString;
- bmFormatString = modify->bmFormatString;
- abPINApdu = (__u8*) modify + sizeof(*modify);
- apdulen = __le32_to_cpu(request.dwLength) - sizeof(*modify) - sizeof(__u8);
- break;
- case 0x04:
- // Cancel PIN function
- default:
- fprintf(stderr, "warning: unknown pin operation\n");
- return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
- SCARD_E_READER_UNSUPPORTED, __constant_cpu_to_le32(0));
- }
-
- // copy the apdu
- __u8 *apdu = (__u8*) malloc(apdulen);
- if (!apdu) {
- return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
- SCARD_E_NO_MEMORY, __constant_cpu_to_le32(0));
- }
- memcpy(apdu, abPINApdu, apdulen);
-
- // TODO
- char *pin = "1234";
-
- __u8 *p;
- /* if system units are bytes or bits */
- uint8_t bytes = bmFormatString >> 7;
- /* PIN position after format in the APDU command (relative to the first
- * data after Lc). The position is based on the system units’ type
- * indicator (maximum1111 for fifteen system units */
- uint8_t pos = (bmFormatString >> 3) & 0xf;
- /* Right or left justify data */
- uint8_t right = (bmFormatString >> 2) & 1;
- /* Bit wise for the PIN format type */
- uint8_t type = bmFormatString & 2;
-
- uint8_t pinlen = strnlen(pin, PINMax + 1);
- if (pinlen > PINMax) {
- fprintf(stderr, "warning: PIN was too long, "
- "should be between %d and %d\n", PINMin, PINMax);
- return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
- SCARD_F_INTERNAL_ERROR, __constant_cpu_to_le32(0));
- }
-
- /* Size in bits of the PIN length inserted in the APDU command. */
- uint8_t lenlen = bmPINBlockString >> 4;
- /* PIN block size in bytes after justification and formatting. */
- uint8_t blocksize = bmPINBlockString & 0xf;
- /* PIN length position in the APDU command */
- uint8_t lenshift = bmPINLengthFormat & 0xf;
- if (lenlen) {
- /* write PIN Length */
- if (lenlen == 8) {
- if (bytes) {
- p = apdu + 5 + lenshift;
- } else {
- if (lenshift == 0)
- p = apdu + 5;
- if (lenshift == 8)
- p = apdu + 5 + 1;
- else {
- fprintf(stderr, "warning: PIN Block too complex, aborting\n");
- return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
- SCARD_F_INTERNAL_ERROR,
- __constant_cpu_to_le32(0));
- }
- }
- *p = pinlen;
- }
-
- fprintf(stderr, "warning: PIN Block too complex, aborting\n");
- return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
- SCARD_F_INTERNAL_ERROR, __constant_cpu_to_le32(0));
- }
-
- uint8_t justify;
- if (right)
- justify = blocksize - pinlen;
- else
- justify = 0;
-
- if (bytes) {
- p = apdu + 5 + pos + justify;
- } else {
- if (pos == 0)
- p = apdu + 5 + justify;
- else if (pos == 8)
- p = apdu + 5 + 1 + justify;
- else {
- fprintf(stderr, "warning: PIN Block too complex, aborting\n");
- return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
- SCARD_F_INTERNAL_ERROR,
- __constant_cpu_to_le32(0));
- }
- }
-
- while (*pin) {
- uint8_t c;
- switch (type) {
- case 0:
- // binary
- switch (*pin) {
- case '0':
- c = 0x00;
- break;
- case '1':
- c = 0x01;
- break;
- case '2':
- c = 0x02;
- break;
- case '3':
- c = 0x03;
- break;
- case '4':
- c = 0x04;
- break;
- case '5':
- c = 0x05;
- break;
- case '6':
- c = 0x06;
- break;
- case '7':
- c = 0x07;
- break;
- case '8':
- c = 0x08;
- break;
- case '9':
- c = 0x09;
- break;
- default:
- fprintf(stderr, "warning: PIN character %c not supported, aborting", *pin);
- return get_RDR_to_PC_DataBlock(request.bSlot,
- request.bSeq, SCARD_F_INTERNAL_ERROR,
- __constant_cpu_to_le32(0));
- }
- break;
- case 1:
- // BCD
- fprintf(stderr, "warning: BCD format not supported, aborting");
- return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
- SCARD_F_INTERNAL_ERROR, __constant_cpu_to_le32(0));
- case 2:
- // ASCII
- c = *pin;
- break;
- default:
- fprintf(stderr, "warning: unknown formatting, aborting");
- return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
- SCARD_F_INTERNAL_ERROR, __constant_cpu_to_le32(0));
- }
- *p = c;
- p++;
- pin++;
- }
-
-
- DWORD dwRecvLength = MAX_BUFFER_SIZE;
- *abDataOut = (__u8 *) malloc(dwRecvLength);
- if (*abDataOut == NULL) {
- return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
- SCARD_E_NO_MEMORY, __constant_cpu_to_le32(0));
- }
-
- LPCSCARD_IO_REQUEST pioSendPci;
- if (dwActiveProtocol == SCARD_PROTOCOL_T0)
- pioSendPci = SCARD_PCI_T0;
- else
- pioSendPci = SCARD_PCI_T1;
- int pcsc_result = SCardTransmit(hcard, pioSendPci, apdu, apdulen, NULL,
- *abDataOut, &dwRecvLength);
-
- return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
- pcsc_result, __cpu_to_le32(dwRecvLength));
-}
-
-RDR_to_PC_NotifySlotChange_t
-get_RDR_to_PC_NotifySlotChange ()
-{
- RDR_to_PC_NotifySlotChange_t result;
- result.bMessageType = 0x50;
- result.bmSlotICCState = 0; // no change
-
- DWORD current = rstate.dwEventState;
- if (SCARD_S_SUCCESS != SCardGetStatusChange(hcontext, 1, &rstate, 1)) {
- fprintf(stderr, "state changed: error\n");
- result.bmSlotICCState = 2; // changed (error)
- } else if (!(current & rstate.dwEventState)) {
- fprintf(stderr, "state changed\n");
- result.bmSlotICCState = 2; // changed
- }
-
- return result;
-}
-
-RDR_to_PC_SlotStatus_t
-perform_unknown(const PC_to_RDR_GetSlotStatus_t request)
-{
- RDR_to_PC_SlotStatus_t result;
- switch (request.bMessageType) {
- case 0x62:
- case 0x6F:
- case 0x69:
- result.bMessageType = 0x80;
- break;
- case 0x63:
- case 0x65:
- case 0x6E:
- case 0x6A:
- case 0x71:
- case 0x72:
- result.bMessageType = 0x81;
- break;
- case 0x61:
- case 0x6C:
- case 0x6D:
- result.bMessageType = 0x82;
- break;
- case 0x6B:
- result.bMessageType = 0x83;
- break;
- case 0x73:
- result.bMessageType = 0x84;
- break;
- default:
- fprintf(stderr, "unknown message type\n");
- result.bMessageType = 0;
- }
- result.dwLength = __constant_cpu_to_le32(0);
- result.bSlot = request.bSlot,
- result.bSeq = request.bSeq;
- result.bStatus = get_bStatus(SCARD_F_UNKNOWN_ERROR);
- result.bError = 0;
- result.bClockStatus = 0;
-
- return result;
-}
-
-int parse_ccid(const __u8* inbuf, __u8** outbuf) {
- if (inbuf == NULL)
- return 0;
- int result = -1;
- if (SCardIsValidContext(hcontext) != SCARD_S_SUCCESS) {
- if (perform_initialization(reader_num) == NULL)
- goto error;
- }
-
- switch (*inbuf) {
- case 0x62:
- { fprintf(stderr, "PC_to_RDR_IccPowerOn\n");
-
- char* atr;
- PC_to_RDR_IccPowerOn_t input =
- *(PC_to_RDR_IccPowerOn_t*) inbuf;
- RDR_to_PC_SlotStatus_t output =
- perform_PC_to_RDR_IccPowerOn(input, &atr);
-
- result = sizeof output + __le32_to_cpu(output.dwLength);
- *outbuf = realloc(*outbuf, result);
- if (*outbuf == NULL) {
- result = -1;
- break;
- }
-
- memcpy(*outbuf, &output, sizeof output);
- memcpy(*outbuf + sizeof output, atr,
- __le32_to_cpu(output.dwLength));
- } break;
-
- case 0x63:
- { fprintf(stderr, "PC_to_RDR_IccPowerOff\n");
-
- PC_to_RDR_IccPowerOff_t input =
- *(PC_to_RDR_IccPowerOff_t*) inbuf;
- RDR_to_PC_SlotStatus_t output =
- perform_PC_to_RDR_IccPowerOff(input);
-
- result = sizeof output;
- *outbuf = realloc(*outbuf, result);
- if (*outbuf == NULL) {
- result = -1;
- break;
- }
-
- memcpy(*outbuf, &output, sizeof output);
- } break;
-
- case 0x65:
- { /*fprintf(stderr, "PC_to_RDR_GetSlotStatus\n");*/
-
- PC_to_RDR_GetSlotStatus_t input =
- *(PC_to_RDR_GetSlotStatus_t*) inbuf;
- RDR_to_PC_SlotStatus_t output =
- perform_PC_to_RDR_GetSlotStatus(input);
-
- result = sizeof output;
- *outbuf = realloc(*outbuf, result);
- if (*outbuf == NULL) {
- result = -1;
- break;
- }
-
- memcpy(*outbuf, &output, sizeof output);
- } break;
-
- case 0x6F:
- { fprintf(stderr, "PC_to_RDR_XfrBlock\n");
-
- __u8* rapdu;
- PC_to_RDR_XfrBlock_t input = *(PC_to_RDR_XfrBlock_t*) inbuf;
- RDR_to_PC_DataBlock_t output =
- perform_PC_to_RDR_XfrBlock(input, inbuf + sizeof input,
- &rapdu);
-
- result = sizeof output + __le32_to_cpu(output.dwLength);
- *outbuf = realloc(*outbuf, result);
- if (*outbuf == NULL) {
- free(rapdu);
- result = -1;
- break;
- }
-
- memcpy(*outbuf, &output, sizeof output);
- memcpy(*outbuf + sizeof output, rapdu,
- __le32_to_cpu(output.dwLength));
- free(rapdu);
- } break;
-
- case 0x6C:
- { fprintf(stderr, "PC_to_RDR_GetParameters\n");
-
- __u8* abProtocolDataStructure;
- PC_to_RDR_GetParameters_t input = *(PC_to_RDR_GetParameters_t*)
- inbuf;
- RDR_to_PC_Parameters_t output = perform_PC_to_RDR_GetParamters(
- input, &abProtocolDataStructure );
-
- result = sizeof output + __le32_to_cpu(output.dwLength);
- *outbuf = realloc(*outbuf, result);
- if (*outbuf == NULL) {
- if (abProtocolDataStructure)
- free(abProtocolDataStructure);
- result = -1;
- break;
- }
-
- memcpy(*outbuf, &output, sizeof output);
- memcpy(*outbuf + sizeof output, abProtocolDataStructure,
- __le32_to_cpu(output.dwLength));
- if (abProtocolDataStructure)
- free(abProtocolDataStructure);
- } break;
-
- case 0x69:
- { fprintf(stderr, "PC_to_RDR_Secure\n");
-
- __u8* rapdu;
- PC_to_RDR_Secure_t input = *(PC_to_RDR_Secure_t *) inbuf;
- RDR_to_PC_DataBlock_t output =
- perform_PC_to_RDR_Secure(input, inbuf + sizeof input,
- &rapdu);
-
- result = sizeof output + __le32_to_cpu(output.dwLength);
- *outbuf = realloc(*outbuf, result);
- if (*outbuf == NULL) {
- free(rapdu);
- result = -1;
- break;
- }
-
- memcpy(*outbuf, &output, sizeof output);
- memcpy(*outbuf + sizeof output, rapdu,
- __le32_to_cpu(output.dwLength));
- free(rapdu);
- } break;
-
- default:
-error:
- { fprintf(stderr, "unknown ccid command: 0x%4X\n", *inbuf);
-
- PC_to_RDR_GetSlotStatus_t input =
- *(PC_to_RDR_GetSlotStatus_t*) inbuf;
- RDR_to_PC_SlotStatus_t output =
- perform_unknown(input);
-
- result = sizeof output;
- *outbuf = realloc(*outbuf, result);
- if (*outbuf == NULL) {
- result = -1;
- break;
- }
-
- memcpy(*outbuf, &output, sizeof output);
- }
- }
-
- return result;
-}
-
-int parse_ccid_control(struct usb_ctrlrequest *setup, __u8 **outbuf) {
- int result = -1;
- __u16 value, index, length;
-
- value = __le16_to_cpu(setup->wValue);
- index = __le16_to_cpu(setup->wIndex);
- length = __le16_to_cpu(setup->wLength);
- if (setup->bRequestType == USB_REQ_CCID)
- switch(setup->bRequest) {
- case CCID_CONTROL_ABORT:
- {
- fprintf(stderr, "ABORT\n");
- if (length != 0x00) {
- fprintf(stderr, "warning: malformed ABORT\n");
- }
- result = SCardCancel(hcontext);
- if (result != SCARD_S_SUCCESS)
- fprintf(stderr, "pc/sc error: %s\n",
- pcsc_stringify_error(result));
- result = 0;
- } break;
- case CCID_CONTROL_GET_CLOCK_FREQUENCIES:
- {
- fprintf(stderr, "GET_CLOCK_FREQUENCIES\n");
- if (value != 0x00) {
- fprintf(stderr,
- "warning: malformed GET_CLOCK_FREQUENCIES\n");
- }
-
- result = sizeof(__le32);
- *outbuf = realloc(*outbuf, result);
- if (*outbuf == NULL) {
- result = -1;
- break;
- }
- __le32 clock = ccid_desc.dwDefaultClock;
- memcpy(*outbuf, &clock, sizeof (__le32));
- } break;
- case CCID_CONTROL_GET_DATA_RATES:
- {
- fprintf(stderr, "GET_DATA_RATES\n");
- if (value != 0x00) {
- fprintf(stderr, "warning: malformed GET_DATA_RATES\n");
- }
-
- result = sizeof (__le32);
- *outbuf = realloc(*outbuf, result);
- if (*outbuf == NULL) {
- result = -1;
- break;
- }
- __le32 drate = ccid_desc.dwDataRate;
- memcpy(*outbuf, &drate, sizeof (__le32));
- } break;
- default:
- printf("unknown status setup->bRequest == %d", setup->bRequest);
- }
-
- return result;
-}
-
+#endif
+#endif
diff --git a/ccid/usb.c b/ccid/usb.c
new file mode 100644
index 0000000..5dce34d
--- /dev/null
+++ b/ccid/usb.c
@@ -0,0 +1,1792 @@
+/*
+ * Copyright (C) 2009 Frank Morgner
+ *
+ * This file is part of ccid.
+ *
+ * ccid is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * ccid is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ccid. If not, see .
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+//#include
+
+#ifdef AIO
+/* this aio code works with libaio-0.3.106 */
+#include
+#endif
+
+#include "usbstring.h"
+#include "ccid.h"
+
+
+#define DRIVER_VENDOR_NUM 0x0D46 /* KOBIL Systems */
+#define DRIVER_ISO_PRODUCT_NUM 0x3010 /* KOBIL Class 3 Reader */
+#define DRIVER_PRODUCT_NUM 0x3010 /* KOBIL Class 3 Reader */
+static int vendorid = DRIVER_VENDOR_NUM;
+static int productid = DRIVER_PRODUCT_NUM;
+static int verbose = 0;
+static int debug = 0;
+static int dohid = 0;
+static int doint = 0;
+char *usb_reader_name = NULL;
+int usb_reader_num = 0;
+
+/* NOTE: these IDs don't imply endpoint numbering; host side drivers
+ * should use endpoint descriptors, or perhaps bcdDevice, to configure
+ * such things. Other product IDs could have different policies.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* these descriptors are modified based on what controller we find */
+
+#define STRINGID_MFGR 1
+#define STRINGID_PRODUCT 2
+#define STRINGID_SERIAL 3
+#define STRINGID_CONFIG 4
+#define STRINGID_INTERFACE 5
+#define STRINGID_HID_INTERFACE 6
+
+static struct usb_device_descriptor
+device_desc = {
+ .bLength = sizeof device_desc,
+
+ //.bcdUSB = __constant_cpu_to_le16 (0x0110),
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = __constant_cpu_to_le16 (0x0200),
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ // .bMaxPacketSize0 ... set by gadgetfs
+ .idVendor = __constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),
+ .iManufacturer = STRINGID_MFGR,
+ .iProduct = STRINGID_PRODUCT,
+ .iSerialNumber = STRINGID_SERIAL,
+ .bNumConfigurations = 1,
+};
+
+#define MAX_USB_POWER 1
+
+#define CONFIG_VALUE 3
+
+static struct usb_config_descriptor
+config = {
+ .bLength = sizeof config,
+ .bDescriptorType = USB_DT_CONFIG,
+
+ /* must compute wTotalLength ... */
+ .bNumInterfaces = 1,
+ .bConfigurationValue = CONFIG_VALUE,
+ .iConfiguration = STRINGID_CONFIG,
+ .bmAttributes = USB_CONFIG_ATT_ONE
+ | USB_CONFIG_ATT_SELFPOWER,
+ .bMaxPower = (MAX_USB_POWER + 1) / 2,
+};
+
+static struct usb_interface_descriptor
+source_sink_intf = {
+ .bLength = sizeof source_sink_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bInterfaceClass = USB_CLASS_CSCID,
+ .iInterface = STRINGID_INTERFACE,
+};
+static struct hid_class_descriptor
+hid_desc = {
+ .bLength = sizeof hid_desc,
+ .bDescriptorType = 0x21,
+ .bcdHID = __constant_cpu_to_le16(0x101),
+ .bCountryCode = 0,
+ .bNumDescriptors = 0,
+};
+
+/* Full speed configurations are used for full-speed only devices as
+ * well as dual-speed ones (the only kind with high speed support).
+ */
+
+static struct usb_endpoint_descriptor
+fs_source_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ /* NOTE some controllers may need FS bulk max packet size
+ * to be smaller. it would be a chip-specific option.
+ */
+ .wMaxPacketSize = __constant_cpu_to_le16 (64),
+};
+
+static struct usb_endpoint_descriptor
+fs_sink_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16 (64),
+};
+
+/* some devices can handle other result packet sizes */
+/*#define STATUS_MAXPACKET 16*/
+/*#define LOG2_STATUS_POLL_MSEC 5*/
+#define STATUS_MAXPACKET 8
+#define LOG2_STATUS_POLL_MSEC 3
+
+static struct usb_endpoint_descriptor
+fs_status_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_MAXPACKET),
+ .bInterval = (1 << LOG2_STATUS_POLL_MSEC),
+};
+
+static struct usb_endpoint_descriptor
+fs_hid_status_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_MAXPACKET),
+ .bInterval = (1 << LOG2_STATUS_POLL_MSEC),
+};
+
+static const struct usb_endpoint_descriptor *fs_eps [3] = {
+ &fs_source_desc,
+ &fs_sink_desc,
+ &fs_status_desc,
+};
+
+
+/* High speed configurations are used only in addition to a full-speed
+ * ones ... since all high speed devices support full speed configs.
+ * Of course, not all hardware supports high speed configurations.
+ */
+
+static struct usb_endpoint_descriptor
+hs_source_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16 (512),
+};
+
+static struct usb_endpoint_descriptor
+hs_sink_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16 (512),
+ .bInterval = 1,
+};
+
+static struct usb_endpoint_descriptor
+hs_status_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_MAXPACKET),
+ .bInterval = LOG2_STATUS_POLL_MSEC + 3,
+};
+
+static struct usb_endpoint_descriptor
+hs_hid_status_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_MAXPACKET),
+ .bInterval = LOG2_STATUS_POLL_MSEC + 3,
+};
+
+static const struct usb_endpoint_descriptor *hs_eps [] = {
+ &hs_source_desc,
+ &hs_sink_desc,
+ &hs_status_desc,
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* 56 is the maximum for the KOBIL Class 3 Reader */
+static char serial [57];
+
+static struct usb_string stringtab [] = {
+ { STRINGID_MFGR, "morgner@informatik.hu-berlin.de", },
+ { STRINGID_PRODUCT, "CCID to PCSC Gadget", },
+ { STRINGID_SERIAL, serial, },
+ { STRINGID_CONFIG, "The Configuration", },
+ { STRINGID_INTERFACE, "CCID to PCSC", },
+ { STRINGID_HID_INTERFACE, "blakeks", },
+};
+
+static struct usb_gadget_strings strings = {
+ .language = 0x0409, /* "en-us" */
+ .strings = stringtab,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* kernel drivers could autoconfigure like this too ... if
+ * they were willing to waste the relevant code/data space.
+ */
+
+static int HIGHSPEED;
+static char *DEVNAME;
+static char *EP_IN_NAME, *EP_OUT_NAME, *EP_STATUS_NAME, *EP_HID_STATUS_NAME;
+
+/* gadgetfs currently has no chunking (or O_DIRECT/zerocopy) support
+ * to turn big requests into lots of smaller ones; so this is "small".
+ */
+#define USB_BUFSIZE (7 * 1024)
+
+static enum usb_device_speed current_speed;
+
+static inline int min(unsigned a, unsigned b)
+{
+ return (a < b) ? a : b;
+}
+
+static int autoconfig ()
+{
+ struct stat statb;
+
+ /* NetChip 2280 PCI device or dummy_hcd, high/full speed */
+ if (stat (DEVNAME = "net2280", &statb) == 0 ||
+ stat (DEVNAME = "dummy_udc", &statb) == 0) {
+ HIGHSPEED = 1;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0100),
+
+ fs_source_desc.bEndpointAddress
+ = hs_source_desc.bEndpointAddress
+ = USB_DIR_IN | 7;
+ EP_IN_NAME = "ep-a";
+ fs_sink_desc.bEndpointAddress = hs_sink_desc.bEndpointAddress
+ = USB_DIR_OUT | 3;
+ EP_OUT_NAME = "ep-b";
+
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress
+ = hs_status_desc.bEndpointAddress
+ = USB_DIR_IN | 11;
+ EP_STATUS_NAME = "ep-f";
+
+ if (dohid) {
+ // TODO EP_HID_STATUS_NAME?
+ EP_HID_STATUS_NAME = "ep-e";
+ fs_hid_status_desc.bEndpointAddress
+ = hs_hid_status_desc.bEndpointAddress
+ = USB_DIR_IN | 10;
+ }
+
+ /* Intel PXA 2xx processor, full speed only */
+ } else if (stat (DEVNAME = "pxa2xx_udc", &statb) == 0) {
+ HIGHSPEED = 0;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0101),
+
+ fs_source_desc.bEndpointAddress = USB_DIR_IN | 6;
+ EP_IN_NAME = "ep6in-bulk";
+ fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 7;
+ EP_OUT_NAME = "ep7out-bulk";
+
+ /* using bulk for this since the pxa interrupt endpoints
+ * always use the no-toggle scheme (discouraged).
+ */
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress = USB_DIR_IN | 11;
+ EP_STATUS_NAME = "ep11in-bulk";
+
+ if (dohid) {
+ EP_HID_STATUS_NAME = "ep12in-bulk";
+ fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 12;
+ }
+#if 0
+ /* AMD au1x00 processor, full speed only */
+ } else if (stat (DEVNAME = "au1x00_udc", &statb) == 0) {
+ HIGHSPEED = 0;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0102),
+
+ fs_source_desc.bEndpointAddress = USB_DIR_IN | 2;
+ EP_IN_NAME = "ep2in";
+ fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 4;
+ EP_OUT_NAME = "ep4out";
+
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
+ EP_STATUS_NAME = "ep3in";
+
+ /* Intel SA-1100 processor, full speed only */
+ } else if (stat (DEVNAME = "sa1100", &statb) == 0) {
+ HIGHSPEED = 0;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0103),
+
+ fs_source_desc.bEndpointAddress = USB_DIR_IN | 2;
+ EP_IN_NAME = "ep2in-bulk";
+ fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 1;
+ EP_OUT_NAME = "ep1out-bulk";
+
+ source_sink_intf.bNumEndpoints = 2;
+ ccid_desc.dwFeatures &= ~0x100000; // USB Wake up signaling not supported
+ EP_STATUS_NAME = 0;
+#endif
+
+ /* Toshiba TC86c001 PCI device, full speed only */
+ } else if (stat (DEVNAME = "goku_udc", &statb) == 0) {
+ HIGHSPEED = 0;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0104),
+
+ fs_source_desc.bEndpointAddress = USB_DIR_IN | 2;
+ EP_IN_NAME = "ep2-bulk";
+ fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 1;
+ EP_OUT_NAME = "ep1-bulk";
+
+ if (dohid) {
+ source_sink_intf.bNumEndpoints = 2;
+ // USB Wake up signaling not supported
+ ccid_desc.dwFeatures &= ~__constant_cpu_to_le32(0x100000);
+
+ EP_HID_STATUS_NAME = "ep3-bulk";
+ fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 3;
+ } else {
+ source_sink_intf.bNumEndpoints = 3;
+
+ EP_STATUS_NAME = "ep3-bulk";
+ fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
+ }
+
+ /* Samsung S3C24xx series, full speed only */
+ } else if (stat (DEVNAME = "s3c2410_udc", &statb) == 0) {
+ HIGHSPEED = 0;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0110),
+
+ fs_source_desc.bEndpointAddress = USB_DIR_IN | 2;
+ EP_IN_NAME = "ep2-bulk";
+ fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 1;
+ EP_OUT_NAME = "ep1-bulk";
+
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
+ EP_STATUS_NAME = "ep3-bulk";
+
+ if (dohid) {
+ EP_HID_STATUS_NAME = "ep4-bulk";
+ fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 4;
+ }
+
+ /* Renesas SH77xx processors, full speed only */
+ } else if (stat (DEVNAME = "sh_udc", &statb) == 0) {
+ HIGHSPEED = 0;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0105),
+
+ fs_source_desc.bEndpointAddress = USB_DIR_IN | 2;
+ EP_IN_NAME = "ep2in-bulk";
+ fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 1;
+ EP_OUT_NAME = "ep1out-bulk";
+
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
+ EP_STATUS_NAME = "ep3in-bulk";
+
+ if (dohid) {
+ EP_HID_STATUS_NAME = "ep4in-bulk";
+ fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 4;
+ }
+
+ /* OMAP 1610 and newer devices, full speed only, fifo mode 0 or 3 */
+ } else if (stat (DEVNAME = "omap_udc", &statb) == 0) {
+ HIGHSPEED = 0;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0106),
+
+ fs_source_desc.bEndpointAddress = USB_DIR_IN | 1;
+ EP_IN_NAME = "ep1in-bulk";
+ fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 2;
+ EP_OUT_NAME = "ep2out-bulk";
+
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
+ EP_STATUS_NAME = "ep3in-int";
+
+ if (dohid) {
+ EP_HID_STATUS_NAME = "ep4in-bulk";
+ fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 4;
+ }
+
+ /* Something based on Mentor USB Highspeed Dual-Role Controller */
+ } else if (stat (DEVNAME = "musb_hdrc", &statb) == 0) {
+ HIGHSPEED = 1;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0107),
+
+ fs_source_desc.bEndpointAddress
+ = hs_source_desc.bEndpointAddress
+ = USB_DIR_IN | 1;
+ EP_IN_NAME = "ep1in";
+ fs_sink_desc.bEndpointAddress = hs_sink_desc.bEndpointAddress
+ = USB_DIR_OUT | 1;
+ EP_OUT_NAME = "ep1out";
+
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress
+ = hs_status_desc.bEndpointAddress
+ = USB_DIR_IN | 3;
+ EP_STATUS_NAME = "ep3";
+
+ /* Atmel AT91 processors, full speed only */
+ } else if (stat (DEVNAME = "at91_udc", &statb) == 0) {
+ HIGHSPEED = 0;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0106),
+
+ fs_source_desc.bEndpointAddress = USB_DIR_IN | 1;
+ EP_IN_NAME = "ep1";
+ fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 2;
+ EP_OUT_NAME = "ep2";
+
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
+ EP_STATUS_NAME = "ep3-int";
+
+ if (dohid) {
+ EP_HID_STATUS_NAME = "ep4";
+ fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 4;
+ }
+
+ /* Sharp LH740x processors, full speed only */
+ } else if (stat (DEVNAME = "lh740x_udc", &statb) == 0) {
+ HIGHSPEED = 0;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0106),
+
+ fs_source_desc.bEndpointAddress = USB_DIR_IN | 1;
+ EP_IN_NAME = "ep1in-bulk";
+ fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 2;
+ EP_OUT_NAME = "ep2out-bulk";
+
+ if (dohid) {
+ source_sink_intf.bNumEndpoints = 2;
+ // USB Wake up signaling not supported
+ ccid_desc.dwFeatures &= ~__constant_cpu_to_le32(0x100000);
+
+ fs_hid_status_desc.bEndpointAddress = USB_DIR_IN | 3;
+ EP_HID_STATUS_NAME = "ep3in-int";
+ } else {
+ source_sink_intf.bNumEndpoints = 3;
+
+ fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
+ EP_STATUS_NAME = "ep3in-int";
+ }
+
+ /* Atmel AT32AP700x processors, high/full speed */
+ } else if (stat (DEVNAME = "atmel_usba_udc", &statb) == 0) {
+ HIGHSPEED = 1;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0108);
+
+ fs_source_desc.bEndpointAddress
+ = hs_source_desc.bEndpointAddress
+ = USB_DIR_IN | 1;
+ EP_IN_NAME = "ep1in-bulk";
+ fs_sink_desc.bEndpointAddress
+ = hs_sink_desc.bEndpointAddress
+ = USB_DIR_OUT | 2;
+ EP_OUT_NAME = "ep2out-bulk";
+
+ if (dohid) {
+ source_sink_intf.bNumEndpoints = 2;
+ // USB Wake up signaling not supported
+ ccid_desc.dwFeatures &= ~__constant_cpu_to_le32(0x100000);
+
+ fs_hid_status_desc.bEndpointAddress
+ = hs_hid_status_desc.bEndpointAddress
+ = USB_DIR_IN | 3;
+ EP_HID_STATUS_NAME = "ep3in-int";
+ } else {
+ source_sink_intf.bNumEndpoints = 3;
+
+ fs_status_desc.bEndpointAddress
+ = hs_status_desc.bEndpointAddress
+ = USB_DIR_IN | 3;
+ EP_STATUS_NAME = "ep3in-int";
+ }
+
+ } else {
+ DEVNAME = 0;
+ return -ENODEV;
+ }
+ if (!doint) {
+ source_sink_intf.bNumEndpoints = 2;
+ // USB Wake up signaling not supported
+ ccid_desc.dwFeatures &= ~__constant_cpu_to_le32(0x100000);
+ }
+ return 0;
+}
+
+#ifdef AIO
+
+static int iso;
+static int interval;
+static unsigned iosize;
+static unsigned bufsize = USB_BUFSIZE;
+
+/* This is almost the only place where usb needs to know whether we're
+ * driving an isochronous stream or a bulk one.
+ */
+static int iso_autoconfig ()
+{
+ struct stat statb;
+
+ /* ISO endpoints "must not be part of a default interface setting".
+ * Never do it like this in "real" code! This uses the default
+ * setting (alt 0) because it's the only one pxa supports.
+ *
+ * This code doesn't adjust the sample rate based on feedback.
+ */
+ device_desc.idProduct = __constant_cpu_to_le16(DRIVER_ISO_PRODUCT_NUM);
+
+ /* NetChip 2280 PCI device or dummy_hcd, high/full speed */
+ if (stat (DEVNAME = "net2280", &statb) == 0 ||
+ stat (DEVNAME = "dummy_udc", &statb) == 0) {
+ unsigned bInterval, wMaxPacketSize;
+
+ HIGHSPEED = 1;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0100);
+
+ /* this code won't use two or four uframe periods */
+ if (bufsize > 1024) {
+ interval = 0;
+ bInterval = 1;
+ /* "modprobe net2280 fifo_mode=1" may be needed */
+ if (bufsize > (2 * 1024)) {
+ wMaxPacketSize = min ((bufsize + 2)/3, 1024);
+ bufsize = min (3 * wMaxPacketSize, bufsize);
+ wMaxPacketSize |= 2 << 11;
+ } else {
+ wMaxPacketSize = min ((bufsize + 1)/2, 1024);
+ wMaxPacketSize |= 1 << 11;
+ }
+ } else {
+ bInterval = interval + 4;
+ wMaxPacketSize = bufsize;
+ }
+
+ fs_source_desc.bEndpointAddress
+ = hs_source_desc.bEndpointAddress
+ = USB_DIR_IN | 7;
+ fs_source_desc.bmAttributes
+ = hs_source_desc.bmAttributes
+ = USB_ENDPOINT_XFER_ISOC;
+ fs_source_desc.wMaxPacketSize = min (bufsize, 1023);
+ hs_source_desc.wMaxPacketSize = wMaxPacketSize;
+ fs_source_desc.bInterval = interval + 1;
+ hs_source_desc.bInterval = bInterval;
+ EP_IN_NAME = "ep-a";
+
+ fs_sink_desc.bEndpointAddress
+ = hs_sink_desc.bEndpointAddress
+ = USB_DIR_OUT | 3;
+ fs_sink_desc.bmAttributes
+ = hs_sink_desc.bmAttributes
+ = USB_ENDPOINT_XFER_ISOC;
+ fs_sink_desc.wMaxPacketSize = min (bufsize, 1023);
+ hs_sink_desc.wMaxPacketSize = wMaxPacketSize;
+ fs_sink_desc.bInterval = interval + 1;
+ hs_sink_desc.bInterval = bInterval;
+ EP_OUT_NAME = "ep-b";
+
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress
+ = hs_status_desc.bEndpointAddress
+ = USB_DIR_IN | 11;
+ EP_STATUS_NAME = "ep-f";
+
+ /* Intel PXA 2xx processor, full speed only */
+ } else if (stat (DEVNAME = "pxa2xx_udc", &statb) == 0) {
+ HIGHSPEED = 0;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0101),
+
+ bufsize = min (bufsize, 256);
+
+ fs_source_desc.bEndpointAddress = USB_DIR_IN | 3;
+ fs_source_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC;
+ fs_source_desc.wMaxPacketSize = bufsize;
+ fs_source_desc.bInterval = interval;
+ EP_IN_NAME = "ep3in-iso";
+
+ fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 4;
+ fs_sink_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC;
+ fs_sink_desc.wMaxPacketSize = bufsize;
+ fs_sink_desc.bInterval = interval;
+ EP_OUT_NAME = "ep4out-iso";
+
+ /* using bulk for this since the pxa interrupt endpoints
+ * always use the no-toggle scheme (discouraged).
+ */
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress = USB_DIR_IN | 11;
+ EP_STATUS_NAME = "ep11in-bulk";
+
+ /* OMAP 1610 and newer devices, full speed only, fifo mode 3 */
+ } else if (stat (DEVNAME = "omap_udc", &statb) == 0) {
+ HIGHSPEED = 0;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0102),
+
+ fs_source_desc.bEndpointAddress = USB_DIR_IN | 7;
+ fs_source_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC;
+ fs_source_desc.wMaxPacketSize = min (bufsize, 256);
+ fs_source_desc.bInterval = interval;
+ EP_IN_NAME = "ep7in-iso";
+
+ fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 8;
+ fs_sink_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC;
+ fs_sink_desc.wMaxPacketSize = min (bufsize, 256);
+ fs_sink_desc.bInterval = interval;
+ EP_OUT_NAME = "ep8out-iso";
+
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress = USB_DIR_IN | 9;
+ EP_STATUS_NAME = "ep9in-int";
+
+ /* Something based on Mentor USB Highspeed Dual-Role Controller;
+ * assumes a core that doesn't include high bandwidth support.
+ */
+ } else if (stat (DEVNAME = "musb_hdrc", &statb) == 0) {
+ unsigned bInterval, wMaxPacketSize;
+
+ HIGHSPEED = 1;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0103);
+
+ bInterval = interval + 4;
+ wMaxPacketSize = bufsize;
+
+ fs_source_desc.bEndpointAddress
+ = hs_source_desc.bEndpointAddress
+ = USB_DIR_IN | 1;
+ fs_source_desc.bmAttributes
+ = hs_source_desc.bmAttributes
+ = USB_ENDPOINT_XFER_ISOC;
+ fs_source_desc.wMaxPacketSize = min (bufsize, 1023);
+ hs_source_desc.wMaxPacketSize = wMaxPacketSize;
+ fs_source_desc.bInterval = interval + 1;
+ hs_source_desc.bInterval = bInterval;
+ EP_IN_NAME = "ep1in";
+
+ fs_sink_desc.bEndpointAddress
+ = hs_sink_desc.bEndpointAddress
+ = USB_DIR_OUT | 1;
+ fs_sink_desc.bmAttributes
+ = hs_sink_desc.bmAttributes
+ = USB_ENDPOINT_XFER_ISOC;
+ fs_sink_desc.wMaxPacketSize = min (bufsize, 1023);
+ hs_sink_desc.wMaxPacketSize = wMaxPacketSize;
+ fs_sink_desc.bInterval = interval + 1;
+ hs_sink_desc.bInterval = bInterval;
+ EP_OUT_NAME = "ep1out";
+
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress
+ = hs_status_desc.bEndpointAddress
+ = USB_DIR_IN | 11;
+ EP_STATUS_NAME = "ep3";
+
+ /* Atmel AT91 processors, full speed only */
+ } else if (stat (DEVNAME = "at91_udc", &statb) == 0) {
+ HIGHSPEED = 0;
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0104),
+
+ fs_source_desc.bEndpointAddress = USB_DIR_IN | 4;
+ fs_source_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC;
+ fs_source_desc.wMaxPacketSize = min (bufsize, 256);
+ fs_source_desc.bInterval = interval;
+ EP_IN_NAME = "ep4";
+
+ fs_sink_desc.bEndpointAddress = USB_DIR_OUT | 2;
+ fs_sink_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC;
+ fs_sink_desc.wMaxPacketSize = min (bufsize, 256);
+ fs_sink_desc.bInterval = interval;
+ EP_OUT_NAME = "ep5";
+
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress = USB_DIR_IN | 3;
+ EP_STATUS_NAME = "ep3-int";
+
+ /* Atmel AT32AP700x processors, high/full speed */
+ } else if (stat (DEVNAME = "atmel_usba_udc", &statb) == 0){
+ HIGHSPEED = 1;
+
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0105);
+
+ fs_source_desc.bEndpointAddress
+ = hs_source_desc.bEndpointAddress
+ = USB_DIR_IN | 5;
+ fs_source_desc.bmAttributes
+ = hs_source_desc.bmAttributes
+ = USB_ENDPOINT_XFER_ISOC;
+ fs_source_desc.wMaxPacketSize
+ = hs_source_desc.wMaxPacketSize
+ = __cpu_to_le16(min (bufsize, 1024));
+ fs_source_desc.bInterval
+ = hs_source_desc.bInterval
+ = interval;
+ EP_IN_NAME = "ep5in-iso";
+
+ fs_sink_desc.bEndpointAddress
+ = hs_sink_desc.bEndpointAddress
+ = USB_DIR_OUT | 6;
+ fs_sink_desc.bmAttributes
+ = hs_sink_desc.bmAttributes
+ = USB_ENDPOINT_XFER_ISOC;
+ fs_sink_desc.wMaxPacketSize
+ = hs_sink_desc.wMaxPacketSize
+ = __cpu_to_le16(min (bufsize, 1024));
+ fs_sink_desc.bInterval
+ = hs_sink_desc.bInterval
+ = interval;
+ EP_OUT_NAME = "ep6out-iso";
+
+ source_sink_intf.bNumEndpoints = 3;
+ fs_status_desc.bEndpointAddress
+ = hs_status_desc.bEndpointAddress
+ = USB_DIR_IN | 3;
+ EP_STATUS_NAME = "ep3in-int";
+
+ } else {
+ DEVNAME = 0;
+ return -ENODEV;
+ }
+ if (verbose) {
+ fprintf (stderr, "iso fs wMaxPacket %04x bInterval %02x\n",
+ __le16_to_cpu(fs_sink_desc.wMaxPacketSize),
+ fs_sink_desc.bInterval);
+ if (HIGHSPEED)
+ fprintf (stderr,
+ "iso hs wMaxPacket %04x bInterval %02x\n",
+ __le16_to_cpu(hs_sink_desc.wMaxPacketSize),
+ hs_sink_desc.bInterval);
+ }
+ return 0;
+}
+
+#else
+#define iso 0
+#endif /* AIO */
+
+/*-------------------------------------------------------------------------*/
+
+/* full duplex data, with at least three threads: ep0, sink, and source */
+
+static pthread_t ep0;
+
+static pthread_t ccid_thread;
+static pthread_t hidthread;
+static int source_fd = -1;
+static int sink_fd = -1;
+static int status_fd = -1;
+static int hidstatus_fd = -1;
+
+// FIXME no result i/o yet
+
+static void close_fd (void *fd_ptr)
+{
+ int result, fd;
+
+ fd = *(int *)fd_ptr;
+ *(int *)fd_ptr = -1;
+
+ /* test the FIFO ioctls (non-ep0 code paths) */
+ if (pthread_self () != ep0) {
+ result = ioctl (fd, GADGETFS_FIFO_STATUS);
+ if (result < 0) {
+ /* ENODEV reported after disconnect */
+ if (errno != ENODEV && errno != EOPNOTSUPP)
+ perror ("get fifo result");
+ } else {
+ fprintf (stderr, "fd %d, unclaimed = %d\n",
+ fd, result);
+ if (result) {
+ result = ioctl (fd, GADGETFS_FIFO_FLUSH);
+ if (result < 0)
+ perror ("fifo flush");
+ }
+ }
+ }
+
+ if (close (fd) < 0)
+ perror ("close fd");
+ else
+ fprintf (stderr, "closed fd\n");
+}
+
+
+/* you should be able to open and configure endpoints
+ * whether or not the host is connected
+ */
+static int
+ep_config (char *name, const char *label,
+ struct usb_endpoint_descriptor *fs,
+ struct usb_endpoint_descriptor *hs
+)
+{
+ int fd, result;
+ char buf [USB_BUFSIZE];
+
+ /* open and initialize with endpoint descriptor(s) */
+ fd = open (name, O_RDWR);
+ if (fd < 0) {
+ result = -errno;
+ fprintf (stderr, "%s open %s error %d (%s)\n",
+ label, name, errno, strerror (errno));
+ return result;
+ }
+
+ /* one (fs or ls) or two (fs + hs) sets of config descriptors */
+ *(__u32 *)buf = 1; /* tag for this format */
+ memcpy (buf + 4, fs, USB_DT_ENDPOINT_SIZE);
+ if (HIGHSPEED)
+ memcpy (buf + 4 + USB_DT_ENDPOINT_SIZE,
+ hs, USB_DT_ENDPOINT_SIZE);
+ result = write (fd, buf, 4 + USB_DT_ENDPOINT_SIZE
+ + (HIGHSPEED ? USB_DT_ENDPOINT_SIZE : 0));
+ if (result < 0) {
+ result = -errno;
+ fprintf (stderr, "%s config %s error %d (%s)\n",
+ label, name, errno, strerror (errno));
+ close (fd);
+ return result;
+ } else if (debug) {
+ unsigned long id;
+
+ id = pthread_self ();
+ fprintf (stderr, "%s (%ld): fd %d\n", label, id, fd);
+ }
+ return fd;
+}
+
+#define source_open(name) \
+ ep_config(name,__FUNCTION__, &fs_source_desc, &hs_source_desc)
+#define sink_open(name) \
+ ep_config(name,__FUNCTION__, &fs_sink_desc, &hs_sink_desc)
+#define status_open(name) \
+ ep_config(name,__FUNCTION__, &fs_status_desc, &hs_status_desc)
+#define hidstatus_open(name) \
+ ep_config(name,__FUNCTION__, &fs_hid_status_desc, &hs_hid_status_desc)
+
+/* ccid thread, forwards ccid requests to pcsc and returns results */
+static void *ccid (void *param)
+{
+
+ //int tmp;
+ pthread_t interrupt_thread;
+
+ void *interrupt (void *param)
+ {
+ status_fd = status_open (((char **) param)[0]);
+ if (source_fd < 0) {
+ /* an error concerning status endpoint is not fatal for in/out bulk*/
+ /* transfer */
+ if (debug)
+ perror("status_fd");
+ pthread_exit(0);
+ }
+ pthread_cleanup_push (close_fd, &status_fd);
+
+ int result;
+ RDR_to_PC_NotifySlotChange_t slotchange;
+ do {
+
+ /* original LinuxThreads cancelation didn't work right */
+ /* so test for it explicitly. */
+ pthread_testcancel ();
+
+ slotchange = get_RDR_to_PC_NotifySlotChange();
+ if (slotchange.bmSlotICCState) {
+ /* don't bother host, when nothing changed */
+ if (debug)
+ fprintf(stderr, "interrupt loop: writing RDR_to_PC_NotifySlotChange... ");
+ result = write(status_fd, &slotchange, sizeof slotchange);
+ if (debug)
+ fprintf(stderr, "done.\n");
+ }
+ sleep(10);
+ } while (result >= 0);
+
+ if (errno != ESHUTDOWN || result < 0) {
+ perror ("interrupt loop aborted");
+ pthread_cancel(ccid_thread);
+ pthread_cancel(ep0);
+ }
+
+ pthread_cleanup_pop (1);
+
+ return 0;
+ }
+
+ void close_interrupt ()
+ {
+ if (doint) {
+ pthread_cancel(interrupt_thread);
+ if (pthread_join (interrupt_thread, 0) != 0)
+ perror ("can't join interrupt thread");
+ fprintf (stderr, "cancled interrupt loop\n");
+ }
+ }
+
+ void close_pcsc()
+ {
+ int result = perform_shutdown();
+ if (result != SCARD_S_SUCCESS)
+ fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(result));
+ else if (verbose)
+ fprintf(stderr, "closed connection to %s\n", usb_reader_name);
+ }
+
+ char **names = (char **) param;
+ char *source_name = names[0];
+ char *sink_name = names[1];
+ char *status_name = names[2];
+ int result;
+
+ source_fd = source_open (source_name);
+ if (source_fd < 0) {
+ if (debug)
+ perror("source_fd");
+ goto error;
+ }
+ pthread_cleanup_push (close_fd, &source_fd);
+
+ sink_fd = sink_open (sink_name);
+ if (sink_fd < 0) {
+ if (debug)
+ perror("sink_fd");
+ goto error;
+ }
+ pthread_cleanup_push (close_fd, &sink_fd);
+
+ usb_reader_name = perform_initialization(usb_reader_num);
+ if (usb_reader_name == NULL) {
+ if (debug)
+ perror("perform_initialization");
+ goto error;
+ }
+ pthread_cleanup_push (close_pcsc, NULL);
+ if (verbose)
+ fprintf (stderr, "connected to %s\n", usb_reader_name);
+
+ if (doint) {
+ static char * interruptnames[1];
+ interruptnames[0] = status_name;
+ if (pthread_create (&interrupt_thread, NULL, interrupt, (void *)
+ interruptnames) != 0) {
+ perror ("can't create interrupt thread");
+ goto error;
+ }
+ } else if (verbose) {
+ fprintf (stderr, "interrupt pipe disabled\n");
+ }
+ pthread_cleanup_push (close_interrupt, NULL);
+
+ __u8 *outbuf = NULL;
+ pthread_cleanup_push (free, outbuf);
+ size_t bufsize = 512;
+ __u8 *inbuf = (__u8 *) malloc(bufsize);
+ pthread_cleanup_push (free, inbuf);
+ if (inbuf == NULL) {
+ if (debug)
+ perror("malloc");
+ goto error;
+ }
+
+ do {
+
+ /* original LinuxThreads cancelation didn't work right
+ * so test for it explicitly.
+ */
+ pthread_testcancel ();
+
+ if (debug)
+ fprintf(stderr, "reading %lu bytes... ", (long unsigned) bufsize);
+ result = read(sink_fd, inbuf, bufsize);
+ if (result < 0) break;
+ if (debug)
+ fprintf(stderr, "got %d, done.\n", result);
+ if (!result) break;
+
+ result = parse_ccid(inbuf, &outbuf);
+ if (result < 0) break;
+
+ if (debug)
+ fprintf(stderr, "writing %d bytes... ", result);
+ result = write(source_fd, outbuf, result);
+ if (debug)
+ fprintf(stderr, "done.\n");
+ } while (result >= 0);
+
+ if (errno != ESHUTDOWN || result < 0) {
+ perror ("ccid loop aborted");
+ pthread_cancel(ep0);
+ }
+
+ pthread_cleanup_pop (1);
+ pthread_cleanup_pop (1);
+ pthread_cleanup_pop (1);
+ pthread_cleanup_pop (1);
+ pthread_cleanup_pop (1);
+ pthread_cleanup_pop (1);
+
+ fflush (stdout);
+ fflush (stderr);
+
+ return 0;
+
+error:
+ pthread_cancel(ep0);
+ pthread_exit(0);
+
+}
+
+static void *hid (void *param)
+{
+
+ char **names = (char **) param;
+ char *status_name = names[0];
+ int result = 0;
+
+ hidstatus_fd = hidstatus_open (status_name);
+ if (hidstatus_fd < 0) {
+ if (debug)
+ perror("hidstatus_fd");
+ goto error;
+ }
+ pthread_cleanup_push (close_fd, &hidstatus_fd);
+
+ __u8 *outbuf = NULL;
+ pthread_cleanup_push (free, outbuf);
+ size_t bufsize = 512;
+ __u8 *inbuf = (__u8 *) malloc(bufsize);
+ pthread_cleanup_push (free, inbuf);
+ if (inbuf == NULL) {
+ if (debug)
+ perror("malloc");
+ goto error;
+ }
+
+ do {
+
+ /* original LinuxThreads cancelation didn't work right
+ * so test for it explicitly.
+ */
+ pthread_testcancel ();
+
+ } while (result >= 0);
+
+ if (errno != ESHUTDOWN || result < 0) {
+ perror ("hid loop aborted");
+ pthread_cancel(ep0);
+ }
+
+ pthread_cleanup_pop (1);
+ pthread_cleanup_pop (1);
+ pthread_cleanup_pop (1);
+
+ fflush (stdout);
+ fflush (stderr);
+
+ return 0;
+
+error:
+ pthread_cancel(ep0);
+ pthread_exit(0);
+}
+
+static void start_io ()
+{
+ //int tmp;
+ sigset_t allsig, oldsig;
+
+#ifdef AIO
+ /* iso uses the same API as bulk/interrupt. we queue one
+ * (u)frame's worth of data per i/o request, and the host
+ * polls that queue once per interval.
+ */
+ switch (current_speed) {
+ case USB_SPEED_FULL:
+ if (iso)
+ iosize = __le16_to_cpup (&fs_source_desc
+ .wMaxPacketSize);
+ else
+ iosize = bufsize;
+ break;
+ case USB_SPEED_HIGH:
+ /* for iso, we updated bufsize earlier */
+ iosize = bufsize;
+ break;
+ default:
+ fprintf (stderr, "bogus link speed %d\n", current_speed);
+ return;
+ }
+#endif /* AIO */
+
+ sigfillset (&allsig);
+ errno = pthread_sigmask (SIG_SETMASK, &allsig, &oldsig);
+ if (errno < 0) {
+ perror ("set thread signal mask");
+ return;
+ }
+
+ /* is it true that the LSB requires programs to disconnect
+ * from their controlling tty before pthread_create()?
+ * why? this clearly doesn't ...
+ */
+
+ static char *names[2];
+ names[0] = EP_IN_NAME;
+ names[1] = EP_OUT_NAME;
+ names[2] = EP_STATUS_NAME;
+ if (pthread_create (&ccid_thread, NULL, ccid, (void *) names) != 0) {
+ perror ("can't create ccid thread");
+ goto cleanup;
+ }
+ static char * hidnames[1];
+ hidnames[0] = EP_HID_STATUS_NAME;
+ if (dohid) {
+ config.bNumInterfaces = 2;
+ if (pthread_create (&hidthread, NULL, hid, (void *)
+ hidnames) != 0) {
+ perror ("can't create hid thread");
+ goto cleanup;
+ }
+ } else {
+ if (verbose)
+ fprintf (stderr, "HID disabled.\n");
+ }
+
+ /* give the other threads a chance to run before we report
+ * success to the host.
+ * FIXME better yet, use pthread_cond_timedwait() and
+ * synchronize on ep config success.
+ */
+ sched_yield ();
+
+cleanup:
+ errno = pthread_sigmask (SIG_SETMASK, &oldsig, 0);
+ if (errno != 0) {
+ perror ("restore sigmask");
+ exit (-1);
+ }
+}
+
+static void stop_io ()
+{
+ if (!pthread_equal (ccid_thread, ep0)) {
+ pthread_cancel (ccid_thread);
+ if (pthread_join (ccid_thread, 0) != 0)
+ perror ("can't join ccid thread");
+ ccid_thread = ep0;
+ fprintf (stderr, "cancled ccid\n");
+ }
+ if (!pthread_equal (ccid_thread, ep0)) {
+ pthread_cancel (hidthread);
+ if (pthread_join (hidthread, 0) != 0)
+ perror ("can't join hid thread");
+ hidthread = ep0;
+ fprintf (stderr, "cancled hid\n");
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static char *
+build_config (char *cp, const struct usb_endpoint_descriptor **ep, const struct usb_endpoint_descriptor *hid_ep)
+{
+ struct usb_config_descriptor *c;
+ int i;
+
+ c = (struct usb_config_descriptor *) cp;
+
+ memcpy (cp, &config, config.bLength);
+ cp += config.bLength;
+
+ memcpy (cp, &source_sink_intf, sizeof source_sink_intf);
+ cp += sizeof source_sink_intf;
+
+ // Append vendor class specification
+ memcpy (cp, &ccid_desc, sizeof ccid_desc);
+ cp += sizeof ccid_desc;
+
+ for (i = 0; i < source_sink_intf.bNumEndpoints; i++) {
+ memcpy (cp, ep [i], USB_DT_ENDPOINT_SIZE);
+ cp += USB_DT_ENDPOINT_SIZE;
+ }
+
+ if (dohid) {
+ struct usb_interface_descriptor hid_intf = {
+ .bLength = sizeof hid_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 1,
+
+ .bInterfaceClass = USB_CLASS_HID,
+ .iInterface = STRINGID_HID_INTERFACE,
+ .bNumEndpoints = 1,
+ };
+ memcpy (cp, &hid_intf, sizeof hid_intf);
+ cp += sizeof hid_intf;
+ // Append vendor class specification
+ memcpy (cp, &hid_desc, sizeof hid_desc);
+ cp += sizeof hid_desc;
+
+ for (i = 0; i < hid_intf.bNumEndpoints; i++) {
+ memcpy (cp, &(hid_ep [i]), USB_DT_ENDPOINT_SIZE);
+ cp += USB_DT_ENDPOINT_SIZE;
+ }
+ }
+
+ c->wTotalLength = __cpu_to_le16 (cp - (char *) c);
+ return cp;
+}
+
+static int init_device (void)
+{
+ char buf [4096], *cp = &buf [0];
+ int fd;
+ int result;
+
+#ifdef AIO
+ if (iso)
+ result = iso_autoconfig ();
+ else
+#endif
+ result = autoconfig ();
+ if (result < 0) {
+ fprintf (stderr, "?? don't recognize /dev/gadget %s device\n",
+ iso ? "iso" : "bulk");
+ return result;
+ }
+
+ fd = open (DEVNAME, O_RDWR);
+ if (fd < 0) {
+ perror (DEVNAME);
+ return -errno;
+ }
+
+ *(__u32 *)cp = 0; /* tag for this format */
+ cp += 4;
+
+ /* write full then high speed configs */
+ cp = build_config (cp, fs_eps, &fs_hid_status_desc);
+ if (HIGHSPEED)
+ cp = build_config (cp, hs_eps, &hs_hid_status_desc);
+
+ device_desc.idVendor = __cpu_to_le16 (vendorid);
+ device_desc.idProduct = __cpu_to_le16 (productid);
+ if (verbose) {
+ fprintf(stderr, "idVendor=%04X idProduct=%04X\n", vendorid,
+ productid);
+ }
+ /* and device descriptor at the end */
+ memcpy (cp, &device_desc, sizeof device_desc);
+ cp += sizeof device_desc;
+
+ result = write (fd, &buf [0], cp - &buf [0]);
+ if (result < 0) {
+ perror ("write dev descriptors");
+ close (fd);
+ return result;
+ } else if (result != (cp - buf)) {
+ fprintf (stderr, "dev init, wrote %d expected %ld\n",
+ result, (long int) (cp - buf));
+ close (fd);
+ return -EIO;
+ }
+ return fd;
+}
+
+static void handle_control (int fd, struct usb_ctrlrequest *setup)
+{
+ int result, tmp;
+ __u8 buf [256];
+ __u16 value, index, length;
+
+ value = __le16_to_cpu(setup->wValue);
+ index = __le16_to_cpu(setup->wIndex);
+ length = __le16_to_cpu(setup->wLength);
+
+ if ((setup->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+ goto special;
+
+ switch (setup->bRequest) { /* usb 2.0 spec ch9 requests */
+ case USB_REQ_GET_DESCRIPTOR:
+ if (debug)
+ fprintf(stderr, "USB_REQ_GET_DESCRIPTOR\n");
+ if (setup->bRequestType != USB_DIR_IN)
+ goto stall;
+ switch (value >> 8) {
+ case USB_DT_STRING:
+ tmp = value & 0x0ff;
+ if (debug)
+ fprintf (stderr,
+ "... get string %d lang %04x\n",
+ tmp, index);
+ if (tmp != 0 && index != strings.language) {
+ fprintf (stderr, "wrong language\n");
+ goto stall;
+ }
+ memset (buf, 0, 256); /* zero all the bytes */
+ result = usb_gadget_get_string (&strings, tmp, buf);
+ if (result < 0) {
+ perror("usb_gadget_get_string");
+ goto stall;
+ }
+ tmp = result;
+ if (length < tmp)
+ tmp = length;
+ result = write (fd, buf, tmp);
+ if (result < 0) {
+ if (errno == EIDRM)
+ fprintf (stderr, "string timeout\n");
+ else
+ perror ("write string data");
+ } else if (result != tmp) {
+ fprintf (stderr, "short string write, %d\n",
+ result);
+ }
+ break;
+ default:
+ goto stall;
+ }
+ return;
+ case USB_REQ_SET_CONFIGURATION:
+ if (debug)
+ fprintf (stderr, "USB_REQ_SET_CONFIGURATION #%d\n", value);
+ if (setup->bRequestType != USB_DIR_OUT)
+ goto stall;
+
+ /* Kernel is normally waiting for us to finish reconfiguring
+ * the device.
+ *
+ * Some hardware can't, notably older PXA2xx hardware. (With
+ * racey and restrictive config change automagic. PXA 255 is
+ * OK, most PXA 250s aren't. If it has a UDC CFR register,
+ * it can handle deferred response for SET_CONFIG.) To handle
+ * such hardware, don't write code this way ... instead, keep
+ * the endpoints always active and don't rely on seeing any
+ * config change events, either this or SET_INTERFACE.
+ */
+ switch (value) {
+ case CONFIG_VALUE:
+ if ( source_fd >= 0 || sink_fd >= 0 || status_fd >= 0 || hidstatus_fd >= 0 )
+ stop_io ();
+ start_io ();
+ break;
+ case 0:
+ stop_io ();
+ break;
+ default:
+ /* kernel bug -- "can't happen" */
+ fprintf (stderr, "? illegal config\n");
+ goto stall;
+ }
+
+ /* ... ack (a write would stall) */
+ result = read (fd, &result, 0);
+ if (result)
+ perror ("ack SET_CONFIGURATION");
+ return;
+ case USB_REQ_GET_INTERFACE:
+ if (debug)
+ fprintf (stderr, "USB_REQ_GET_INTERFACE\n");
+ if (setup->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
+ || index != 0
+ || length > 1)
+ goto stall;
+
+ /* only one altsetting in this driver */
+ buf [0] = 0;
+ result = write (fd, buf, length);
+ if (result < 0) {
+ if (errno == EIDRM)
+ fprintf (stderr, "GET_INTERFACE timeout\n");
+ else
+ perror ("write GET_INTERFACE data");
+ } else if (result != length) {
+ fprintf (stderr, "short GET_INTERFACE write, %d\n",
+ result);
+ }
+ return;
+ case USB_REQ_SET_INTERFACE:
+ if (debug)
+ fprintf (stderr, "USB_REQ_SET_INTERFACE\n");
+ if (setup->bRequestType != USB_RECIP_INTERFACE
+ || index != 0
+ || value != 0)
+ goto stall;
+
+ /* just reset toggle/halt for the interface's endpoints */
+ result = 0;
+ if (ioctl (source_fd, GADGETFS_CLEAR_HALT) < 0) {
+ result = errno;
+ perror ("reset source fd");
+ }
+ if (ioctl (sink_fd, GADGETFS_CLEAR_HALT) < 0) {
+ result = errno;
+ perror ("reset sink fd");
+ }
+ if (status_fd > 0) {
+ if (ioctl (status_fd, GADGETFS_CLEAR_HALT) < 0) {
+ result = errno;
+ perror ("reset status fd");
+ }
+ }
+ if (hidstatus_fd > 0) {
+ if (ioctl (hidstatus_fd, GADGETFS_CLEAR_HALT) < 0) {
+ result = errno;
+ perror ("reset hidstatus fd");
+ }
+ }
+ /* FIXME eventually reset the result endpoint too */
+ if (result)
+ goto stall;
+
+ /* ... and ack (a write would stall) */
+ result = read (fd, &result, 0);
+ if (result)
+ perror ("ack SET_INTERFACE");
+ return;
+ default:
+ goto stall;
+ }
+
+special:
+ switch (setup->bRequestType) {
+ case USB_REQ_CCID: {
+ __u8 *outbuf = NULL;
+ result = parse_ccid_control(setup, &outbuf);
+ if (result < 0 || result > 256)
+ goto stall;
+ if (debug)
+ fprintf(stderr, "writing %d bytes... ", result);
+ result = write (fd, outbuf, result);
+ if (debug)
+ fprintf(stderr, "done.\n");
+ free(outbuf);
+ if (result < 0)
+ goto stall;
+ } return;
+ default:
+ goto stall;
+ }
+
+stall:
+ if (verbose) {
+ fprintf(stderr, "%s:%d\n", __FILE__, __LINE__);
+ fprintf (stderr, "... protocol stall %02x.%02x\n",
+ setup->bRequestType, setup->bRequest);
+ for (tmp = 0; tmp<5; tmp++) {
+ printf("%d: %s\n", stringtab[tmp].id, stringtab[tmp].s);
+ }
+ }
+
+ /* non-iso endpoints are stalled by issuing an i/o request
+ * in the "wrong" direction. ep0 is special only because
+ * the direction isn't fixed.
+ */
+ if (setup->bRequestType & USB_DIR_IN)
+ result = read (fd, &result, 0);
+ else
+ result = write (fd, &result, 0);
+ if (result != -1)
+ fprintf (stderr, "can't stall ep0 for %02x.%02x\n",
+ setup->bRequestType, setup->bRequest);
+ else if (errno != EL2HLT)
+ perror ("ep0 stall");
+}
+
+static void signothing (int sig, siginfo_t *info, void *ptr)
+{
+ /* NOP */
+ if (debug)
+ fprintf (stderr, "%s %d\n", __FUNCTION__, sig);
+}
+
+static const char *speed (enum usb_device_speed s)
+{
+ switch (s) {
+ case USB_SPEED_LOW: return "low speed";
+ case USB_SPEED_FULL: return "full speed";
+ case USB_SPEED_HIGH: return "high speed";
+ default: return "UNKNOWN speed";
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* control thread, handles main event loop */
+
+#define NEVENT 5
+#define LOGDELAY (15 * 60) /* seconds before stdout timestamp */
+
+static void *ep0_thread (void *param)
+{
+ int fd = *(int*) param;
+ struct sigaction action;
+ time_t now, last;
+ struct pollfd ep0_poll;
+
+ ccid_thread = ep0 = pthread_self ();
+ pthread_cleanup_push (close_fd, param);
+ pthread_cleanup_push (stop_io, NULL);
+
+ /* REVISIT signal handling ... normally one pthread should
+ * be doing sigwait() to handle all async signals.
+ */
+ action.sa_sigaction = signothing;
+ sigfillset (&action.sa_mask);
+ action.sa_flags = SA_SIGINFO;
+ if (sigaction (SIGINT, &action, NULL) < 0) {
+ perror ("SIGINT");
+ return 0;
+ }
+ if (sigaction (SIGQUIT, &action, NULL) < 0) {
+ perror ("SIGQUIT");
+ return 0;
+ }
+
+ ep0_poll.fd = fd;
+ ep0_poll.events = POLLIN | POLLOUT | POLLHUP;
+
+ /* event loop */
+ last = 0;
+ for (;;) {
+ int tmp;
+ struct usb_gadgetfs_event event [NEVENT];
+ int connected = 0;
+ int i, nevent;
+
+ /* Use poll() to test that mechanism, to generate
+ * activity timestamps, and to make it easier to
+ * tweak this code to work without pthreads. When
+ * AIO is needed without pthreads, ep0 can be driven
+ * instead using SIGIO.
+ */
+ tmp = poll(&ep0_poll, 1, -1);
+ if (verbose) {
+ time (&now);
+ if ((now - last) > LOGDELAY) {
+ char timebuf[26];
+
+ last = now;
+ ctime_r (&now, timebuf);
+ printf ("\n** %s", timebuf);
+ }
+ }
+ if (tmp < 0) {
+ /* exit path includes EINTR exits */
+ perror("poll");
+ break;
+ }
+
+ tmp = read (fd, &event, sizeof event);
+ if (tmp < 0) {
+ if (errno == EAGAIN) {
+ sleep (1);
+ continue;
+ }
+ perror ("ep0 read after poll");
+ goto done;
+ }
+ nevent = tmp / sizeof event [0];
+ if (nevent != 1 && debug)
+ fprintf (stderr, "read %d ep0 events\n",
+ nevent);
+
+ for (i = 0; i < nevent; i++) {
+ switch (event [i].type) {
+ case GADGETFS_NOP:
+ if (verbose)
+ fprintf (stderr, "NOP\n");
+ break;
+ case GADGETFS_CONNECT:
+ connected = 1;
+ current_speed = event [i].u.speed;
+ if (verbose)
+ fprintf (stderr,
+ "CONNECT %s\n",
+ speed (event [i].u.speed));
+ break;
+ case GADGETFS_SETUP:
+ connected = 1;
+ handle_control (fd, &event [i].u.setup);
+ break;
+ case GADGETFS_DISCONNECT:
+ connected = 0;
+ current_speed = USB_SPEED_UNKNOWN;
+ if (verbose)
+ fprintf(stderr, "DISCONNECT\n");
+ stop_io ();
+ break;
+ case GADGETFS_SUSPEND:
+ // connected = 1;
+ if (verbose)
+ fprintf (stderr, "SUSPEND\n");
+ break;
+ default:
+ fprintf (stderr,
+ "* unhandled event %d\n",
+ event [i].type);
+ }
+ }
+ continue;
+done:
+ fflush (stdout);
+ if (connected)
+ stop_io ();
+ break;
+ }
+ if (verbose)
+ fprintf (stderr, "ep0 done.\n");
+
+ pthread_cleanup_pop (1);
+ pthread_cleanup_pop (1);
+
+ fflush (stdout);
+ fflush (stderr);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+int
+main (int argc, char **argv)
+{
+ int fd, c, i;
+
+ /* random initial serial number */
+ srand ((int) time (0));
+ for (i = 0; i < sizeof serial - 1; ) {
+ c = rand () % 127;
+ if ((('a' <= c && c <= 'z') || ('0' <= c && c <= '9')))
+ serial [i++] = c;
+ }
+
+ for (i=1; i