initial commit related to CCID device/firmware implementation
The CCID core here shall be rather generic, and combined with the
hardware specific bits for (initially) the sysmoOCTSIM target.
Change-Id: I2789f7fcdb1b24c6ef47c7f37f4889f90acfd698
diff --git a/ccid/Makefile b/ccid/Makefile
new file mode 100644
index 0000000..34df8c9
--- /dev/null
+++ b/ccid/Makefile
@@ -0,0 +1,10 @@
+
+
+ccid_descriptors: ccid_descriptors.o
+ $(CC) -lasan -losmocore -laio -o $@ $^
+
+%.o: %.c
+ $(CC) -o $@ -c $^
+
+clean:
+ rm ccid_descriptors *.o
diff --git a/ccid/ccid_descriptors.c b/ccid/ccid_descriptors.c
new file mode 100644
index 0000000..7117fbb
--- /dev/null
+++ b/ccid/ccid_descriptors.c
@@ -0,0 +1,358 @@
+
+#include <stdint.h>
+#include <endian.h>
+#include <sys/types.h>
+#include <linux/usb/functionfs.h>
+
+#include "ccid_proto.h"
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#else
+#define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))
+#define cpu_to_le32(x) \
+ ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \
+ (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24))
+#endif
+
+#define le32_to_cpu(x) le32toh(x)
+#define le16_to_cpu(x) le16toh(x)
+
+/***********************************************************************
+ * Actual USB CCID Descriptors
+ ***********************************************************************/
+
+static const struct {
+ struct usb_functionfs_descs_head_v2 header;
+ __le32 fs_count;
+ struct {
+ struct usb_interface_descriptor intf;
+ struct usb_ccid_class_descriptor ccid;
+ struct usb_endpoint_descriptor_no_audio ep_irq;
+ struct usb_endpoint_descriptor_no_audio ep_out;
+ struct usb_endpoint_descriptor_no_audio ep_in;
+ } __attribute__ ((packed)) fs_descs;
+} __attribute__ ((packed)) descriptors = {
+ .header = {
+ .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+ .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC),
+ .length = cpu_to_le32(sizeof(descriptors)),
+ },
+ .fs_count = cpu_to_le32(5),
+ .fs_descs = {
+ .intf = {
+ .bLength = sizeof(descriptors.fs_descs.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 3,
+ .bInterfaceClass = 11,
+ .iInterface = 1,
+ },
+ .ccid = {
+ .bLength = sizeof(descriptors.fs_descs.ccid),
+ .bDescriptorType = 33,
+ .bcdCCID = cpu_to_le16(0x0110),
+ .bMaxSlotIndex = 7,
+ .bVoltageSupport = 0x07, /* 5/3/1.8V */
+ .dwProtocols = cpu_to_le32(1), /* T=0 only */
+ .dwDefaultClock = cpu_to_le32(5000),
+ .dwMaximumClock = cpu_to_le32(20000),
+ .bNumClockSupported = 0,
+ .dwDataRate = cpu_to_le32(9600),
+ .dwMaxDataRate = cpu_to_le32(921600),
+ .bNumDataRatesSupported = 0,
+ .dwMaxIFSD = cpu_to_le32(0),
+ .dwSynchProtocols = cpu_to_le32(0),
+ .dwMechanical = cpu_to_le32(0),
+ .dwFeatures = cpu_to_le32(0x10),
+ .dwMaxCCIDMessageLength = 272,
+ .bClassGetResponse = 0xff,
+ .bClassEnvelope = 0xff,
+ .wLcdLayout = cpu_to_le16(0),
+ .bPINSupport = 0,
+ .bMaxCCIDBusySlots = 8,
+ },
+ .ep_irq = {
+ .bLength = sizeof(descriptors.fs_descs.ep_irq),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 64,
+ },
+ .ep_out = {
+ .bLength = sizeof(descriptors.fs_descs.ep_out),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ /* .wMaxPacketSize = autoconfiguration (kernel) */
+ },
+ .ep_in = {
+ .bLength = sizeof(descriptors.fs_descs.ep_in),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 3 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ /* .wMaxPacketSize = autoconfiguration (kernel) */
+ },
+ },
+};
+
+#define STR_INTERFACE_ "Osmocom CCID Interface"
+
+static const struct {
+ struct usb_functionfs_strings_head header;
+ struct {
+ __le16 code;
+ const char str1[sizeof(STR_INTERFACE_)];
+ } __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+ .header = {
+ .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
+ .length = cpu_to_le32(sizeof(strings)),
+ .str_count = cpu_to_le32(1),
+ .lang_count = cpu_to_le32(1),
+ },
+ .lang0 = {
+ cpu_to_le16(0x0409), /* en-us */
+ STR_INTERFACE_,
+ },
+};
+
+
+
+/***********************************************************************
+ * USB FunctionFS interface
+ ***********************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/utils.h>
+
+#ifndef FUNCTIONFS_SUPPORTS_POLL
+#include <libaio.h>
+struct aio_help {
+ uint8_t buf[64];
+ struct iocb *iocb;
+};
+#endif
+
+/* usb function handle */
+struct ufunc_handle {
+ struct osmo_fd ep0;
+ struct osmo_fd ep_in;
+ struct osmo_fd ep_out;
+ struct osmo_fd ep_int;
+#ifndef FUNCTIONFS_SUPPORTS_POLL
+ struct osmo_fd aio_evfd;
+ io_context_t aio_ctx;
+ struct aio_help aio_in;
+ struct aio_help aio_out;
+ struct aio_help aio_int;
+#endif
+};
+
+static int ep_int_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ printf("INT\n");
+ return 0;
+}
+
+static int ep_out_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ uint8_t buf[64];
+ int rc;
+
+ printf("OUT\n");
+ if (what & BSC_FD_READ) {
+ rc = read(ofd->fd, buf, sizeof(buf));
+ ccid_handle_out(uh->ccid_handle, buf, rc);
+ }
+ return 0;
+}
+
+static int ep_in_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ printf("IN\n");
+ if (what & BSC_FD_WRITE) {
+ /* write what we have to write */
+ }
+ return 0;
+}
+
+const struct value_string ffs_evt_type_names[] = {
+ { FUNCTIONFS_BIND, "BIND" },
+ { FUNCTIONFS_UNBIND, "UNBIND" },
+ { FUNCTIONFS_ENABLE, "ENABLE" },
+ { FUNCTIONFS_DISABLE, "DISABLE" },
+ { FUNCTIONFS_SETUP, "SETUP" },
+ { FUNCTIONFS_SUSPEND, "SUSPEND" },
+ { FUNCTIONFS_RESUME, "RESUME" },
+ { 0, NULL }
+};
+
+static void handle_setup(const struct usb_ctrlrequest *setup)
+{
+ printf("bRequestType = %d\n", setup->bRequestType);
+ printf("bRequest = %d\n", setup->bRequest);
+ printf("wValue = %d\n", le16_to_cpu(setup->wValue));
+ printf("wIndex = %d\n", le16_to_cpu(setup->wIndex));
+ printf("wLength = %d\n", le16_to_cpu(setup->wLength));
+}
+
+static void aio_refill_out(struct ufunc_handle *uh);
+
+static int ep_0_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ struct ufunc_handle *uh = (struct ufunc_handle *) ofd->data;
+ int rc;
+
+ printf("EP0\n");
+
+ if (what & BSC_FD_READ) {
+ struct usb_functionfs_event evt;
+ rc = read(ofd->fd, (uint8_t *)&evt, sizeof(evt));
+ if (rc < sizeof(evt))
+ return -23;
+ printf("\t%s\n", get_value_string(ffs_evt_type_names, evt.type));
+ switch (evt.type) {
+ case FUNCTIONFS_ENABLE:
+ aio_refill_out(uh);
+ break;
+ case FUNCTIONFS_SETUP:
+ handle_setup(&evt.u.setup);
+ break;
+ }
+
+ }
+ return 0;
+}
+
+#ifndef FUNCTIONFS_SUPPORTS_POLL
+
+static void aio_refill_out(struct ufunc_handle *uh)
+{
+ int rc;
+ struct aio_help *ah = &uh->aio_out;
+ io_prep_pread(ah->iocb, uh->ep_out.fd, ah->buf, sizeof(ah->buf), 0);
+ io_set_eventfd(ah->iocb, uh->aio_evfd.fd);
+ rc = io_submit(uh->aio_ctx, 1, &ah->iocb);
+ OSMO_ASSERT(rc >= 0);
+}
+
+static int evfd_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ struct ufunc_handle *uh = (struct ufunc_handle *) ofd->data;
+ struct io_event evt[3];
+ uint64_t ev_cnt;
+ int i, rc;
+
+ rc = read(ofd->fd, &ev_cnt, sizeof(ev_cnt));
+ assert(rc == sizeof(ev_cnt));
+
+ rc = io_getevents(uh->aio_ctx, 1, 3, evt, NULL);
+ if (rc <= 0)
+ return rc;
+
+ for (i = 0; i < rc; i++) {
+ int fd = evt[i].obj->aio_fildes;
+ if (fd == uh->ep_int.fd) {
+ /* interrupt endpoint AIO has completed. This means the IRQ transfer
+ * which we generated has reached the host */
+ } else if (fd == uh->ep_in.fd) {
+ /* IN endpoint AIO has completed. This means the IN transfer which
+ * we sent to the host has completed */
+ } else if (fd == uh->ep_out.fd) {
+ /* IN endpoint AIO has completed. This means the host has sent us
+ * some OUT data */
+ //printf("\t%s\n", osmo_hexdump(uh->aio_out.buf, evt[i].res));
+ ccid_handle_out(uh->ccid_handle, uh->aio_out.buf, evt[i].res);
+ aio_refill_out(uh);
+ }
+ }
+}
+#endif
+
+
+static int ep0_init(struct ufunc_handle *uh)
+{
+ int rc;
+
+ /* open control endpoint and write descriptors to it */
+ rc = open("ep0", O_RDWR);
+ assert(rc >= 0);
+ osmo_fd_setup(&uh->ep0, rc, BSC_FD_READ, &ep_0_cb, uh, 0);
+ osmo_fd_register(&uh->ep0);
+ rc = write(uh->ep0.fd, &descriptors, sizeof(descriptors));
+ if (rc != sizeof(descriptors))
+ return -1;
+ rc = write(uh->ep0.fd, &strings, sizeof(strings));
+ if (rc != sizeof(strings))
+ return -1;
+
+ /* open other endpoint file descriptors */
+ rc = open("ep1", O_RDWR);
+ assert(rc >= 0);
+ osmo_fd_setup(&uh->ep_int, rc, 0, &ep_int_cb, uh, 1);
+#ifdef FUNCTIONFS_SUPPORTS_POLL
+ osmo_fd_register(&uh->ep_int);
+#endif
+
+ rc = open("ep2", O_RDWR);
+ assert(rc >= 0);
+ osmo_fd_setup(&uh->ep_out, rc, BSC_FD_READ, &ep_out_cb, uh, 2);
+#ifdef FUNCTIONFS_SUPPORTS_POLL
+ osmo_fd_register(&uh->ep_out);
+#endif
+
+ rc = open("ep3", O_RDWR);
+ assert(rc >= 0);
+ osmo_fd_setup(&uh->ep_in, rc, 0, &ep_in_cb, uh, 3);
+#ifdef FUNCTIONFS_SUPPORTS_POLL
+ osmo_fd_register(&uh->ep_in);
+#endif
+
+#ifndef FUNCTIONFS_SUPPORTS_POLL
+#include <sys/eventfd.h>
+ /* for some absolutely weird reason, gadgetfs+functionfs don't support
+ * the standard methods of non-blocking I/o (select/poll). We need to
+ * work around using Linux AIO, which is not to be confused with POSIX AIO! */
+
+ memset(&uh->aio_ctx, 0, sizeof(uh->aio_ctx));
+ rc = io_setup(3, &uh->aio_ctx);
+ OSMO_ASSERT(rc >= 0);
+
+ /* create an eventfd, which will be marked readable once some AIO completes */
+ rc = eventfd(0, 0);
+ OSMO_ASSERT(rc >= 0);
+ osmo_fd_setup(&uh->aio_evfd, rc, BSC_FD_READ, &evfd_cb, uh, 0);
+ osmo_fd_register(&uh->aio_evfd);
+
+ uh->aio_out.iocb = malloc(sizeof(struct iocb));
+
+#endif
+
+ return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+ struct ufunc_handle ufh = (struct ufunc_handle) { 0, };
+ int rc;
+
+ chdir(argv[1]);
+ rc = ep0_init(&ufh);
+ if (rc < 0) {
+ fprintf(stderr, "Error %d\n", rc);
+ exit(2);
+ }
+
+ while (1) {
+ osmo_select_main(0);
+ }
+}
diff --git a/ccid/ccid_device.c b/ccid/ccid_device.c
new file mode 100644
index 0000000..c81f177
--- /dev/null
+++ b/ccid/ccid_device.c
@@ -0,0 +1,370 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+
+#include "ccid_proto.h"
+
+#define NR_SLOTS 8
+
+struct ccid_slot {
+ struct ccid_instance *ci;
+ uint8_t slot_nr;
+ bool icc_present;
+ bool icc_powered;
+ bool icc_in_reset;
+};
+
+struct ccid_ops {
+ int (*send)(struct ccid_instance *ci, struct msgb *msg);
+};
+
+struct ccid_instance {
+ struct ccid_slot slot[NR_SLOTS];
+ struct ccid_ops ops;
+};
+
+#define msgb_ccid_out(x) (union ccid_pc_to_rdr *)msgb_data(x)
+#define msgb_ccid_in(x) (union ccid_rdr_to_pc *)msgb_data(x)
+
+static struct ccid_slot *get_ccid_slot(struct ccid_instance *ci, uint8_t slot_nr)
+{
+ if (slot_nr >= sizeof(ci->slot))
+ return NULL;
+ else
+ return &ci->slot[slot_nr]
+}
+
+static uint8_t get_icc_status(const struct ccid_slot *cs)
+{
+ if (cs->icc_present && cs->icc_powered && !cs->icc_in_reset)
+ return CCID_ICC_STATUS_PRES_ACT;
+ else if (!cs->icc_present)
+ return CCID_ICC_STATUS_NO_ICC;
+ else
+ return CCID_ICC_STATUS_PRES_INACT;
+}
+
+#define SET_HDR(x, msg_type, slot, seq) do { \
+ (x)->hdr.bMessageType = msg_type; \
+ (x)->hdr.dwLength = 0; \
+ (x)->hdr.bSlot = slot; \
+ (x)->hdr.bSeq = seq; \
+ } while (0)
+
+#define SET_HDR_IN(x, msg_type, slot, seq, status, error) do { \
+ SET_HDR(&(x)->hdr, msg_type, slot, seq); \
+ (x)->hdr.bStatus = status; \
+ (x)->hdr.bError = error; \
+ } while (0)
+
+/***********************************************************************
+ * Message generation / sending
+ ***********************************************************************/
+
+static struct msgb *ccid_msgb_alloc(void)
+{
+ struct msgb *msg = msgb_alloc("ccid");
+ OSMO_ASSERT(msg);
+ return msg;
+}
+
+static int ccid_send(struct ccid_instance *ci, struct msgb *msg)
+{
+ return ci->ops.send_in(ci, msg);
+}
+
+static int ccid_slot_send(struct ccid_slot *cs, struct msgb *msg)
+{
+ const struct ccid_header *ch = (const struct ccid_header *) msgb_ccid_in(msg);
+
+ /* patch bSlotNr into message */
+ ch->hdr.bSlot = cs->slot_nr;
+ return ccid_send(cs->ci, msg);
+}
+
+
+/* Section 6.2.1 */
+static struct msgb *ccid_gen_data_block(struct ccid_slot *cs, uint8_t seq, uint8_t cmd_sts,
+ enum ccid_error_code err, const uint8_t *data,
+ uint32_t data_len)
+{
+ struct msgb *msg = ccid_msgb_alloc();
+ struct ccid_rdr_to_pc_data_block *db = msgb_put(msg, sizeof(*db) + data_len);
+ uint8_t sts = (cmd_sts & CCID_CMD_STATUS_MASK) | get_icc_status(cs);
+
+ SET_HDR_IN(db, RDR_to_PC_DataBlock, cs->slot_nr, seq, sts, err);
+ db->hdr.dwLength = cpu_to_le32(data_len);
+ memcpy(db->abData, data, data_len);
+ return msg;
+}
+
+/* Section 6.2.2 */
+static struct msgb *ccid_gen_slot_status(struct ccid_slot *cs, uint8_t seq, uint8_t cmd_sts,
+ enum ccid_error_code err)
+{
+ struct msgb *msg = ccid_msgb_alloc();
+ struct ccid_rdr_to_pc_slot_status *ss = msgb_put(msg, sizeof(*ss));
+ uint8_t sts = (cmd_sts & CCID_CMD_STATUS_MASK) | get_icc_status(cs);
+
+ SET_HDR_IN(ss, RDR_to_PC_SlotStatus, cs->slot_nr, seq, sts, err);
+ return msg;
+}
+
+/* Section 6.2.3 */
+/* TODO */
+
+/* Section 6.2.4 */
+static struct msgb *ccid_gen_escape(struct ccid_slot *cs, uint8_t seq, uint8_t cmd_sts,
+ enum ccid_error_code err, const uint8_t *data,
+ uint32_t data_len)
+{
+ struct msgb *msg = ccid_msgb_alloc();
+ struct ccid_rdr_to_pc_escape *esc = msgb_put(msg, sizeof(*esc) + data_len);
+ uint8_t sts = (cmd_sts & CCID_CMD_STATUS_MASK) | get_icc_status(cs);
+
+ SET_HDR_IN(esc, RDR_to_PC_Escape, cs->slot_nr, seq, sts, err);
+ esc->hdr.dwLength = cpu_to_le32(data_len);
+ memcpy(esc->abData, data, data_len);
+ return msg;
+}
+
+/* Section 6.2.5 */
+static struct msgb *ccid_gen_clock_and_rate(struct ccid_slot *cs, uint8_t seq, uint8_t cmd_sts,
+ enum ccid_error_code err, uint32_t clock_khz, uint32_t rate_bps)
+{
+ struct msgb *msg = ccid_msgb_alloc();
+ struct ccid_rdr_to_pc_data_rate_and_clock *drc = msgb_put(msg, sizeof(*drc));
+ uint8_t sts = (cmd_sts & CCID_CMD_STATUS_MASK) | get_icc_status(cs);
+
+ SET_HDR_IN(drc, RDR_to_PC_DataRateAndClockFrequency, cs->slot_nr, seq, sts, err);
+ drc->dwLength = cpu_to_le32(8); /* Message-specific data length (wtf?) */
+ drc->dwClockFrequency = cpu_to_le32(clock_khz); /* kHz */
+ drc->dwDataRate = cpu_to_le32(rate_bps); /* bps */
+ return msg;
+}
+
+
+
+#if 0
+static struct msgb *gen_err_resp(struct ccid_instance *ci, enum ccid_msg_type msg_type,
+ enum ccid_error_code err_code)
+{
+ struct c
+}
+#endif
+
+/***********************************************************************
+ * Message reception / parsing
+ ***********************************************************************/
+
+/* Section 6.1.3 */
+static int ccid_handle_get_slot_status(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+ struct msgb *resp;
+
+ resp = ccid_gen_slot_status(cs, u->get_slot_status.hdr.bSeq, CCID_CMD_STATUS_OK, 0);
+
+ return ccid_send(cs->ci, resp);
+}
+
+
+/* Section 6.1.1 */
+static int ccid_handle_icc_power_on(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+ struct msgb *resp;
+
+ /* TODO: send actual ATR; handle error cases */
+ /* TODO: handle this asynchronously */
+ resp = ccid_gen_data_block(cs, u->icc_power_on.hdr.hSeq, CCID_CMD_STATUS_OK, 0, NULL, 0);
+
+ return ccid_send(cs->ci, resp);
+}
+
+/* Section 6.1.2 */
+static int ccid_handle_icc_power_off(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+ resp = ccid_gen_slot_status(cs, u->get_slot_status.hdr.bSeq, CCID_CMD_STATUS_OK, 0);
+ return ccid_send(cs->ci, resp);
+}
+
+/* Section 6.1.4 */
+static int ccid_handle_xfr_block(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+ resp = ccid_gen_data_block(cs, u->icc_power_on.hdr.hSeq, CCID_CMD_STATUS_OK, 0, NULL, 0);
+ return ccid_send(cs->ci, resp);
+}
+
+/* Section 6.1.5 */
+static int ccid_handle_get_parameters(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+}
+
+/* Section 6.1.6 */
+static int ccid_handle_reset_parameters(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+}
+
+/* Section 6.1.7 */
+static int ccid_handle_set_parameters(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+}
+
+/* Section 6.1.8 */
+static int ccid_handle_escape(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+}
+
+/* Section 6.1.9 */
+static int ccid_handle_icc_clock(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+ resp = ccid_gen_slot_status(cs, u->get_slot_status.hdr.bSeq, CCID_CMD_STATUS_OK, 0);
+ return ccid_send(cs->ci, resp);
+}
+
+/* Section 6.1.10 */
+static int ccid_handle_t0apdu(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+ resp = ccid_gen_slot_status(cs, u->get_slot_status.hdr.bSeq, CCID_CMD_STATUS_OK, 0);
+ return ccid_send(cs->ci, resp);
+}
+
+/* Section 6.1.11 */
+static int ccid_handle_secure(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+}
+
+/* Section 6.1.12 */
+static int ccid_handle_mechanical(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+ resp = ccid_gen_slot_status(cs, u->get_slot_status.hdr.bSeq, CCID_CMD_STATUS_OK, 0);
+ return ccid_send(cs->ci, resp);
+}
+
+/* Section 6.1.13 */
+static int ccid_handle_abort(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+ resp = ccid_gen_slot_status(cs, u->get_slot_status.hdr.bSeq, CCID_CMD_STATUS_OK, 0);
+ return ccid_send(cs->ci, resp);
+}
+
+/* Section 6.1.14 */
+static int ccid_handle_set_rate_and_clock(struct ccid_slot *cs, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid(msg);
+}
+
+/* handle data arriving from the host on the OUT endpoint */
+int ccid_handle_out(struct ccid_instance *ci, struct msgb *msg)
+{
+ const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
+ const struct ccid_header *ch = (const struct ccid_header *) u;
+ unsigned int len = msgb_length(msg);
+ struct ccid_slot *cs;
+
+ if (len < sizeof(*ch)) {
+ /* FIXME */
+ return -1;
+ }
+
+ cs = get_ccid_slot(ci, ch->hdr.bSlot);
+ if (!cs) {
+ /* FIXME */
+ return -1;
+ }
+
+ switch (ch->bMessageType) {
+ case PC_to_RDR_GetSlotStatus:
+ if (len != sizeof(u->get_slot_status))
+ goto short_msg;
+ rc = ccid_handle_get_slot_status(cs, msg);
+ break;
+ case PC_to_RDR_IccPowerOn:
+ if (len != sizeof(u->icc_power_on))
+ goto short_msg;
+ rc = ccid_handle_icc_power_on(cs, msg);
+ break;
+ case PC_to_RDR_IccPowerOff:
+ if (len != sizeof(u->icc_power_off))
+ goto short_msg;
+ rc = ccid_handle_icc_power_off(cs, msg);
+ break;
+ case PC_to_RDR_XfrBlock:
+ if (len < sizeof(u->xfr_block))
+ goto short_msg;
+ rc = ccid_handle_xfr_block(cs, msg);
+ break;
+ case PC_to_RDR_GetParameters:
+ if (len != sizeof(u->get_parameters))
+ goto short_msg;
+ rc = ccid_handle_get_parameters(cs, msg);
+ break;
+ case PC_to_RDR_ResetParameters:
+ if (len != sizeof(u->reset_parameters))
+ goto short_msg;
+ rc = ccid_handle_reset_parameters(cs, msg);
+ break;
+ case PC_to_RDR_SetParameters:
+ if (len != sizeof(u->set_parameters))
+ goto short_msg;
+ rc = ccid_handle_set_parameters(cs, msg);
+ break;
+ case PC_to_RDR_Escape:
+ if (len < sizeof(u->escape))
+ goto short_msg;
+ rc = ccid_handle_escape(cs, msg);
+ break;
+ case PC_to_RDR_IccClock:
+ if (len != sizeof(u->icc_clock))
+ goto short_msg;
+ rc = ccid_handle_icc_clock(cs, msg);
+ break;
+ case PC_to_RDR_T0APDU:
+ if (len != /*FIXME*/ sizeof(u->t0apdu))
+ goto short_msg;
+ rc = ccid_handle_t0_apdu(cs, msg);
+ break;
+ case PC_to_RDR_Secure:
+ if (len < sizeof(u->secure))
+ goto short_msg;
+ rc = ccid_handle_secure(cs, msg);
+ break;
+ case PC_to_RDR_Mechanical:
+ if (len != sizeof(u->mechanical))
+ goto short_msg;
+ rc = ccid_handle_mechanical(cs, msg);
+ break;
+ case PC_to_RDR_Abort:
+ if (len != sizeof(u->abort))
+ goto short_msg;
+ rc = ccid_handle_abort(cs, msg);
+ break;
+ case PC_to_RDR_SetDataRateAndClockFrequency:
+ if (len != sizeof(u->set_rate_and_clock))
+ goto short_msg;
+ rc = ccid_handle_set_rate_and_clock(cs, msg);
+ break;
+ default:
+ FIXME
+ break;
+ }
+ FIXME
+short_msg:
+ FIXME
+}
diff --git a/ccid/ccid_proto.h b/ccid/ccid_proto.h
new file mode 100644
index 0000000..c3c740c
--- /dev/null
+++ b/ccid/ccid_proto.h
@@ -0,0 +1,367 @@
+#pragma once
+#include <stdint.h>
+
+/* Identifies the length of type of subordinate descriptors of a CCID device
+ * Table 5.1-1 Smart Card Device Class descriptors */
+struct usb_ccid_class_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t bcdCCID;
+ uint8_t bMaxSlotIndex;
+ uint8_t bVoltageSupport;
+ uint32_t dwProtocols;
+ uint32_t dwDefaultClock;
+ uint32_t dwMaximumClock;
+ uint8_t bNumClockSupported;
+ uint32_t dwDataRate;
+ uint32_t dwMaxDataRate;
+ uint8_t bNumDataRatesSupported;
+ uint32_t dwMaxIFSD;
+ uint32_t dwSynchProtocols;
+ uint32_t dwMechanical;
+ uint32_t dwFeatures;
+ uint32_t dwMaxCCIDMessageLength;
+ uint8_t bClassGetResponse;
+ uint8_t bClassEnvelope;
+ uint16_t wLcdLayout;
+ uint8_t bPINSupport;
+ uint8_t bMaxCCIDBusySlots;
+} __attribute__((packed));
+/* handling of bulk out from host */
+
+enum ccid_msg_type {
+ /* Section 6.3 / Table 6.3-1: Interrupt IN */
+ RDR_to_PC_NotifySlotChange = 0x50,
+ RDR_to_PC_HardwareError = 0x51,
+
+ /* Section 6.1 / Table 6.1-1: Bulk OUT */
+ PC_to_RDR_IccPowerOn = 0x62,
+ PC_to_RDR_IccPowerOff = 0x63,
+ PC_to_RDR_GetSlotStatus = 0x65,
+ PC_to_RDR_XfrBlock = 0x6f,
+ PC_to_RDR_GetParameters = 0x6c,
+ PC_to_RDR_ResetParameters = 0x6d,
+ PC_to_RDR_SetParameters = 0x61,
+ PC_to_RDR_Escape = 0x6b,
+ PC_to_RDR_IccClock = 0x6e,
+ PC_to_RDR_T0APDU = 0x6a,
+ PC_to_RDR_Secure = 0x69,
+ PC_to_RDR_Mechanical = 0x71,
+ PC_to_RDR_Abort = 0x72,
+ PC_to_RDR_SetDataRateAndClockFrequency = 0x73,
+
+ /* Section 6.2 / Table 6.2-1: Bulk IN */
+ RDR_to_PC_DataBlock = 0x80,
+ RDR_to_PC_SlotStatus = 0x81,
+ RDR_to_PC_Parameters = 0x82,
+ RDR_to_PC_Escape = 0x83,
+ RDR_to_PC_DataRateAndClockFrequency = 0x84,
+};
+
+/* CCID message header on BULK-OUT endpoint */
+struct ccid_header {
+ uint8_t bMessageType;
+ uint32_t dwLength;
+ uint8_t bSlot;
+ uint8_t bSeq;
+} __attribute__ ((packed));
+
+/***********************************************************************
+ * Bulk OUT
+ ***********************************************************************/
+
+/* Section 6.1.1 */
+enum ccid_power_select {
+ CCID_PWRSEL_AUTO = 0x00,
+ CCID_PWRSEL_5V0 = 0x01,
+ CCID_PWRSEL_3V0 = 0x02,
+ CCID_PWRSEL_1V8 = 0x03,
+};
+
+/* Section 6.1.1 */
+struct ccid_pc_to_rdr_icc_power_on {
+ struct ccid_header hdr;
+ uint8_t bPowerSelect;
+ uint8_t abRFU[2];
+} __attribute__ ((packed));
+/* Response: RDR_to_PC_DataBlock */
+
+/* Section 6.1.2 */
+struct ccid_pc_to_rdr_icc_power_off {
+ struct ccid_header hdr;
+ uint8_t abRFU[3];
+} __attribute__ ((packed));
+/* Response: RDR_to_PC_SlotStatus */
+
+/* Section 6.1.3 */
+struct ccid_pc_to_rdr_get_slot_status {
+ struct ccid_header hdr;
+ uint8_t abRFU[3];
+} __attribute__ ((packed));
+/* Response: RDR_to_PC_SlotStatus */
+
+/* Section 6.1.4 */
+struct ccid_pc_to_rdr_xfr_block {
+ struct ccid_header hdr;
+ uint8_t bBWI;
+ uint16_t wLevelParameter;
+ uint8_t abData[0];
+} __attribute__ ((packed));
+/* Response: RDR_to_PC_DataBlock */
+
+/* Section 6.1.5 */
+struct ccid_pc_to_rdr_get_parameters {
+ struct ccid_header hdr;
+ uint8_t abRFU[3];
+} __attribute__ ((packed));
+/* Response: RDR_to_PC_Parameters */
+
+/* Section 6.1.6 */
+struct ccid_pc_to_rdr_reset_parameters {
+ struct ccid_header hdr;
+ uint8_t abRFU[3];
+} __attribute__ ((packed));
+/* Response: RDR_to_PC_Parameters */
+
+/* Section 6.1.7 */
+struct ccid_proto_data_t0 {
+ uint8_t bmFindexDindex;
+ uint8_t bmTCCKST0;
+ uint8_t bGuardTimeT0;
+ uint8_t bWaitingIntegerT0;
+ uint8_t bClockStop;
+} __attribute__ ((packed));
+struct ccid_proto_data_t1 {
+ uint8_t bmFindexDindex;
+ uint8_t bmTCCKST1;
+ uint8_t bGuardTimeT1;
+ uint8_t bWaitingIntegersT1;
+ uint8_t bClockStop;
+ uint8_t bFSC;
+ uint8_t bNadValue;
+} __attribute__ ((packed));
+struct ccid_pc_to_rdr_set_parameters {
+ struct ccid_header hdr;
+ uint8_t bProtocolNum;
+ uint8_t abRFU[2];
+ union {
+ struct ccid_proto_data_t0 t0;
+ struct ccid_proto_data_t1 t1;
+ } abProtocolData;
+} __attribute__ ((packed));
+/* Response: RDR_to_PC_Parameters */
+
+/* Section 6.1.8 */
+struct ccid_pc_to_rdr_escape {
+ struct ccid_header hdr;
+ uint8_t abRFU[3];
+ uint8_t abData[0];
+} __attribute__ ((packed));
+/* Response: RDR_to_PC_Escape */
+
+/* Section 6.1.9 */
+enum ccid_clock_command {
+ CCID_CLOCK_CMD_RESTART = 0x00,
+ CCID_CLOCK_CMD_STOP = 0x01,
+};
+struct ccid_pc_to_rdr_icc_clock {
+ struct ccid_header hdr;
+ uint8_t bClockCommand;
+ uint8_t abRFU[2];
+} __attribute__ ((packed));
+/* response: RDR_to_PC_SlotStatus */
+
+/* Section 6.1.10 */
+struct ccid_pc_to_rdr_t0apdu {
+ struct ccid_header hdr;
+ uint8_t bmChanges;
+ uint8_t bClassGetResponse;
+ uint8_t bClassEnvelope;
+} __attribute__ ((packed));
+/* Response: RDR_to_PC_SlotStatus */
+
+/* Section 6.1.11 */
+struct ccid_pc_to_rdr_secure {
+ struct ccid_header hdr;
+ uint8_t bBWI;
+ uint16_t wLevelParameter;
+ uint8_t abData[0];
+} __attribute__ ((packed));
+struct ccid_pin_operation_data {
+ uint8_t bPINOperation;
+ uint8_t abPNDataStructure[0];
+} __attribute__ ((packed));
+struct ccid_pin_verification_data {
+ uint8_t bTimeOut;
+ uint8_t bmFormatString;
+ uint8_t bmPINBlockString;
+ uint8_t bmPINLengthFormat;
+ uint16_t wPINMaxExtraDigit;
+ uint8_t bEntryValidationCondition;
+ uint8_t bNumberMessage;
+ uint16_t wLangId;
+ uint8_t bMsgIndex;
+ uint8_t bTecPrologue;
+ uint8_t abPINApdu[0];
+} __attribute__ ((packed));
+/* Response: RDR_to_PC_DataBlock */
+
+/* Section 6.1.12 */
+struct ccid_pc_to_rdr_mechanical {
+ struct ccid_header hdr;
+ uint8_t bFunction; /* ccid_mech_function */
+ uint8_t abRFU[2];
+} __attribute__ ((packed));
+enum ccid_mech_function {
+ CCID_MECH_FN_ACCEPT_CARD = 0x01,
+ CCID_MECH_FN_EJECT_CARD = 0x02,
+ CCID_MECH_FN_CAPTURE_CARD = 0x03,
+ CCID_MECH_FN_LOCK_CARD = 0x04,
+ CCID_MECH_FN_UNLOCK_CARD = 0x05,
+};
+/* Response: RDR_to_PC_SlotStatus */
+
+/* Section 6.1.13 */
+struct ccid_pc_to_rdr_abort {
+ struct ccid_header hdr;
+ uint8_t abRFU[3];
+} __attribute__ ((packed));
+/* Response: RDR_to_PC_SlotStatus */
+
+/* Section 6.1.14 */
+struct ccid_pc_to_rdr_set_rate_and_clock {
+ struct ccid_header hdr;
+ uint8_t abRFU[3];
+ uint32_t dwClockFrequency;
+ uint32_t dwDataRate;
+} __attribute__ ((packed));
+/* Response: RDR_to_PC_DataRateAndClockFrequency */
+
+union ccid_pc_to_rdr {
+ struct ccid_pc_to_rdr_icc_power_on icc_power_on;
+ struct ccid_pc_to_rdr_icc_power_off icc_power_off;
+ struct ccid_pc_to_rdr_get_slot_status get_slot_status;
+ struct ccid_pc_to_rdr_xfr_block xfr_block;
+ struct ccid_pc_to_rdr_get_parameters get_parameters;
+ struct ccid_pc_to_rdr_reset_parameters reset_parameters;
+ struct ccid_pc_to_rdr_set_parameters set_parameters;
+ struct ccid_pc_to_rdr_escape escape;
+ struct ccid_pc_to_rdr_icc_clock icc_clock;
+ struct ccid_pc_to_rdr_t0apdu t0apdu;
+ struct ccid_pc_to_rdr_secure secure;
+ struct ccid_pc_to_rdr_mechanical mechanical;
+ struct ccid_pc_to_rdr_abort abort;
+ struct ccid_pc_to_rdr_set_rate_and_clock set_rate_and_clock;
+};
+
+/***********************************************************************
+ * Bulk IN
+ ***********************************************************************/
+
+/* CCID message header on BULK-IN endpoint */
+struct ccid_header_in {
+ struct ccid_header hdr;
+ uint8_t bStatus;
+ uint8_t bError;
+} __attribute__ ((packed));
+
+/* Section 6.2.1 RDR_to_PC_DataBlock */
+struct ccid_rdr_to_pc_data_block {
+ struct ccid_header_in hdr;
+ uint8_t bChainParameter;
+ uint8_t abData[0];
+} __attribute__ ((packed));
+
+/* Section 6.2.2 RDR_to_PC_SlotStatus */
+struct ccid_rdr_to_pc_slot_status {
+ struct ccid_header_in hdr;
+ uint8_t bClockStatus;
+} __attribute__ ((packed));
+enum ccid_clock_status {
+ CCID_CLOCK_STATUS_RUNNING = 0x00,
+ CCID_CLOCK_STATUS_STOPPED_L = 0x01,
+ CCID_CLOCK_STATUS_STOPPED_H = 0x02,
+ CCID_CLOCK_STATUS_STOPPED_UNKN = 0x03,
+};
+
+/* Section 6.2.3 RDR_to_PC_Parameters */
+struct ccid_rdr_to_pc_parameters {
+ struct ccid_header_in hdr;
+ union {
+ struct ccid_proto_data_t0 t0;
+ struct ccid_proto_data_t1 t1;
+ } abProtocolData;
+} __attribute__ ((packed));
+
+/* Section 6.2.4 RDR_to_PC_Escape */
+struct ccid_rdr_to_pc_escape {
+ struct ccid_header hdr;
+ uint8_t bRFU;
+ uint8_t abData[0];
+} __attribute__ ((packed));
+
+/* Section 6.2.5 RDR_to_PC_DataRateAndClockFrequency */
+struct ccid_rdr_to_pc_data_rate_and_clock {
+ struct ccid_header hdr;
+ uint8_t bRFU;
+ uint32_t dwClockFrequency;
+ uint32_t dwDataRate;
+} __attribute__ ((packed));
+
+/* Section 6.2.6 */
+#define CCID_ICC_STATUS_MASK 0x03
+#define CCID_ICC_STATUS_PRES_ACT 0x00
+#define CCID_ICC_STATUS_PRES_INACT 0x01
+#define CCID_ICC_STATUS_NO_ICC 0x02
+#define CCID_CMD_STATUS_MASK 0xC0
+#define CCID_CMD_STATUS_OK 0x00
+#define CCID_CMD_STATUS_FAILED 0x40
+#define CCID_CMD_STATUS_TIME_EXT 0x80
+/* Table 6.2-2: Slot Error value when bmCommandStatus == 1 */
+enum ccid_error_code {
+ CCID_ERR_CMD_ABORTED = 0xff,
+ CCID_ERR_ICC_MUTE = 0xfe,
+ CCID_ERR_XFR_PARITY_ERROR = 0xfd,
+ CCID_ERR_XFR_OVERRUN = 0xfc,
+ CCID_ERR_HW_ERROR = 0xfb,
+ CCID_ERR_BAD_ATR_TS = 0xf8,
+ CCID_ERR_BAD_ATR_TCK = 0xf7,
+ CCID_ERR_ICC_PROTOCOL_NOT_SUPPORTED = 0xf6,
+ CCID_ERR_ICC_CLASS_NOT_SUPPORTED = 0xf5,
+ CCID_ERR_PROCEDURE_BYTE_CONFLICT = 0xf4,
+ CCID_ERR_DEACTIVATED_PROTOCOL = 0xf3,
+ CCID_ERR_BUSY_WITH_AUTO_SEQUENCE = 0xf2,
+ CCID_ERR_PIN_TIMEOUT = 0xf0,
+ CCID_ERR_PIN_CANCELLED = 0xef,
+ CCID_ERR_CMD_SLOT_BUSY = 0xe0,
+ CCID_ERR_CMD_NOT_SUPPORTED = 0x00
+};
+
+union ccid_rdr_to_pc {
+ struct ccid_rdr_to_pc_data_block data_block;
+ struct ccid_rdr_to_pc_slot_status slot_status;
+ struct ccid_rdr_to_pc_parameters parameters;
+ struct ccid_rdr_to_pc_escape escape;
+ struct ccid_rdr_to_pc_data_rate_and_clock rate_and_clock;
+};
+
+/***********************************************************************
+ * Interupt IN
+ ***********************************************************************/
+
+/* Section 6.3.1 */
+struct ccid_rdr_to_pc_notify_slot_change {
+ uint8_t bMessageType;
+ uint8_t bmSlotCCState[0]; /* as long as bNumSlots/4 padded to next byte */
+} __attribute__ ((packed));
+
+/* Section 6.3.1 */
+struct ccid_rdr_to_pc_hardware_error {
+ struct ccid_header hdr;
+ uint8_t bHardwareErrorCode;
+} __attribute__ ((packed));
+
+union ccid_rdr_to_pc_irq {
+ struct ccid_rdr_to_pc_notify_slot_change slot_change;
+ struct ccid_rdr_to_pc_hardware_error hw_error;
+};
diff --git a/ccid/create_ccid_gadget.sh b/ccid/create_ccid_gadget.sh
new file mode 100755
index 0000000..aafb152
--- /dev/null
+++ b/ccid/create_ccid_gadget.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+GADGET_NAME=osmo-ccid
+
+
+
+GADGET_CONFIGFS=/sys/kernel/config/usb_gadget
+
+die() {
+ echo ERROR: $1
+ exit 2
+}
+
+modprobe configfs
+modprobe usb_f_fs
+modprobe dummy_hcd is_high_speed=0 is_super_speed=0
+
+[ -d $GADGET_CONFIGFS ] || die "usb_gadget configfs not mounted"
+
+gadgetdir="$GADGET_CONFIGFS/$GADGET_NAME"
+
+# create gadget
+[ -d $gadgetdir ] || mkdir $gadgetdir || die "Cannot create $gadgetdir. Permission problem?"
+set -e -x
+cd $gadgetdir
+echo 0x2342 > idVendor
+echo 0x2342 > idProduct
+[ -d strings/0x409 ] || mkdir strings/0x409
+echo 2342 > strings/0x409/serialnumber
+echo "sysmocom GmbH" > strings/0x409/manufacturer
+echo "sysmoOCTSIM" > strings/0x409/product
+
+# create config
+[ -d configs/c.1 ] || mkdir configs/c.1
+[ -d configs/c.1/strings/0x409 ] || mkdir configs/c.1/strings/0x409
+echo "sysmoOCTSIM config" > configs/c.1/strings/0x409/configuration
+
+[ -d functions/ffs.usb0 ] || mkdir functions/ffs.usb0
+[ -e configs/c.1/ffs.usb0 ] || ln -s functions/ffs.usb0 configs/c.1
+
+[ -d /dev/ffs-ccid ] || mkdir /dev/ffs-ccid
+[ -e /dev/ffs-ccid/ep0 ] || mount -t functionfs usb0 /dev/ffs-ccid/
+
+# enable device, only works after program has opened EP FDs
+#echo dummy_udc.0 > UDC
+