summaryrefslogtreecommitdiffstats
path: root/firmware/usbstack/usb_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/usbstack/usb_core.c')
-rw-r--r--firmware/usbstack/usb_core.c369
1 files changed, 262 insertions, 107 deletions
diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c
index b291dc7655..9fe8b3e603 100644
--- a/firmware/usbstack/usb_core.c
+++ b/firmware/usbstack/usb_core.c
@@ -22,6 +22,7 @@
#include "thread.h"
#include "kernel.h"
#include "string.h"
+#include "panic.h"
/*#define LOGF_ENABLE*/
#include "logf.h"
@@ -90,9 +91,9 @@ static struct usb_device_descriptor __attribute__((aligned(2)))
.idVendor = USB_VENDOR_ID,
.idProduct = USB_PRODUCT_ID,
.bcdDevice = 0x0100,
- .iManufacturer = 1,
- .iProduct = 2,
- .iSerialNumber = 3,
+ .iManufacturer = USB_STRING_INDEX_MANUFACTURER,
+ .iProduct = USB_STRING_INDEX_PRODUCT,
+ .iSerialNumber = USB_STRING_INDEX_SERIAL,
.bNumConfigurations = 1
} ;
@@ -141,12 +142,12 @@ static const struct usb_string_descriptor __attribute__((aligned(2)))
lang_descriptor =
USB_STRING_INITIALIZER(u"\x0409"); /* LANGID US English */
-static const struct usb_string_descriptor* const usb_strings[] =
+static const struct usb_string_descriptor* const usb_strings[USB_STRING_INDEX_MAX] =
{
- &lang_descriptor,
- &usb_string_iManufacturer,
- &usb_string_iProduct,
- &usb_string_iSerial
+ [USB_STRING_INDEX_LANGUAGE] = &lang_descriptor,
+ [USB_STRING_INDEX_MANUFACTURER] = &usb_string_iManufacturer,
+ [USB_STRING_INDEX_PRODUCT] = &usb_string_iProduct,
+ [USB_STRING_INDEX_SERIAL] = &usb_string_iSerial,
};
static int usb_address = 0;
@@ -172,7 +173,7 @@ static int usb_no_host_callback(struct timeout *tmo)
static int usb_core_num_interfaces;
typedef void (*completion_handler_t)(int ep, int dir, int status, int length);
-typedef bool (*control_handler_t)(struct usb_ctrlrequest* req,
+typedef bool (*control_handler_t)(struct usb_ctrlrequest* req, void* reqdata,
unsigned char* dest);
static struct
@@ -262,7 +263,15 @@ static struct usb_class_driver drivers[USB_NUM_DRIVERS] =
#endif
};
-static void usb_core_control_request_handler(struct usb_ctrlrequest* req);
+#ifdef USB_LEGACY_CONTROL_API
+static struct usb_ctrlrequest buffered_request;
+static struct usb_ctrlrequest* volatile active_request = NULL;
+static volatile unsigned int num_active_requests = 0;
+static void* volatile control_write_data = NULL;
+static volatile bool control_write_data_done = false;
+#endif
+
+static void usb_core_control_request_handler(struct usb_ctrlrequest* req, void* reqdata);
static unsigned char response_data[256] USB_DEVBSS_ATTR;
@@ -446,10 +455,10 @@ void usb_core_handle_transfer_completion(
case EP_CONTROL:
logf("ctrl handled %ld req=0x%x",
current_tick,
- ((struct usb_ctrlrequest*)event->data)->bRequest);
+ ((struct usb_ctrlrequest*)event->data[0])->bRequest);
usb_core_control_request_handler(
- (struct usb_ctrlrequest*)event->data);
+ (struct usb_ctrlrequest*)event->data[0], event->data[1]);
break;
default:
handler = ep_data[ep].completion_handler[EP_DIR(event->dir)];
@@ -560,7 +569,7 @@ static void allocate_interfaces_and_endpoints(void)
}
-static void control_request_handler_drivers(struct usb_ctrlrequest* req)
+static void control_request_handler_drivers(struct usb_ctrlrequest* req, void* reqdata)
{
int i, interface = req->wIndex & 0xff;
bool handled = false;
@@ -571,7 +580,7 @@ static void control_request_handler_drivers(struct usb_ctrlrequest* req)
drivers[i].first_interface <= interface &&
drivers[i].last_interface > interface)
{
- handled = drivers[i].control_request(req, response_data);
+ handled = drivers[i].control_request(req, reqdata, response_data);
if(handled)
break;
}
@@ -579,11 +588,11 @@ static void control_request_handler_drivers(struct usb_ctrlrequest* req)
if(!handled) {
/* nope. flag error */
logf("bad req:desc %d:%d", req->bRequest, req->wValue >> 8);
- usb_drv_stall(EP_CONTROL, true, true);
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
}
}
-static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req)
+static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req, void* reqdata)
{
int size;
const void* ptr = NULL;
@@ -637,8 +646,7 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req)
case USB_DT_STRING:
logf("STRING %d", index);
- if((unsigned)index < (sizeof(usb_strings) /
- sizeof(struct usb_string_descriptor*))) {
+ if((unsigned)index < USB_STRING_INDEX_MAX) {
size = usb_strings[index]->bLength;
ptr = usb_strings[index];
}
@@ -652,7 +660,7 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req)
}
else {
logf("bad string id %d", index);
- usb_drv_stall(EP_CONTROL, true, true);
+ ptr = NULL;
}
break;
@@ -663,8 +671,8 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req)
default:
logf("ctrl desc.");
- control_request_handler_drivers(req);
- break;
+ control_request_handler_drivers(req, reqdata);
+ return;
}
if(ptr) {
@@ -674,8 +682,9 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req)
if (ptr != response_data)
memcpy(response_data, ptr, length);
- usb_drv_recv(EP_CONTROL, NULL, 0);
- usb_drv_send(EP_CONTROL, response_data, length);
+ usb_drv_control_response(USB_CONTROL_ACK, response_data, length);
+ } else {
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
}
}
@@ -719,105 +728,105 @@ static void usb_core_do_clear_feature(int recip, int recip_nr, int feature)
}
}
-static void request_handler_device(struct usb_ctrlrequest* req)
+static void request_handler_device(struct usb_ctrlrequest* req, void* reqdata)
{
+ unsigned address;
+
switch(req->bRequest) {
- case USB_REQ_GET_CONFIGURATION: {
- logf("usb_core: GET_CONFIG");
- response_data[0] = (usb_state == ADDRESS ? 0 : 1);
- usb_drv_recv(EP_CONTROL, NULL, 0);
- usb_drv_send(EP_CONTROL, response_data, 1);
- break;
- }
- case USB_REQ_SET_CONFIGURATION: {
- usb_drv_cancel_all_transfers();
- usb_core_do_set_config(req->wValue);
- usb_drv_send(EP_CONTROL, NULL, 0);
- break;
- }
- case USB_REQ_SET_ADDRESS: {
- unsigned char address = req->wValue;
- usb_drv_send(EP_CONTROL, NULL, 0);
- usb_drv_cancel_all_transfers();
- usb_drv_set_address(address);
- usb_core_do_set_addr(address);
- break;
- }
+ case USB_REQ_GET_CONFIGURATION:
+ logf("usb_core: GET_CONFIG");
+ response_data[0] = (usb_state == ADDRESS ? 0 : 1);
+ usb_drv_control_response(USB_CONTROL_ACK, response_data, 1);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ usb_drv_cancel_all_transfers();
+ usb_core_do_set_config(req->wValue);
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
+ break;
+ case USB_REQ_SET_ADDRESS:
+ /* NOTE: We really have no business handling this and drivers
+ * should just handle it themselves. We don't care beyond
+ * knowing if we've been assigned an address yet, or not. */
+ address = req->wValue;
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
+ usb_drv_cancel_all_transfers();
+ usb_drv_set_address(address);
+ usb_core_do_set_addr(address);
+ break;
case USB_REQ_GET_DESCRIPTOR:
logf("usb_core: GET_DESC %d", req->wValue >> 8);
- request_handler_device_get_descriptor(req);
- break;
- case USB_REQ_CLEAR_FEATURE:
+ request_handler_device_get_descriptor(req, reqdata);
break;
case USB_REQ_SET_FEATURE:
if(req->wValue==USB_DEVICE_TEST_MODE) {
int mode = req->wIndex >> 8;
- usb_drv_send(EP_CONTROL, NULL, 0);
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
usb_drv_set_test_mode(mode);
+ } else {
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
}
break;
case USB_REQ_GET_STATUS:
response_data[0] = 0;
response_data[1] = 0;
- usb_drv_recv(EP_CONTROL, NULL, 0);
- usb_drv_send(EP_CONTROL, response_data, 2);
+ usb_drv_control_response(USB_CONTROL_ACK, response_data, 2);
break;
default:
logf("bad req:desc %d:%d", req->bRequest, req->wValue);
- usb_drv_stall(EP_CONTROL, true, true);
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
break;
}
}
-static void request_handler_interface_standard(struct usb_ctrlrequest* req)
+static void request_handler_interface_standard(struct usb_ctrlrequest* req, void* reqdata)
{
switch (req->bRequest)
{
case USB_REQ_SET_INTERFACE:
logf("usb_core: SET_INTERFACE");
- usb_drv_send(EP_CONTROL, NULL, 0);
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
break;
case USB_REQ_GET_INTERFACE:
logf("usb_core: GET_INTERFACE");
response_data[0] = 0;
- usb_drv_recv(EP_CONTROL, NULL, 0);
- usb_drv_send(EP_CONTROL, response_data, 1);
- break;
- case USB_REQ_CLEAR_FEATURE:
- break;
- case USB_REQ_SET_FEATURE:
+ usb_drv_control_response(USB_CONTROL_ACK, response_data, 1);
break;
case USB_REQ_GET_STATUS:
response_data[0] = 0;
response_data[1] = 0;
- usb_drv_recv(EP_CONTROL, NULL, 0);
- usb_drv_send(EP_CONTROL, response_data, 2);
+ usb_drv_control_response(USB_CONTROL_ACK, response_data, 2);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ /* TODO: These used to be ignored (erroneously).
+ * Should they be passed to the drivers instead? */
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
break;
default:
- control_request_handler_drivers(req);
+ control_request_handler_drivers(req, reqdata);
break;
}
}
-static void request_handler_interface(struct usb_ctrlrequest* req)
+static void request_handler_interface(struct usb_ctrlrequest* req, void* reqdata)
{
switch(req->bRequestType & USB_TYPE_MASK) {
case USB_TYPE_STANDARD:
- request_handler_interface_standard(req);
+ request_handler_interface_standard(req, reqdata);
break;
case USB_TYPE_CLASS:
- control_request_handler_drivers(req);
+ control_request_handler_drivers(req, reqdata);
break;
case USB_TYPE_VENDOR:
default:
logf("bad req:desc %d", req->bRequest);
- usb_drv_stall(EP_CONTROL, true, true);
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
break;
}
}
-static void request_handler_endoint_drivers(struct usb_ctrlrequest* req)
+static void request_handler_endpoint_drivers(struct usb_ctrlrequest* req, void* reqdata)
{
bool handled = false;
control_handler_t control_handler = NULL;
@@ -827,30 +836,30 @@ static void request_handler_endoint_drivers(struct usb_ctrlrequest* req)
ep_data[EP_NUM(req->wIndex)].control_handler[EP_DIR(req->wIndex)];
if(control_handler)
- handled = control_handler(req, response_data);
+ handled = control_handler(req, reqdata, response_data);
if(!handled) {
/* nope. flag error */
logf("usb bad req %d", req->bRequest);
- usb_drv_stall(EP_CONTROL, true, true);
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
}
}
-static void request_handler_endpoint_standard(struct usb_ctrlrequest* req)
+static void request_handler_endpoint_standard(struct usb_ctrlrequest* req, void* reqdata)
{
switch (req->bRequest) {
case USB_REQ_CLEAR_FEATURE:
usb_core_do_clear_feature(USB_RECIP_ENDPOINT,
req->wIndex,
req->wValue);
- usb_drv_send(EP_CONTROL, NULL, 0);
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
break;
case USB_REQ_SET_FEATURE:
logf("usb_core: SET FEATURE (%d)", req->wValue);
if(req->wValue == USB_ENDPOINT_HALT)
usb_drv_stall(EP_NUM(req->wIndex), true, EP_DIR(req->wIndex));
-
- usb_drv_send(EP_CONTROL, NULL, 0);
+
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
break;
case USB_REQ_GET_STATUS:
response_data[0] = 0;
@@ -859,35 +868,34 @@ static void request_handler_endpoint_standard(struct usb_ctrlrequest* req)
if(req->wIndex > 0)
response_data[0] = usb_drv_stalled(EP_NUM(req->wIndex),
EP_DIR(req->wIndex));
-
- usb_drv_recv(EP_CONTROL, NULL, 0);
- usb_drv_send(EP_CONTROL, response_data, 2);
+
+ usb_drv_control_response(USB_CONTROL_ACK, response_data, 2);
break;
default:
- request_handler_endoint_drivers(req);
+ request_handler_endpoint_drivers(req, reqdata);
break;
}
}
-static void request_handler_endpoint(struct usb_ctrlrequest* req)
+static void request_handler_endpoint(struct usb_ctrlrequest* req, void* reqdata)
{
switch(req->bRequestType & USB_TYPE_MASK) {
case USB_TYPE_STANDARD:
- request_handler_endpoint_standard(req);
+ request_handler_endpoint_standard(req, reqdata);
break;
case USB_TYPE_CLASS:
- request_handler_endoint_drivers(req);
+ request_handler_endpoint_drivers(req, reqdata);
break;
case USB_TYPE_VENDOR:
default:
logf("bad req:desc %d", req->bRequest);
- usb_drv_stall(EP_CONTROL, true, true);
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
break;
}
}
/* Handling USB requests starts here */
-static void usb_core_control_request_handler(struct usb_ctrlrequest* req)
+static void usb_core_control_request_handler(struct usb_ctrlrequest* req, void* reqdata)
{
#ifdef HAVE_USB_CHARGING_ENABLE
timeout_cancel(&usb_no_host_timeout);
@@ -905,20 +913,19 @@ static void usb_core_control_request_handler(struct usb_ctrlrequest* req)
switch(req->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
- request_handler_device(req);
+ request_handler_device(req, reqdata);
break;
case USB_RECIP_INTERFACE:
- request_handler_interface(req);
+ request_handler_interface(req, reqdata);
break;
case USB_RECIP_ENDPOINT:
- request_handler_endpoint(req);
+ request_handler_endpoint(req, reqdata);
break;
- case USB_RECIP_OTHER:
+ default:
logf("unsupported recipient");
- usb_drv_stall(EP_CONTROL, true, true);
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
break;
}
- //logf("control handled");
}
/* called by usb_drv_int() */
@@ -928,32 +935,47 @@ void usb_core_bus_reset(void)
usb_address = 0;
usb_state = DEFAULT;
#ifdef HAVE_USB_CHARGING_ENABLE
+#ifdef HAVE_USB_CHARGING_IN_THREAD
+ /* On some targets usb_charging_maxcurrent_change() cannot be called
+ * from an interrupt handler; get the USB thread to do it instead. */
+ usb_charger_update();
+#else
usb_charging_maxcurrent_change(usb_charging_maxcurrent());
#endif
+#endif
}
/* called by usb_drv_transfer_completed() */
void usb_core_transfer_complete(int endpoint, int dir, int status, int length)
{
- struct usb_transfer_completion_event_data *completion_event;
+ struct usb_transfer_completion_event_data* completion_event =
+ &ep_data[endpoint].completion_event[EP_DIR(dir)];
- switch (endpoint) {
- case EP_CONTROL:
- /* already handled */
- break;
+ void* data0 = NULL;
+ void* data1 = NULL;
- default:
- completion_event = &ep_data[endpoint].completion_event[EP_DIR(dir)];
+#ifdef USB_LEGACY_CONTROL_API
+ if(endpoint == EP_CONTROL) {
+ bool cwdd = control_write_data_done;
+ struct usb_ctrlrequest* req = active_request;
- completion_event->endpoint = endpoint;
- completion_event->dir = dir;
- completion_event->data = 0;
- completion_event->status = status;
- completion_event->length = length;
- /* All other endpoints. Let the thread deal with it */
- usb_signal_transfer_completion(completion_event);
- break;
+ if(dir == USB_DIR_OUT && req && cwdd) {
+ data0 = req;
+ data1 = control_write_data;
+ } else {
+ return;
+ }
}
+#endif
+
+ completion_event->endpoint = endpoint;
+ completion_event->dir = dir;
+ completion_event->data[0] = data0;
+ completion_event->data[1] = data1;
+ completion_event->status = status;
+ completion_event->length = length;
+
+ usb_signal_transfer_completion(completion_event);
}
void usb_core_handle_notify(long id, intptr_t data)
@@ -971,21 +993,154 @@ void usb_core_handle_notify(long id, intptr_t data)
}
}
-/* called by usb_drv_int() */
-void usb_core_control_request(struct usb_ctrlrequest* req)
+void usb_core_control_request(struct usb_ctrlrequest* req, void* reqdata)
{
struct usb_transfer_completion_event_data* completion_event =
&ep_data[EP_CONTROL].completion_event[EP_DIR(USB_DIR_IN)];
completion_event->endpoint = EP_CONTROL;
completion_event->dir = 0;
- completion_event->data = (void*)req;
+ completion_event->data[0] = (void*)req;
+ completion_event->data[1] = reqdata;
completion_event->status = 0;
completion_event->length = 0;
logf("ctrl received %ld, req=0x%x", current_tick, req->bRequest);
usb_signal_transfer_completion(completion_event);
}
+void usb_core_control_complete(int status)
+{
+ /* We currently don't use this, it's here to make the API look good ;)
+ * It makes sense to #define it away on normal builds.
+ */
+ (void)status;
+ logf("ctrl complete %ld, %d", current_tick, status);
+}
+
+#ifdef USB_LEGACY_CONTROL_API
+/* Only needed if the driver does not support the new API yet */
+void usb_core_legacy_control_request(struct usb_ctrlrequest* req)
+{
+ /* Only submit non-overlapping requests */
+ if (num_active_requests++ == 0)
+ {
+ buffered_request = *req;
+ active_request = &buffered_request;
+ control_write_data = NULL;
+ control_write_data_done = false;
+
+ usb_core_control_request(req, NULL);
+ }
+}
+
+void usb_drv_control_response(enum usb_control_response resp,
+ void* data, int length)
+{
+ struct usb_ctrlrequest* req = active_request;
+ unsigned int num_active = num_active_requests--;
+
+ /*
+ * There should have been a prior request submission, at least.
+ * FIXME: It seems the iPod video can get here and ignoring it
+ * allows the connection to succeed??
+ */
+ if (num_active == 0)
+ {
+ //panicf("null ctrl req");
+ return;
+ }
+
+ /*
+ * This can happen because an active request was already pending when
+ * the driver submitted a new one in usb_core_legacy_control_request().
+ * This could mean two things: (a) a driver bug; or (b) the host sent
+ * another request because we were too slow in handling an earlier one.
+ *
+ * The USB spec requires we respond to the latest request and drop any
+ * earlier ones, but that's not easy to do with the current design of
+ * the USB stack. Thus, the host will be expecting a response for the
+ * latest request, but this response is for the _earliest_ request.
+ *
+ * Play it safe and return a STALL. At this point we've recovered from
+ * the error on our end and will be ready to handle the next request.
+ */
+ if (num_active > 1)
+ {
+ active_request = NULL;
+ num_active_requests = 0;
+ usb_drv_stall(EP_CONTROL, true, true);
+ return;
+ }
+
+ if(req->wLength == 0)
+ {
+ active_request = NULL;
+
+ /* No-data request */
+ if(resp == USB_CONTROL_ACK)
+ usb_drv_send(EP_CONTROL, data, length);
+ else if(resp == USB_CONTROL_STALL)
+ usb_drv_stall(EP_CONTROL, true, true);
+ else
+ panicf("RECEIVE on non-data req");
+ }
+ else if(req->bRequestType & USB_DIR_IN)
+ {
+ /* Control read request */
+ if(resp == USB_CONTROL_ACK)
+ {
+ active_request = NULL;
+ usb_drv_recv_nonblocking(EP_CONTROL, NULL, 0);
+ usb_drv_send(EP_CONTROL, data, length);
+ }
+ else if(resp == USB_CONTROL_STALL)
+ {
+ active_request = NULL;
+ usb_drv_stall(EP_CONTROL, true, true);
+ }
+ else
+ {
+ panicf("RECEIVE on ctrl read req");
+ }
+ }
+ else if(!control_write_data_done)
+ {
+ /* Control write request, data phase */
+ if(resp == USB_CONTROL_RECEIVE)
+ {
+ control_write_data = data;
+ control_write_data_done = true;
+ usb_drv_recv_nonblocking(EP_CONTROL, data, length);
+ }
+ else if(resp == USB_CONTROL_STALL)
+ {
+ /* We should stall the OUT endpoint here, but the old code did
+ * not do so and some drivers may not handle it correctly. */
+ active_request = NULL;
+ usb_drv_stall(EP_CONTROL, true, true);
+ }
+ else
+ {
+ panicf("ACK on ctrl write data");
+ }
+ }
+ else
+ {
+ active_request = NULL;
+ control_write_data = NULL;
+ control_write_data_done = false;
+
+ /* Control write request, status phase */
+ if(resp == USB_CONTROL_ACK)
+ usb_drv_send(EP_CONTROL, NULL, 0);
+ else if(resp == USB_CONTROL_STALL)
+ usb_drv_stall(EP_CONTROL, true, true);
+ else
+ panicf("RECEIVE on ctrl write status");
+ }
+}
+#endif
+
void usb_core_notify_set_address(uint8_t addr)
{
logf("notify set addr received %ld", current_tick);