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
This commit is contained in:
parent
29ae5d45d7
commit
6982fabbc9
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in New Issue