From 6982fabbc92511323c33ff691833371df4e451c8 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 16 May 2019 23:01:35 +0200 Subject: ccid: Implement class-specific requests in core ccid_device.c layer This has been tested succesfully with usb_f_fs for GET_{CLOCK_FREQS,DATA_RATES}. The ABORT handling is currently just a stub that needs to be completed once the state machine handling for asynchronously dispatched requests becomes more clear. Change-Id: I21d9d9f125ad4c0056d26c575faca5ecad689134 --- ccid/ccid_device.c | 110 +++++++++++++++++++++++++++++++++++++++++++- ccid/ccid_device.h | 20 +++++++- ccid/ccid_main_functionfs.c | 54 ++++++++++++++++++---- 3 files changed, 173 insertions(+), 11 deletions(-) diff --git a/ccid/ccid_device.c b/ccid/ccid_device.c index 70553b8..346f77d 100644 --- a/ccid/ccid_device.c +++ b/ccid/ccid_device.c @@ -11,6 +11,15 @@ #include "ccid_proto.h" #include "ccid_device.h" +/* local, stand-alone definition of a USB control request */ +struct _usb_ctrl_req { + uint8_t bRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} __attribute__ ((packed));; + /* decode on-the-wire T0 parameters into their parsed form */ static int decode_ccid_pars_t0(struct ccid_pars_decoded *out, const struct ccid_proto_data_t0 *in) { @@ -758,13 +767,112 @@ short_msg: return -1; } +/* Section 5.3.1 ABORT */ +static int ccid_handle_ctrl_abort(struct ccid_instance *ci, const struct _usb_ctrl_req *req) +{ + uint16_t w_value = osmo_load16le(&req->wValue); + uint8_t slot_nr = w_value & 0xff; + uint8_t seq = w_value >> 8; + struct ccid_slot *cs; + + if (slot_nr >= ARRAY_SIZE(ci->slot)) + return CCID_CTRL_RET_INVALID; + + cs = &ci->slot[slot_nr]; + + LOGP(DCCID, LOGL_NOTICE, "Not handling PC_to_RDR_Abort; please implement it\n"); + /* Upon receiving the Control pipe ABORT request the CCID should check + * the state of the requested slot. */ + + /* If the last Bulk-OUT message received by the CCID was a + * PC_to_RDR_Abort command with the same bSlot and bSeq as the ABORT + * request, then the CCID will respond to the Bulk-OUT message with + * the RDR_to_PC_SlotStatus response. */ + + /* FIXME */ + + /* If the previous Bulk-OUT message received by the CCID was not a + * PC_to_RDR_Abort command with the same bSlot and bSeq as the ABORT + * request, then the CCID will fail all Bulk-Out commands to that slot + * until the PC_to_RDR_Abort command with the same bSlot and bSeq is + * received. Bulk-OUT commands will be failed by sending a response + * with bmCommandStatus=Failed and bError=CMD_ABORTED. */ + + /* FIXME */ + return CCID_CTRL_RET_OK; +} + +/* Section 5.3.2 GET_CLOCK_FREQUENCIES */ +static int ccid_handle_ctrl_get_clock_freq(struct ccid_instance *ci, const struct _usb_ctrl_req *req, + const uint8_t **data_in) +{ + uint16_t len = osmo_load16le(&req->wLength); + + if (len != sizeof(uint32_t) * ci->class_desc->bNumClockSupported) + return CCID_CTRL_RET_INVALID; + + *data_in = (const uint8_t *) ci->clock_freqs; + return CCID_CTRL_RET_OK; +} + +/* Section 5.3.3 GET_DATA_RATES */ +static int ccid_handle_ctrl_get_data_rates(struct ccid_instance *ci, const struct _usb_ctrl_req *req, + const uint8_t **data_in) +{ + uint16_t len = osmo_load16le(&req->wLength); + + if (len != sizeof(uint32_t) * ci->class_desc->bNumClockSupported) + return CCID_CTRL_RET_INVALID; + + *data_in = (const uint8_t *) ci->data_rates; + return CCID_CTRL_RET_OK; +} + +/*! Handle [class specific] CTRL request. We assume the caller has already verified that the + * request was made to the correct interface as well as it is a class-specific request. + * \param[in] ci CCID Instance for which CTRL request was received + * \param[in] ctrl_req buffer holding the 8 bytes CTRL transfer header + * \param[out] data_in data to be returned to the host in the IN transaction (if any) + * \returns CCID_CTRL_RET_OK, CCID_CTRL_RET_INVALID or CCID_CTRL_RET_UNKNOWN + */ +int ccid_handle_ctrl(struct ccid_instance *ci, const uint8_t *ctrl_req, const uint8_t **data_in) +{ + const struct _usb_ctrl_req *req = (const struct _usb_ctrl_req *) ctrl_req; + int rc; + + LOGPCI(ci, LOGL_DEBUG, "CTRL bmReqT=0x%02X bRequest=%s, wValue=0x%04X, wIndex=0x%04X, wLength=%d\n", + req->bRequestType, get_value_string(ccid_class_spec_req_vals, req->bRequest), + req->wValue, req->wIndex, req->wLength); + + switch (req->bRequest) { + case CLASS_SPEC_CCID_ABORT: + rc = ccid_handle_ctrl_abort(ci, req); + break; + case CLASS_SPEC_CCID_GET_CLOCK_FREQ: + rc = ccid_handle_ctrl_get_clock_freq(ci, req, data_in); + break; + case CLASS_SPEC_CCID_GET_DATA_RATES: + rc = ccid_handle_ctrl_get_data_rates(ci, req, data_in); + break; + default: + return CCID_CTRL_RET_UNKNOWN; + } + return rc; +} + void ccid_instance_init(struct ccid_instance *ci, const struct ccid_ops *ops, - const struct ccid_slot_ops *slot_ops, const char *name, void *priv) + const struct ccid_slot_ops *slot_ops, + const struct usb_ccid_class_descriptor *class_desc, + const uint32_t *data_rates, const uint32_t *clock_freqs, + const char *name, void *priv) { int i; ci->ops = ops; ci->slot_ops = slot_ops; + ci->class_desc = class_desc; + ci->clock_freqs = clock_freqs; + ci->data_rates = data_rates; ci->name = name; ci->priv = priv; diff --git a/ccid/ccid_device.h b/ccid/ccid_device.h index 7eba842..d9cba52 100644 --- a/ccid/ccid_device.h +++ b/ccid/ccid_device.h @@ -96,6 +96,12 @@ struct ccid_instance { /* set of function pointers implementing specific operations */ const struct ccid_ops *ops; const struct ccid_slot_ops *slot_ops; + /* USB CCID Class Specific Descriptor */ + const struct usb_ccid_class_descriptor *class_desc; + /* array of permitted data rates; length: bNumDataRatesSupported */ + const uint32_t *data_rates; + /* array of permitted clock frequencies; length: bNumClockSupported */ + const uint32_t *clock_freqs; const char *name; /* user-supplied opaque data */ void *priv; @@ -111,5 +117,17 @@ struct msgb *ccid_gen_data_block(struct ccid_slot *cs, uint8_t seq, uint8_t cmd_ uint32_t data_len); void ccid_instance_init(struct ccid_instance *ci, const struct ccid_ops *ops, - const struct ccid_slot_ops *slot_ops, const char *name, void *priv); + const struct ccid_slot_ops *slot_ops, + const struct usb_ccid_class_descriptor *class_desc, + const uint32_t *data_rates, const uint32_t *clock_freqs, + const char *name, void *priv); int ccid_handle_out(struct ccid_instance *ci, struct msgb *msg); + +/* Invalid request received: Please return STALL */ +#define CCID_CTRL_RET_INVALID -1 +/* Unknown request received: Not something CCID is supposed to handle */ +#define CCID_CTRL_RET_UNKNOWN 0 +/* Request OK. In case of a CTRL-IN: continue by sending wLength bytes to host */ +#define CCID_CTRL_RET_OK 1 + +int ccid_handle_ctrl(struct ccid_instance *ci, const uint8_t *ctrl_req, const uint8_t **data_in); diff --git a/ccid/ccid_main_functionfs.c b/ccid/ccid_main_functionfs.c index 62bc402..81cf225 100644 --- a/ccid/ccid_main_functionfs.c +++ b/ccid/ccid_main_functionfs.c @@ -24,6 +24,14 @@ * Actual USB CCID Descriptors ***********************************************************************/ +static uint32_t clock_freqs[] = { + 2500000 +}; + +static uint32_t data_rates[] = { + 9600 +}; + static const struct { struct usb_functionfs_descs_head_v2 header; __le32 fs_count; @@ -56,12 +64,12 @@ static const struct { .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, + .dwDefaultClock = cpu_to_le32(2500000), + .dwMaximumClock = cpu_to_le32(20000000), + .bNumClockSupported = ARRAY_SIZE(clock_freqs), .dwDataRate = cpu_to_le32(9600), .dwMaxDataRate = cpu_to_le32(921600), - .bNumDataRatesSupported = 0, + .bNumDataRatesSupported = ARRAY_SIZE(data_rates), .dwMaxIFSD = cpu_to_le32(0), .dwSynchProtocols = cpu_to_le32(0), .dwMechanical = cpu_to_le32(0), @@ -167,6 +175,8 @@ struct ufunc_handle { struct ccid_instance *ccid_handle; }; +static struct ccid_instance g_ci; + static int ep_int_cb(struct osmo_fd *ofd, unsigned int what) { LOGP(DUSB, LOGL_DEBUG, "%s\n", __func__); @@ -212,12 +222,38 @@ const struct value_string ffs_evt_type_names[] = { { 0, NULL } }; -static void handle_setup(const struct usb_ctrlrequest *setup) +static void handle_setup(int fd, const struct usb_ctrlrequest *setup) { + const uint8_t *data_in = NULL; + int rc; + LOGP(DUSB, LOGL_NOTICE, "EP0 SETUP bRequestType=0x%02x, bRequest=0x%02x wValue=0x%04x, " "wIndex=0x%04x, wLength=%u\n", setup->bRequestType, setup->bRequest, le16_to_cpu(setup->wValue), le16_to_cpu(setup->wIndex), le16_to_cpu(setup->wLength)); + /* FIXME: Handle control transfer */ + rc = ccid_handle_ctrl(&g_ci, (const uint8_t *) setup, &data_in); + switch (rc) { + case CCID_CTRL_RET_INVALID: + if (setup->bRequestType & USB_DIR_IN) + read(fd, NULL, 0); /* cause stall */ + else + write(fd, NULL, 0); /* cause stall */ + break; + case CCID_CTRL_RET_UNKNOWN: + /* FIXME: is this correct behavior? */ + if (setup->bRequestType & USB_DIR_IN) + write(fd, NULL, 0); /* send ZLP */ + else + read(fd, NULL, 0); + break; + case CCID_CTRL_RET_OK: + if (setup->bRequestType & USB_DIR_IN) + write(fd, data_in, le16_to_cpu(setup->wLength)); + else + read(fd, NULL, 0); /* FIXME: control OUT? */ + break; + } } static void aio_refill_out(struct ufunc_handle *uh); @@ -238,7 +274,7 @@ static int ep_0_cb(struct osmo_fd *ofd, unsigned int what) aio_refill_out(uh); break; case FUNCTIONFS_SETUP: - handle_setup(&evt.u.setup); + handle_setup(ofd->fd, &evt.u.setup); break; } @@ -501,7 +537,6 @@ static void signal_handler(int signal) int main(int argc, char **argv) { struct ufunc_handle ufh = (struct ufunc_handle) { 0, }; - struct ccid_instance ci = (struct ccid_instance) { 0, }; int rc; tall_main_ctx = talloc_named_const(NULL, 0, "ccid_main_functionfs"); @@ -510,8 +545,9 @@ int main(int argc, char **argv) signal(SIGUSR1, &signal_handler); - ccid_instance_init(&ci, &c_ops, &slotsim_slot_ops, "", &ufh); - ufh.ccid_handle = &ci; + ccid_instance_init(&g_ci, &c_ops, &slotsim_slot_ops, &descriptors.fs_descs.ccid, + data_rates, clock_freqs, "", &ufh); + ufh.ccid_handle = &g_ci; if (argc < 2) { fprintf(stderr, "You have to specify the mount-path of the functionfs\n"); -- cgit v1.2.3