/* * Handle non standard and non device calls. * * return non-zero if the call was handled. */ int hid_handle_control(struct usb_ctrl_req_t *req, void *data) { struct hid_ctx *ctx = data; const void *buf = NULL; size_t len = 0; if (req->type == USB_CTRL_REQ_CLASS) return (hid_handle_control_class(req, ctx)); switch (req->bRequest) { case USB_CTRL_REQ_GET_DESCRIPTOR: if (req->wValueHigh == USB_HID_REPORT_DESC_TYPE_REPORT) { buf = ctx->hidf->report_desc; len = ctx->hidf->report_desc_size; } else { if (ctx->hidf->get_descriptor) len = ctx->hidf->get_descriptor(req->wValueHigh, req->wValueLow, &buf); } if (len == 0) return (0); usb_ep0_tx_cp(buf, len, req->wLength, NULL, NULL); usb_handle_control_status(0); return (1); case USB_CTRL_REQ_SET_DESCRIPTOR: // TODO return (0); default: return (0); } }
/* Windows 7 fails to configure device unless this is implemented */ static int cdc_handle_control(struct usb_ctrl_req_t *req, void *data) { struct cdc_ctx *ctx = data; switch ((enum cdc_ctrl_req_code)req->bRequest) { case USB_CTRL_REQ_CDC_SET_LINE_CODING: { if (req->wLength != sizeof(struct cdc_line_coding)) { usb_handle_control_status(1); } else { usb_ep0_rx(&ctx->line_coding, req->wLength, cdc_handle_control_set_line_coding, ctx); return 1; } break; } case USB_CTRL_REQ_CDC_GET_LINE_CODING: { usb_ep0_tx_cp(&ctx->line_coding, sizeof(struct cdc_line_coding), req->wLength, NULL, NULL); usb_handle_control_status(0); break; } case USB_CTRL_REQ_CDC_SET_CTRL_LINE_STATE: { /* * We should remain inactive unless there is a terminal on the other end of the link, * indicated by the first two bits of wValue */ ctx->control_lines = req->wValue; usb_handle_control_status(0); break; } default: return 0; } return 0; }
static int usb_tx_config_desc(int idx, int reqlen) { const struct usb_desc_config_t *d = usb.identity->configs[idx]->desc; usb_ep0_tx_cp(d, d->wTotalLength, reqlen, NULL, NULL); return (0); }
static int usb_tx_string_desc(int idx, int reqlen) { const struct usb_desc_string_t * const *d; for (d = usb.identity->string_descs; idx != 0 && *d != NULL; ++d) --idx; if (*d == NULL) return (-1); usb_ep0_tx_cp(*d, (*d)->bLength, reqlen, NULL, NULL); return (0); }
__noinline void usb_handle_control_status_cb(ep_callback_t cb, void *cbdata) { /* empty status transfer */ switch (usb.ctrl_dir) { case USB_CTRL_REQ_IN: usbd_pipe_state[USBD_PIPE_EP0_RX].data01 = USB_DATA01_DATA1; usb_rx(&usbd_pipe_state[USBD_PIPE_EP0_RX], NULL, 0, cb, cbdata); break; default: usbd_pipe_state[USBD_PIPE_EP0_TX].data01 = USB_DATA01_DATA1; usb_ep0_tx_cp(NULL, 0, 1 /* short packet */, cb, cbdata); break; } }
static int usb_tx_string_desc(int idx, int reqlen) { const struct usb_desc_string_t * const *d; for (d = usb.identity->string_descs; idx != 0 && *d != NULL; ++d) --idx; switch ((uintptr_t)*d) { case (uintptr_t)NULL: return (-1); case (uintptr_t)USB_DESC_STRING_SERIALNO: return (usb_tx_serialno(reqlen)); default: usb_ep0_tx_cp(*d, (*d)->bLength, reqlen, NULL, NULL); return (0); } }
__noinline void usb_handle_control_status_cb(ep_callback_t cb) { /* empty status transfer */ switch (usb.ctrl_dir) { case USB_CTRL_REQ_IN: usbd_ep_state[0].rx.data01 = USB_DATA01_DATA1; usb_rx(&usbd_ep_state[0].rx, NULL, 0, cb, NULL); break; default: usbd_ep_state[0].tx.data01 = USB_DATA01_DATA1; usb_ep0_tx_cp(NULL, 0, 1 /* short packet */, cb, NULL); break; } }
void usb_handle_control_status(int fail) { if (fail) { usb_pipe_stall(&usb.ep_state[0].rx); usb_pipe_stall(&usb.ep_state[0].tx); return; } /* empty status transfer */ switch (usb.ctrl_dir) { case USB_CTRL_REQ_IN: usb.ep_state[0].rx.data01 = USB_DATA01_DATA1; usb_rx(&usb.ep_state[0].rx, NULL, 0, usb_handle_control_done, NULL); break; default: usb.ep_state[0].tx.data01 = USB_DATA01_DATA1; usb_ep0_tx_cp(NULL, 0, 1 /* short packet */, usb_handle_control_done, NULL); break; } }
static void usb_handle_control(void *data, ssize_t len, void *cbdata) { struct usb_ctrl_req_t *req = data; uint16_t zero16 = 0; int fail = 1; usb_clear_transfers(); usb.ctrl_dir = req->in; if (req->type != USB_CTRL_REQ_STD) { usb_handle_control_nonstd(req); return; } /* Only STD requests here */ switch (req->bRequest) { case USB_CTRL_REQ_GET_STATUS: /** * Because we don't support remote wakeup or * self-powered operation, and we are specialized to * only EP 0 so far, all GET_STATUS replies are just * empty. */ usb_ep0_tx_cp(&zero16, sizeof(zero16), req->wLength, NULL, NULL); break; case USB_CTRL_REQ_CLEAR_FEATURE: case USB_CTRL_REQ_SET_FEATURE: /** * Nothing to do. Maybe return STALLs on illegal * accesses? */ break; case USB_CTRL_REQ_SET_ADDRESS: /** * We must keep our previous address until the end of * the status stage; therefore we can't set the * address right now. Since this is a special case, * the EP 0 handler will take care of this later on. */ usb.address = req->wValue & 0x7f; usb.state = USBD_STATE_SETTING_ADDRESS; break; case USB_CTRL_REQ_GET_DESCRIPTOR: switch (req->wValue >> 8) { case USB_DESC_DEV: usb_ep0_tx_cp(usb.identity->dev_desc, usb.identity->dev_desc->bLength, req->wLength, NULL, NULL); fail = 0; break; case USB_DESC_CONFIG: fail = usb_tx_config_desc(req->wValue & 0xff, req->wLength); break; case USB_DESC_STRING: fail = usb_tx_string_desc(req->wValue & 0xff, req->wLength); break; default: fail = -1; break; } /* we set fail already, so we can go directly to `err' */ goto err; case USB_CTRL_REQ_GET_CONFIGURATION: usb_ep0_tx_cp(&usb.config, 1, req->wLength, NULL, NULL); /* XXX implicit LE */ break; case USB_CTRL_REQ_SET_CONFIGURATION: if (usb_set_config(req->wValue) < 0) goto err; break; case USB_CTRL_REQ_GET_INTERFACE: /* We only support iface setting 0 */ usb_ep0_tx_cp(&zero16, 1, req->wLength, NULL, NULL); break; case USB_CTRL_REQ_SET_INTERFACE: if (usb_set_interface(req->wIndex, req->wValue) < 0) goto err; break; default: goto err; } fail = 0; err: usb_handle_control_status(fail); }
int dfu_handle_control(struct usb_ctrl_req_t *req, void *data) { struct dfu_ctx *ctx = data; int fail = 1; /* XXX check for std vs class request */ switch ((uint8_t)req->bRequest) { case USB_CTRL_REQ_SET_INTERFACE: if (req->wValue >= ctx->dfuf->segment_count) goto out; ctx->segment = req->wValue; ctx->state = DFU_STATE_dfuIDLE; break; case USB_CTRL_REQ_GET_INTERFACE: usb_ep0_tx_cp(&ctx->segment, 1, req->wLength, NULL, NULL); break; case USB_CTRL_REQ_DFU_DNLOAD: { void *buf; switch (ctx->state) { case DFU_STATE_dfuIDLE: ctx->off = 0; break; case DFU_STATE_dfuDNLOAD_IDLE: break; default: goto err; } /** * XXX we are not allowed to STALL here, and we need to eat all transferred data. * better not allow setup_write to break the protocol. */ ctx->status = ctx->dfuf->segment[ctx->segment].setup_write(ctx, ctx->off, req->wLength, &buf); if (ctx->status != DFU_STATUS_OK) { ctx->state = DFU_STATE_dfuERROR; goto err_have_status; } if (req->wLength > 0) { ctx->state = DFU_STATE_dfuDNLOAD_SYNC; usb_ep0_rx(buf, req->wLength, dfu_dnload_complete, ctx); } else { ctx->state = DFU_STATE_dfuMANIFEST_SYNC; dfu_dnload_complete(NULL, 0, ctx); } goto out_no_status; } case USB_CTRL_REQ_DFU_GETSTATUS: { struct dfu_status_t st; st.bState = ctx->state; st.bStatus = ctx->status; st.bwPollTimeout = 1; /* XXX allow setting in desc? */ /** * If we're in DFU_STATE_dfuMANIFEST, we just finished * the download, and we're just about to send our last * status report. Once the report has been sent, go * and reset the system to put the new firmware into * effect. */ usb_ep0_tx_cp(&st, sizeof(st), req->wLength, NULL, NULL); break; } case USB_CTRL_REQ_DFU_CLRSTATUS: if (ctx->state != DFU_STATE_dfuERROR) goto err; ctx->state = DFU_STATE_dfuIDLE; ctx->status = DFU_STATUS_OK; break; case USB_CTRL_REQ_DFU_GETSTATE: { uint8_t st = ctx->state; usb_ep0_tx_cp(&st, sizeof(st), req->wLength, NULL, NULL); break; } case USB_CTRL_REQ_DFU_ABORT: switch (ctx->state) { case DFU_STATE_dfuIDLE: case DFU_STATE_dfuDNLOAD_IDLE: /* case DFU_STATE_dfuUPLOAD_IDLE: */ ctx->state = DFU_STATE_dfuIDLE; break; default: goto err; } break; /* case USB_CTRL_REQ_DFU_UPLOAD: */ default: return (0); } fail = 0; goto out; err: ctx->status = DFU_STATUS_errSTALLEDPKT; err_have_status: ctx->state = DFU_STATE_dfuERROR; out: usb_handle_control_status(fail); out_no_status: return (1); }
/* * Handle class (HID) specific calls. * * see hid_handle_control() */ static int hid_handle_control_class(struct usb_ctrl_req_t *req, struct hid_ctx *ctx) { size_t len = 0; /* XXX maintain state for all report descriptors */ switch ((enum hid_ctrl_req_code)req->bRequest) { case USB_CTRL_REQ_HID_GET_REPORT: { enum hid_report_type report_type = req->wValue >> 8; uint8_t report_id = req->wValue & 0xff; int ret = -1; ctx->get_report_outstanding_length = req->wLength; if (ctx->hidf->get_report) ret = ctx->hidf->get_report(ctx, report_type, report_id); if (ret <= 0) { ctx->get_report_outstanding_length = 0; usb_handle_control_status(1); } return (1); } case USB_CTRL_REQ_HID_SET_REPORT: { int ret = -1; void *buf = NULL; ctx->set_report_length = req->wLength; ctx->set_report_type = req->wValue >> 8; ctx->set_report_id = req->wValue & 0xff; if (ctx->hidf->set_report) ret = ctx->hidf->set_report(ctx->set_report_type, ctx->set_report_id, &buf, ctx->set_report_length); if (ret > 0) usb_ep0_rx(buf, ctx->set_report_length, hid_set_report_done, ctx); else usb_handle_control_status(1); return (1); } case USB_CTRL_REQ_HID_GET_IDLE: /* XXX implement */ usb_ep0_tx_cp(&len, 1, req->wLength, NULL, NULL); usb_handle_control_status(0); return (1); case USB_CTRL_REQ_HID_SET_IDLE: /* XXX implement */ usb_handle_control_status(0); return (1); case USB_CTRL_REQ_HID_GET_PROTOCOL: /* XXX implement */ /* usb_ep0_tx_cp(&len, 1, req->wLength, NULL, NULL); */ /* usb_handle_control_status(0); */ return (0); case USB_CTRL_REQ_HID_SET_PROTOCOL: /* XXX implement */ usb_handle_control_status(0); return (1); default: return (0); } }