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