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.c537
1 files changed, 537 insertions, 0 deletions
diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c
new file mode 100644
index 0000000000..e0c4d91338
--- /dev/null
+++ b/firmware/usbstack/usb_core.c
@@ -0,0 +1,537 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: $
+ *
+ * Copyright (C) 2007 by Björn Stenberg
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "system.h"
+#include "thread.h"
+#include "kernel.h"
+#include "string.h"
+//#define LOGF_ENABLE
+#include "logf.h"
+
+//#define USB_STORAGE
+//#define USB_BENCHMARK
+#define USB_CHARGING_ONLY
+
+#include "usb_ch9.h"
+#include "usb_drv.h"
+#include "usb_core.h"
+
+#if defined(USB_STORAGE)
+#include "usb_storage.h"
+#elif defined(USB_BENCHMARK)
+#include "usb_benchmark.h"
+#endif
+
+/*-------------------------------------------------------------------------*/
+/* USB protocol descriptors: */
+
+#define USB_SC_SCSI 0x06 /* Transparent */
+#define USB_PROT_BULK 0x50 /* bulk only */
+
+int usb_max_pkt_size = 512;
+
+static const struct usb_device_descriptor device_descriptor = {
+ .bLength = sizeof(struct usb_device_descriptor),
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = 0x0200, /* USB version 2.0 */
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .bMaxPacketSize0 = 64,
+ .idVendor = USB_VENDOR_ID,
+ .idProduct = USB_PRODUCT_ID,
+ .bcdDevice = 0x0100,
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .iSerialNumber = 0,
+ .bNumConfigurations = 1
+};
+
+static const struct {
+ struct usb_config_descriptor config_descriptor;
+ struct usb_interface_descriptor interface_descriptor;
+ struct usb_endpoint_descriptor ep1_hs_in_descriptor;
+ struct usb_endpoint_descriptor ep1_hs_out_descriptor;
+} config_data =
+{
+ {
+ .bLength = sizeof(struct usb_config_descriptor),
+ .bDescriptorType = USB_DT_CONFIG,
+ .wTotalLength = sizeof config_data,
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = USB_CONFIG_ATT_ONE,
+ .bMaxPower = 250, /* 500mA in 2mA units */
+ },
+
+#ifdef USB_CHARGING_ONLY
+ /* dummy interface for charging-only */
+ {
+ .bLength = sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .iInterface = 4
+ },
+
+ {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = EP_TX | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 64,
+ .bInterval = 0
+ },
+ {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = EP_RX | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 64,
+ .bInterval = 0
+ }
+#endif
+
+#ifdef USB_STORAGE
+ /* storage interface */
+ {
+ .bLength = sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = USB_SC_SCSI,
+ .bInterfaceProtocol = USB_PROT_BULK,
+ .iInterface = 0
+ },
+
+ {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = EP_TX | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ .bInterval = 0
+ },
+ {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = EP_RX | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ .bInterval = 0
+ }
+#endif
+
+#ifdef USB_SERIAL
+ /* serial interface */
+ {
+ .bLength = sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0
+ },
+
+ {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = EP_TX | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ .bInterval = 0
+ },
+ {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = EP_RX | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ .bInterval = 0
+ }
+#endif
+
+#ifdef USB_BENCHMARK
+ /* bulk test interface */
+ {
+ .bLength = sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 255,
+ .bInterfaceProtocol = 255,
+ .iInterface = 3
+ },
+
+ {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = EP_RX | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+// .wMaxPacketSize = 64,
+ .wMaxPacketSize = 512,
+ .bInterval = 0
+ },
+ {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = EP_TX | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+// .wMaxPacketSize = 64,
+ .wMaxPacketSize = 512,
+ .bInterval = 0
+ }
+#endif
+};
+
+static const struct usb_qualifier_descriptor qualifier_descriptor =
+{
+ .bLength = sizeof(struct usb_qualifier_descriptor),
+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+ .bcdUSB = 0x0200,
+ .bDeviceClass = 0,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .bMaxPacketSize0 = 64,
+ .bNumConfigurations = 1
+};
+
+/* full speed = 12 Mbit */
+static const struct usb_endpoint_descriptor ep1_fs_in_descriptor =
+{
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 64,
+ .bInterval = 0
+};
+
+static const struct usb_endpoint_descriptor ep1_fs_out_descriptor =
+{
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 64,
+ .bInterval = 0
+};
+
+static const struct usb_endpoint_descriptor* ep_descriptors[4] =
+{
+ &config_data.ep1_hs_in_descriptor,
+ &config_data.ep1_hs_out_descriptor,
+ &ep1_fs_in_descriptor,
+ &ep1_fs_out_descriptor
+};
+
+/* this is stringid #0: languages supported */
+static const struct usb_string_descriptor lang_descriptor =
+{
+ sizeof(struct usb_string_descriptor),
+ USB_DT_STRING,
+ {0x0409} /* LANGID US English */
+};
+
+/* this is stringid #1 and up: the actual strings */
+static const struct {
+ unsigned char size;
+ unsigned char type;
+ unsigned short string[32];
+} usb_strings[] =
+{
+ {
+ 16,
+ USB_DT_STRING,
+ {'R','o','c','k','b','o','x'}
+ },
+ {
+ 42,
+ USB_DT_STRING,
+ {'D','i','g','i','t','a','l',' ','a','u','d','i','o',' ','p','l','a','y','e','r'}
+ },
+ {
+ 40,
+ USB_DT_STRING,
+ {'B','u','l','k',' ','t','e','s','t',' ','i','n','t','e','r','f','a','c','e'}
+ },
+ {
+ 28,
+ USB_DT_STRING,
+ {'C','h','a','r','g','i','n','g',' ','o','n','l','y'}
+ }
+};
+
+
+static int usb_address = 0;
+static bool initialized = false;
+static struct event_queue usbcore_queue;
+
+#ifdef USB_STORAGE
+static const char usbcore_thread_name[] = "usb_core";
+static struct thread_entry* usbcore_thread;
+static long usbcore_stack[DEFAULT_STACK_SIZE];
+static void usb_core_thread(void);
+#endif
+
+static void ack_control(struct usb_ctrlrequest* req);
+
+void usb_core_init(void)
+{
+ queue_init(&usbcore_queue, false);
+ usb_drv_init();
+#ifdef USB_STORAGE
+ usb_storage_init();
+ usbcore_thread =
+ create_thread(usb_core_thread, usbcore_stack, sizeof(usbcore_stack), 0,
+ usbcore_thread_name
+ IF_PRIO(, PRIORITY_SYSTEM)
+ IF_COP(, CPU));
+#endif
+
+#ifdef USB_BENCHMARK
+ usb_benchmark_init();
+#endif
+ initialized = true;
+ logf("usb_core_init() finished");
+}
+
+void usb_core_exit(void)
+{
+ if (initialized) {
+ usb_drv_exit();
+ queue_delete(&usbcore_queue);
+#ifdef USB_STORAGE
+ remove_thread(usbcore_thread);
+#endif
+ }
+ logf("usb_core_exit() finished");
+}
+
+#ifdef USB_STORAGE
+void usb_core_thread(void)
+{
+ while (1) {
+ struct queue_event ev;
+
+ queue_wait(&usbcore_queue, &ev);
+
+ usb_storage_transfer_complete(ev.id, ev.data);
+ }
+}
+#endif
+
+/* called by usb_drv_int() */
+void usb_core_control_request(struct usb_ctrlrequest* req)
+{
+ /* note: interrupt context */
+
+#ifdef USB_BENCHMARK
+ if ((req->bRequestType & 0x60) == USB_TYPE_VENDOR) {
+ usb_benchmark_control_request(req);
+ return;
+ }
+#endif
+
+ switch (req->bRequest) {
+ case USB_REQ_SET_CONFIGURATION:
+ logf("usb_core: SET_CONFIG");
+#ifdef USB_STORAGE
+ usb_storage_control_request(req);
+#endif
+ ack_control(req);
+ break;
+
+ case USB_REQ_SET_INTERFACE:
+ logf("usb_core: SET_INTERFACE");
+ ack_control(req);
+ break;
+
+ case USB_REQ_CLEAR_FEATURE:
+ logf("usb_core: CLEAR_FEATURE");
+ if (req->wValue)
+ usb_drv_stall(req->wIndex, true);
+ else
+ usb_drv_stall(req->wIndex, false);
+ ack_control(req);
+ break;
+
+ case USB_REQ_SET_ADDRESS:
+ usb_address = req->wValue;
+ logf("usb_core: SET_ADR %d", usb_address);
+ ack_control(req);
+ usb_drv_set_address(usb_address);
+ break;
+
+ case USB_REQ_GET_STATUS: {
+ static char tmp[2] = {0,0};
+ tmp[0] = 0;
+ tmp[1] = 0;
+ logf("usb_core: GET_STATUS");
+ usb_drv_send(EP_CONTROL, UNCACHED_ADDR(&tmp), 2);
+ ack_control(req);
+ break;
+ }
+
+ case USB_REQ_GET_DESCRIPTOR: {
+ int index = req->wValue & 0xff;
+ int length = req->wLength;
+ int size;
+ const void* ptr = NULL;
+ logf("usb_core: GET_DESC %d", req->wValue >> 8);
+
+ switch (req->wValue >> 8) { /* type */
+ case USB_DT_DEVICE:
+ ptr = &device_descriptor;
+ size = sizeof device_descriptor;
+ break;
+
+ case USB_DT_CONFIG:
+ ptr = &config_data;
+ size = sizeof config_data;
+ break;
+
+ case USB_DT_STRING:
+ switch (index) {
+ case 0: /* lang descriptor */
+ ptr = &lang_descriptor;
+ size = sizeof lang_descriptor;
+ break;
+
+ default:
+ if ((unsigned)index <= (sizeof(usb_strings)/sizeof(usb_strings[0]))) {
+ index -= 1;
+ ptr = &usb_strings[index];
+ size = usb_strings[index].size;
+ }
+ else {
+ logf("bad string id %d", index);
+ usb_drv_stall(EP_CONTROL, true);
+ }
+ break;
+ }
+ break;
+
+ case USB_DT_INTERFACE:
+ ptr = &config_data.interface_descriptor;
+ size = sizeof config_data.interface_descriptor;
+ break;
+
+ case USB_DT_ENDPOINT:
+ if (index <= NUM_ENDPOINTS) {
+ ptr = &ep_descriptors[index];
+ size = sizeof ep_descriptors[index];
+ }
+ else {
+ logf("bad endpoint %d", index);
+ usb_drv_stall(EP_CONTROL, true);
+ }
+ break;
+
+ case USB_DT_DEVICE_QUALIFIER:
+ ptr = &qualifier_descriptor;
+ size = sizeof qualifier_descriptor;
+ break;
+
+/*
+ case USB_DT_OTHER_SPEED_CONFIG:
+ ptr = &other_speed_descriptor;
+ size = sizeof other_speed_descriptor;
+ break;
+*/
+ default:
+ logf("bad desc %d", req->wValue >> 8);
+ usb_drv_stall(EP_CONTROL, true);
+ break;
+ }
+
+ if (ptr) {
+ length = MIN(size, length);
+ usb_drv_send(EP_CONTROL, (void*)UNCACHED_ADDR(ptr), length);
+ }
+ ack_control(req);
+ break;
+ } /* USB_REQ_GET_DESCRIPTOR */
+
+ default:
+#ifdef USB_STORAGE
+ /* does usb_storage know this request? */
+ if (!usb_storage_control_request(req))
+#endif
+ {
+ /* nope. flag error */
+ logf("usb bad req %d", req->bRequest);
+ usb_drv_stall(EP_CONTROL, true);
+ ack_control(req);
+ }
+ break;
+ }
+}
+
+/* called by usb_drv_int() */
+void usb_core_bus_reset(void)
+{
+ usb_address = 0;
+}
+
+/* called by usb_drv_transfer_completed() */
+void usb_core_transfer_complete(int endpoint, bool in)
+{
+#ifdef USB_CHARGING_ONLY
+ (void)in;
+#endif
+
+ switch (endpoint) {
+ case EP_CONTROL:
+ /* already handled */
+ break;
+
+ case EP_RX:
+ case EP_TX:
+#if defined(USB_BENCHMARK)
+ usb_benchmark_transfer_complete(endpoint, in);
+#elif defined(USB_STORAGE)
+ queue_post(&usbcore_queue, endpoint);
+#endif
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void ack_control(struct usb_ctrlrequest* req)
+{
+ if (req->bRequestType & 0x80)
+ usb_drv_recv(EP_CONTROL, NULL, 0);
+ else
+ usb_drv_send(EP_CONTROL, NULL, 0);
+}
+