summaryrefslogtreecommitdiff
path: root/firmware/drivers
diff options
context:
space:
mode:
authorChristian Gmeiner <christian.gmeiner@gmail.com>2007-08-27 16:04:32 +0000
committerChristian Gmeiner <christian.gmeiner@gmail.com>2007-08-27 16:04:32 +0000
commit8181a0c905a591caef684a2d7487feedbec84c10 (patch)
tree3939183e8e73928d1b5dcfc97b40f33b6baa309b /firmware/drivers
parent9305c86f5b8fdfd60882428f884ba29bded8da78 (diff)
downloadrockbox-8181a0c905a591caef684a2d7487feedbec84c10.zip
rockbox-8181a0c905a591caef684a2d7487feedbec84c10.tar.gz
rockbox-8181a0c905a591caef684a2d7487feedbec84c10.tar.bz2
rockbox-8181a0c905a591caef684a2d7487feedbec84c10.tar.xz
Usb Stack: only setup packet handling, and not enabled by default as there is a lot to do.
* settings code is not fully ready -> changing device driver has no effect * clean ups * check copyriths * find a way to detect IN transfers * support for full and highspeed * ... git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14470 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers')
-rw-r--r--firmware/drivers/usb/arcotg_dcd.c983
-rw-r--r--firmware/drivers/usb/arcotg_dcd.h173
2 files changed, 1156 insertions, 0 deletions
diff --git a/firmware/drivers/usb/arcotg_dcd.c b/firmware/drivers/usb/arcotg_dcd.c
new file mode 100644
index 0000000..982fdfb
--- /dev/null
+++ b/firmware/drivers/usb/arcotg_dcd.c
@@ -0,0 +1,983 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: $
+ *
+ * Copyright (C) 2007 by Christian Gmeiner
+ *
+ * Based on code from the Linux Target Image Builder from Freescale
+ * available at http://www.bitshrine.org/ and
+ * http://www.bitshrine.org/gpp/linux-2.6.16-mx31-usb-2.patch
+ * Adapted for Rockbox in January 2007
+ * Original file: drivers/usb/gadget/arcotg_udc.c
+ *
+ * USB Device Controller Driver
+ * Driver for ARC OTG USB module in the i.MX31 platform, etc.
+ *
+ * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Based on mpc-udc.h
+ * Author: Li Yang (leoli@freescale.com)
+ * Jiang Bo (Tanya.jiang@freescale.com)
+ *
+ * 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 <string.h>
+#include "arcotg_dcd.h"
+
+/*-------------------------------------------------------------------------*/
+
+static struct arcotg_dcd dcd_controller;
+struct usb_response res;
+
+/* datastructes to controll transfers */
+struct dtd dev_td[USB_MAX_PIPES] IBSS_ATTR;
+struct dqh dev_qh[USB_MAX_PIPES] __attribute((aligned (1 << 11))) IBSS_ATTR;
+
+/* shared memory used by rockbox and dcd to exchange data */
+#define BUFFER_SIZE 512
+unsigned char buffer[BUFFER_SIZE] IBSS_ATTR;
+
+/*-------------------------------------------------------------------------*/
+
+/* description of our device driver operations */
+struct usb_dcd_controller_ops arotg_dcd_ops = {
+ .enable = usb_arcotg_dcd_enable,
+ .disable = NULL,
+ .set_halt = usb_arcotg_dcd_set_halt,
+ .send = usb_arcotg_dcd_send,
+ .receive = usb_arcotg_dcd_receive,
+ .ep0 = &dcd_controller.endpoints[0],
+};
+
+/* description of our usb controller driver */
+struct usb_controller arcotg_dcd = {
+ .name = "arcotg_dcd",
+ .type = DEVICE,
+ .speed = USB_SPEED_UNKNOWN,
+ .init = usb_arcotg_dcd_init,
+ .shutdown = usb_arcotg_dcd_shutdown,
+ .irq = usb_arcotg_dcd_irq,
+ .start = usb_arcotg_dcd_start,
+ .stop = usb_arcotg_dcd_stop,
+ .controller_ops = (void*)&arotg_dcd_ops,
+};
+
+static struct usb_response response;
+
+/*-------------------------------------------------------------------------*/
+
+/* TODO hmmm */
+
+struct timer {
+ unsigned long s;
+ unsigned long e;
+};
+
+void
+timer_set(struct timer * timer, unsigned long val)
+{
+ timer->s = USEC_TIMER;
+ timer->e = timer->s + val + 1;
+}
+
+int
+timer_expired(struct timer * timer)
+{
+ unsigned long val = USEC_TIMER;
+
+ if (timer->e > timer->s) {
+ return !(val >= timer->s && val <= timer->e);
+ } else {
+ return (val > timer->e && val < timer->s);
+ }
+}
+
+#define MAX_PACKET_SIZE USB_MAX_CTRL_PAYLOAD
+
+#define ERROR_TIMEOUT (-3)
+#define ERROR_UNKNOWN (-7)
+
+#define PRIME_TIMER 100000
+#define TRANSFER_TIMER 1000000
+#define RESET_TIMER 5000000
+#define SETUP_TIMER 200000
+
+/*-------------------------------------------------------------------------*/
+
+/* gets called by usb_stack_init() to register
+ * this arcotg device conrtollder driver in the
+ * stack. */
+void usb_dcd_init(void) {
+ usb_controller_register(&arcotg_dcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+void usb_arcotg_dcd_init(void) {
+
+ struct timer t;
+ int i, ep_num = 0;
+
+ logf("arcotg_dcd: init");
+ memset(&dcd_controller, 0, sizeof(struct arcotg_dcd));
+
+ /* setup list of aviable endpoints */
+ INIT_LIST_HEAD(&arcotg_dcd.endpoints.list);
+
+ for (i = 0; i < USB_MAX_PIPES; i++) {
+
+ dcd_controller.endpoints[i].pipe_num = i;
+
+ if (i % 2 == 0) {
+ dcd_controller.endpoints[i].ep_num = ep_num;
+ } else {
+ dcd_controller.endpoints[i].ep_num = ep_num;
+ ep_num++;
+ }
+
+ logf("pipe %d -> ep %d %s", dcd_controller.endpoints[i].pipe_num, dcd_controller.endpoints[i].ep_num, dcd_controller.endpoints[i].name);
+
+ if (ep_name[i] != NULL) {
+ memcpy(&dcd_controller.endpoints[i].name, ep_name[i], sizeof(dcd_controller.endpoints[i].name));
+
+ if (i != 0) {
+ /* add to list of configurable endpoints */
+ list_add_tail(&dcd_controller.endpoints[i].list, &arcotg_dcd.endpoints.list);
+ }
+ }
+ }
+
+ /* ep0 is special */
+ arcotg_dcd.ep0 = &dcd_controller.endpoints[0];
+ arcotg_dcd.ep0->maxpacket = USB_MAX_CTRL_PAYLOAD;
+
+ /* stop */
+ UDC_USBCMD &= ~USB_CMD_RUN;
+
+ udelay(50000);
+ timer_set(&t, RESET_TIMER);
+
+ /* reset */
+ UDC_USBCMD |= USB_CMD_CTRL_RESET;
+
+ while ((UDC_USBCMD & USB_CMD_CTRL_RESET)) {
+ if (timer_expired(&t)) {
+ logf("TIMEOUT->init");
+ }
+ }
+
+ /* put controller in device mode */
+ UDC_USBMODE |= USB_MODE_CTRL_MODE_DEVICE;
+
+ /* init queue heads */
+ qh_init(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL, USB_MAX_CTRL_PAYLOAD, 0, 0);
+ qh_init(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL, USB_MAX_CTRL_PAYLOAD, 0, 0);
+
+ UDC_ENDPOINTLISTADDR = (unsigned int)dev_qh;
+}
+
+void usb_arcotg_dcd_shutdown(void) {
+
+}
+
+void usb_arcotg_dcd_start(void) {
+
+ logf("start");
+
+ if (arcotg_dcd.device_driver != NULL) {
+ logf("YEEEEEEESSSSSSS");
+ } else {
+ logf("NOOOOOO");
+ }
+
+ /* clear stopped bit */
+ dcd_controller.stopped = false;
+
+ UDC_USBCMD |= USB_CMD_RUN;
+}
+
+void usb_arcotg_dcd_stop(void) {
+
+ logf("stop");
+
+ /* set stopped bit */
+ dcd_controller.stopped = true;
+
+ UDC_USBCMD &= ~USB_CMD_RUN;
+}
+
+void usb_arcotg_dcd_irq(void) {
+
+ int i;
+
+ if (dcd_controller.stopped == true) {
+ return;
+ }
+
+ /* check if we need to wake up from suspend */
+ if (!(UDC_USBSTS & USB_STS_SUSPEND) && dcd_controller.resume_state) {
+ resume_int();
+ }
+
+ /* USB Interrupt */
+ if (UDC_USBSTS & USB_STS_INT) {
+
+ /* setup packet, we only support ep0 as control ep */
+ if (UDC_ENDPTSETUPSTAT & EP_SETUP_STATUS_EP0) {
+ /* copy data from queue head to local buffer */
+ memcpy(&dcd_controller.local_setup_buff, (uint8_t *) &dev_qh[0].setup_buffer, 8);
+ /* ack setup packet*/
+ UDC_ENDPTSETUPSTAT = UDC_ENDPTSETUPSTAT;
+ setup_received_int(&dcd_controller.local_setup_buff);
+ }
+
+ if (UDC_ENDPTCOMPLETE) {
+ UDC_ENDPTCOMPLETE = UDC_ENDPTCOMPLETE;
+ }
+ }
+
+ if (UDC_USBSTS & USB_STS_PORT_CHANGE) {
+ port_change_int();
+ }
+
+ if (UDC_USBSTS & USB_STS_SUSPEND) {
+ suspend_int();
+ }
+
+ if (UDC_USBSTS & USB_STS_RESET) {
+ reset_int();
+ }
+
+ if (UDC_USBSTS & USB_STS_ERR) {
+ logf("!!! error !!!");
+ }
+
+ if (UDC_USBSTS & USB_STS_SYS_ERR) {
+ logf("!!! sys error !!!");
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+/* interrupt handlers */
+
+static void setup_received_int(struct usb_ctrlrequest* request) {
+
+ int error = 0;
+ uint8_t address = 0;
+ int handled = 0; /* set to zero if we do not handle the message, */
+ /* and should pass it to the driver */
+
+ logf("setup_int");
+ into_usb_ctrlrequest(request);
+
+ /* handle all requests we support */
+ switch (request->bRequestType & USB_TYPE_MASK) {
+ case USB_TYPE_STANDARD:
+
+ switch (request->bRequest) {
+ case USB_REQ_SET_ADDRESS:
+
+ /* store address as we need to ack before setting it */
+ address = (uint8_t)request->wValue;
+
+ handled = 1;
+ break;
+
+ case USB_REQ_GET_STATUS:
+
+ logf("sending status..");
+ response.buf = &dcd_controller.usb_state;
+ response.length = 2;
+
+ handled = usb_arcotg_dcd_send(NULL, &response);
+ break;
+ }
+
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ /* we only support set/clear feature for endpoint */
+ if (request->bRequestType == USB_RECIP_ENDPOINT) {
+ int dir = (request->wIndex & 0x0080) ? EP_DIR_IN : EP_DIR_OUT;
+ int num = (request->wIndex & 0x000f);
+ struct usb_ep *ep;
+
+ if (request->wValue != 0 || request->wLength != 0 || (num * 2 + dir) > USB_MAX_PIPES) {
+ break;
+ }
+ ep = &dcd_controller.endpoints[num * 2 + dir];
+
+ if (request->bRequest == USB_REQ_SET_FEATURE) {
+ logf("SET_FEATURE doing set_halt");
+ handled = usb_arcotg_dcd_set_halt(ep, 1);
+ } else {
+ logf("CLEAR_FEATURE doing clear_halt");
+ handled = usb_arcotg_dcd_set_halt(ep, 0);
+ }
+
+ if (handled == 0) {
+ handled = 1; /* dont pass it to driver */
+ }
+ }
+#if 0
+ if (rc == 0) {
+ /* send status only if _arcotg_ep_set_halt success */
+ if (ep0_prime_status(udc, EP_DIR_IN))
+ Ep0Stall(udc);
+ }
+#endif
+ break;
+ }
+
+ /* if dcd can not handle reqeust, ask driver */
+ if (handled == 0) {
+ if (arcotg_dcd.device_driver != NULL && arcotg_dcd.device_driver->request != NULL) {
+ handled = arcotg_dcd.device_driver->request(request);
+ logf("result from driver %d", handled);
+ }
+ }
+
+ if (handled <= 0) {
+ error = handled;
+ }
+
+ /* ack transfer */
+ usb_ack(request, error);
+
+ if (address != 0) {
+ logf("setting address to %d", address);
+ UDC_DEVICEADDR = address << 25;
+ }
+}
+
+static void port_change_int(void) {
+
+ //logf("port_change_int");
+ uint32_t tmp;
+ enum usb_device_speed speed = USB_SPEED_UNKNOWN;
+
+ /* bus resetting is finished */
+ if (!(UDC_PORTSC1 & PORTSCX_PORT_RESET)) {
+ /* Get the speed */
+ tmp = (UDC_PORTSC1 & PORTSCX_PORT_SPEED_MASK);
+ switch (tmp) {
+ case PORTSCX_PORT_SPEED_HIGH:
+ speed = USB_SPEED_HIGH;
+ break;
+ case PORTSCX_PORT_SPEED_FULL:
+ speed = USB_SPEED_FULL;
+ break;
+ case PORTSCX_PORT_SPEED_LOW:
+ speed = USB_SPEED_LOW;
+ break;
+ default:
+ speed = USB_SPEED_UNKNOWN;
+ break;
+ }
+ }
+
+ /* update speed */
+ arcotg_dcd.speed = speed;
+
+ /* update USB state */
+ if (!dcd_controller.resume_state) {
+ dcd_controller.usb_state = USB_STATE_DEFAULT;
+ }
+
+ /* inform device driver */
+ if (arcotg_dcd.device_driver != NULL && arcotg_dcd.device_driver->speed != NULL) {
+ arcotg_dcd.device_driver->speed(speed);
+ }
+}
+
+static void suspend_int(void) {
+
+ //logf("suspend_int");
+ dcd_controller.resume_state = dcd_controller.usb_state;
+ dcd_controller.usb_state = USB_STATE_SUSPENDED;
+
+ /* report suspend to the driver */
+ if (arcotg_dcd.device_driver != NULL && arcotg_dcd.device_driver->suspend != NULL) {
+ arcotg_dcd.device_driver->suspend();
+ }
+}
+
+static void resume_int(void) {
+
+ //logf("resume_int");
+ dcd_controller.usb_state = dcd_controller.resume_state;
+ dcd_controller.resume_state = USB_STATE_NOTATTACHED;
+
+ /* report resume to the driver */
+ if (arcotg_dcd.device_driver != NULL && arcotg_dcd.device_driver->resume != NULL) {
+ arcotg_dcd.device_driver->resume();
+ }
+}
+
+static void reset_int(void) {
+
+ //logf("reset_int");
+ struct timer t;
+
+ timer_set(&t, RESET_TIMER);
+
+ UDC_ENDPTSETUPSTAT = UDC_ENDPTSETUPSTAT;
+ UDC_ENDPTCOMPLETE = UDC_ENDPTCOMPLETE;
+
+ while (UDC_ENDPTPRIME) { /* prime and flush pending transfers */
+ if (timer_expired(&t)) {
+ logf("TIMEOUT->p&f");
+ }
+ }
+
+ UDC_ENDPTFLUSH = ~0;
+
+ if ((UDC_PORTSC1 & (1 << 8)) == 0) {
+ logf("TIMEOUT->port");
+ }
+
+ UDC_USBSTS = (1 << 6);
+
+ while ((UDC_USBSTS & (1 << 2)) == 0) { /* wait for port change */
+ if (timer_expired(&t)) {
+ logf("TIMEOUT->portchange");
+ }
+ }
+
+ UDC_USBSTS = (1 << 2);
+}
+
+
+/*-------------------------------------------------------------------------*/
+/* usb controller ops */
+
+int usb_arcotg_dcd_enable(struct usb_ep* ep) {
+
+ unsigned short max = 0;
+ unsigned char mult = 0, zlt = 0;
+ int retval = 0;
+ char *val = NULL; /* for debug */
+
+ /* catch bogus parameter */
+ if (!ep) {
+ return -EINVAL;
+ }
+
+ logf("ahhh %d", ep->desc->wMaxPacketSize);
+ max = ep->desc->wMaxPacketSize;
+ retval = -EINVAL;
+
+ /* check the max package size validate for this endpoint */
+ /* Refer to USB2.0 spec table 9-13,
+ */
+ switch (ep->desc->bmAttributes & 0x03) {
+ case USB_ENDPOINT_XFER_BULK:
+ zlt = 1;
+ break;
+
+ case USB_ENDPOINT_XFER_INT:
+ zlt = 1;
+ break;
+
+ case USB_ENDPOINT_XFER_ISOC:
+ break;
+
+ case USB_ENDPOINT_XFER_CONTROL:
+ break;
+ }
+
+#if 0
+ switch (ep->desc->bmAttributes & 0x03) {
+ case USB_ENDPOINT_XFER_BULK:
+ if (strstr(ep->ep.name, "-iso") || strstr(ep->ep.name, "-int")) {
+ goto en_done;
+ }
+ mult = 0;
+ zlt = 1;
+
+ switch (arcotg_dcd.speed) {
+ case USB_SPEED_HIGH:
+ if ((max == 128) || (max == 256) || (max == 512)) {
+ break;
+ }
+ default:
+ switch (max) {
+ case 4:
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ break;
+ default:
+ + case USB_SPEED_LOW:
+ + goto en_done;
+ + }
+ + }
+ + break;
+ + case USB_ENDPOINT_XFER_INT:
+ + if (strstr(ep->ep.name, "-iso")) /* bulk is ok */
+ + goto en_done;
+ + mult = 0;
+ + zlt = 1;
+ + switch (udc->gadget.speed) {
+ + case USB_SPEED_HIGH:
+ + if (max <= 1024)
+ + break;
+ + case USB_SPEED_FULL:
+ + if (max <= 64)
+ + break;
+ + default:
+ + if (max <= 8)
+ + break;
+ + goto en_done;
+ + }
+ + break;
+ + case USB_ENDPOINT_XFER_ISOC:
+ + if (strstr(ep->ep.name, "-bulk") || strstr(ep->ep.name, "-int"))
+ + goto en_done;
+ + mult = (unsigned char)
+ + (1 + ((le16_to_cpu(desc->wMaxPacketSize) >> 11) & 0x03));
+ + zlt = 0;
+ + switch (udc->gadget.speed) {
+ + case USB_SPEED_HIGH:
+ + if (max <= 1024)
+ + break;
+ + case USB_SPEED_FULL:
+ + if (max <= 1023)
+ + break;
+ + default:
+ + goto en_done;
+ + }
+ + break;
+ + case USB_ENDPOINT_XFER_CONTROL:
+ + if (strstr(ep->ep.name, "-iso") || strstr(ep->ep.name, "-int"))
+ + goto en_done;
+ + mult = 0;
+ + zlt = 1;
+ + switch (udc->gadget.speed) {
+ + case USB_SPEED_HIGH:
+ + case USB_SPEED_FULL:
+ + switch (max) {
+ + case 1:
+ + case 2:
+ + case 4:
+ + case 8:
+ + case 16:
+ + case 32:
+ + case 64:
+ + break;
+ + default:
+ + goto en_done;
+ + }
+ + case USB_SPEED_LOW:
+ + switch (max) {
+ + case 1:
+ + case 2:
+ + case 4:
+ + case 8:
+ + break;
+ + default:
+ + goto en_done;
+ + }
+ + default:
+ + goto en_done;
+ + }
+ + break;
+ +
+ + default:
+ + goto en_done;
+ + }
+#endif
+
+ /* here initialize variable of ep */
+ ep->maxpacket = max;
+
+ /* hardware special operation */
+
+ /* Init EPx Queue Head (Ep Capabilites field in QH
+ * according to max, zlt, mult) */
+ qh_init(ep->ep_num,
+ (ep->desc->bEndpointAddress & USB_DIR_IN) ? USB_RECV : USB_SEND,
+ (unsigned char) (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK),
+ max, zlt, mult);
+
+ /* Init endpoint x at here */
+ ep_setup(ep->ep_num,
+ (unsigned char)((ep->desc->bEndpointAddress & USB_DIR_IN) ? USB_RECV : USB_SEND),
+ (unsigned char)(ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK));
+
+ /* Now HW will be NAKing transfers to that EP,
+ * until a buffer is queued to it. */
+
+ retval = 0;
+ switch (ep->desc->bmAttributes & 0x03) {
+ case USB_ENDPOINT_XFER_BULK:
+ val = "bulk";
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ val = "iso";
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ val = "intr";
+ break;
+ default:
+ val = "ctrl";
+ break;
+ }
+
+ logf("ep num %d", (int)ep->ep_num);
+
+ logf("enabled %s (ep%d%s-%s)", ep->name,
+ ep->desc->bEndpointAddress & 0x0f,
+ (ep->desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", val);
+ logf(" maxpacket %d", max);
+
+ return retval;
+}
+
+int usb_arcotg_dcd_set_halt(struct usb_ep* ep, bool halt) {
+
+ int status = -EOPNOTSUPP; /* operation not supported */
+ unsigned char dir = 0;
+ unsigned int tmp_epctrl = 0;
+
+ if (!ep) {
+ status = -EINVAL;
+ goto out;
+ }
+
+ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ status = -EOPNOTSUPP;
+ goto out;
+ }
+
+ status = 0;
+ dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
+
+ tmp_epctrl = UDC_ENDPTCTRL(ep->ep_num);
+
+ if (halt) {
+ /* set the stall bit */
+ if (dir) {
+ tmp_epctrl |= EPCTRL_TX_EP_STALL;
+ } else {
+ tmp_epctrl |= EPCTRL_RX_EP_STALL;
+ }
+ } else {
+ /* clear the stall bit and reset data toggle */
+ if (dir) {
+ tmp_epctrl &= ~EPCTRL_TX_EP_STALL;
+ tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
+ } else {
+ tmp_epctrl &= ~EPCTRL_RX_EP_STALL;
+ tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
+ }
+ }
+ UDC_ENDPTCTRL(ep->ep_num) = tmp_epctrl;
+
+out:
+ logf(" %s %s halt rc=%d", ep->name, halt ? "set" : "clear", status);
+ return status;
+}
+
+int usb_arcotg_dcd_send(struct usb_ep* ep, struct usb_response* res) {
+
+ char* ptr;
+ int todo, error, size, done = 0;
+ int index = 1; /* use as default ep0 tx qh and td */
+ struct dtd* td;
+ struct dqh* qh;
+ unsigned int mask;
+
+ if (res == NULL) {
+ logf("invalid input");
+ return -EINVAL;
+ }
+
+ if (ep != NULL) {
+ index = ep->pipe_num;
+ }
+
+ logf("buff: %x", res->buf);
+ logf("len: %d", res->length);
+
+ ptr = res->buf;
+ size = res->length;
+
+ td = &dev_td[index];
+ qh = &dev_qh[index];
+ mask = 1 << (15 + index);
+ logf("sending mask: %x", mask);
+
+ do {
+ /* calculate how much to copy and send */
+ todo = MIN(size, BUFFER_SIZE);
+
+ /* copy data to shared memory area */
+ memcpy(buffer, ptr, todo);
+
+ /* init transfer descriptor */
+ td_init(td, buffer, todo);
+
+ /* start transfer*/
+ error = td_enqueue(td, qh, mask);
+
+ if (error == 0) {
+ /* waiting for finished transfer */
+ error = td_wait(td, mask);
+ }
+
+ if (error) {
+ done = error;
+ break;
+ }
+
+ size -= todo;
+ ptr += todo;
+ done += todo;
+
+ } while (size > 0);
+
+ logf("usb_send done %d",done);
+ return done;
+}
+
+int usb_arcotg_dcd_receive(struct usb_ep* ep, struct usb_response* res) {
+
+ char* ptr;
+ int todo, error, size, done = 0;
+ int index = 0; /* use as default ep0 rx qh and td */
+ struct dtd* td;
+ struct dqh* qh;
+ unsigned int mask;
+
+ if (res == NULL) {
+ logf("invalid input");
+ return -EINVAL;
+ }
+
+ if (ep != NULL) {
+ index = ep->pipe_num;
+ }
+
+ ptr = res->buf;
+ size = res->length;
+
+ td = &dev_td[index];
+ qh = &dev_qh[index];
+ mask = 1 << index;
+
+ do {
+ /* calculate how much to receive in one step */
+ todo = MIN(size, BUFFER_SIZE);
+
+ /* init transfer descritpor */
+ td_init(td, buffer, size);
+
+ /* start transfer */
+ error = td_enqueue(td, qh, mask);
+
+ if (error == 0) {
+ /* wait until transfer is finished */
+ error = td_wait(td, mask);
+ }
+
+ if (error) {
+ done = error;
+ break;
+ }
+
+ /* copy receive data to buffer */
+ memcpy(ptr, buffer, todo);
+
+ size -= todo;
+ ptr += todo;
+ done += todo;
+
+ } while (size > 0);
+
+ logf("usb_recive done %d",done);
+ return done;
+}
+
+/*-------------------------------------------------------------------------*/
+/* lifecylce */
+
+static void qh_init(unsigned char ep_num, unsigned char dir, unsigned char ep_type,
+ unsigned int max_pkt_len, unsigned int zlt, unsigned char mult) {
+
+ struct dqh *qh = &dev_qh[2 * ep_num + dir];
+ uint32_t tmp = 0;
+ memset(qh, 0, sizeof(struct dqh));
+
+ /* set the Endpoint Capabilites Reg of QH */
+ switch (ep_type) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ /* Interrupt On Setup (IOS). for control ep */
+ tmp = (max_pkt_len << LENGTH_BIT_POS) | INTERRUPT_ON_COMPLETE;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ tmp = (max_pkt_len << LENGTH_BIT_POS) | (mult << EP_QUEUE_HEAD_MULT_POS);
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ tmp = max_pkt_len << LENGTH_BIT_POS;
+ if (zlt) {
+ tmp |= EP_QUEUE_HEAD_ZLT_SEL;
+ }
+ break;
+ default:
+ logf("error ep type is %d", ep_type);
+ return;
+ }
+
+ /* see 32.14.4.1 Queue Head Initialization */
+
+ /* write the wMaxPacketSize field as required by the USB Chapter9 or application specific portocol */
+ qh->endpt_cap = tmp;
+
+ /* write the next dTD Terminate bit fild to 1 */
+ qh->dtd_ovrl.next_dtd = 1;
+
+ /* write the Active bit in the status field to 0 */
+ qh->dtd_ovrl.dtd_token &= ~STATUS_ACTIVE;
+
+ /* write the Hald bit in the status field to 0 */
+ qh->dtd_ovrl.dtd_token &= ~STATUS_HALTED;
+
+ logf("qh: init %d", (2 * ep_num + dir));
+}
+
+static void td_init(struct dtd* td, void* buffer, uint32_t todo) {
+
+ /* see 32.14.5.2 Building a Transfer Descriptor */
+
+ /* init first 7 dwords with 0 */
+ memset(td, 0, sizeof(struct dtd)); /* set set all to 0 */
+
+ /* set terminate bit to 1*/
+ td->next_dtd = 1;
+
+ /* fill in total bytes with transfer size */
+ td->dtd_token = (todo << 16);
+
+ /* set interrupt on compilte if desierd */
+ td->dtd_token |= INTERRUPT_ON_COMPLETE;
+
+ /* initialize the status field with the active bit set to 1 and all remaining status bits to 0 */
+ td->dtd_token |= STATUS_ACTIVE;
+
+ td->buf_ptr0 = (uint32_t)buffer;
+}
+
+static void ep_setup(unsigned char ep_num, unsigned char dir, unsigned char ep_type) {
+
+ unsigned int tmp_epctrl = 0;
+ struct timer t;
+
+ tmp_epctrl = UDC_ENDPTCTRL(ep_num);
+ if (dir) {
+ if (ep_num) {
+ tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
+ }
+ logf("tx enablde");
+ tmp_epctrl |= EPCTRL_TX_ENABLE;
+ tmp_epctrl |= ((unsigned int)(ep_type) << EPCTRL_TX_EP_TYPE_SHIFT);
+ } else {
+ if (ep_num) {
+ tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
+ }
+ logf("rx enablde");
+ tmp_epctrl |= EPCTRL_RX_ENABLE;
+ tmp_epctrl |= ((unsigned int)(ep_type) << EPCTRL_RX_EP_TYPE_SHIFT);
+ }
+
+ UDC_ENDPTCTRL(ep_num) = tmp_epctrl;
+
+ /* wait for the write reg to finish */
+
+ timer_set(&t, SETUP_TIMER);
+ while (!(UDC_ENDPTCTRL(ep_num) & (tmp_epctrl & (EPCTRL_TX_ENABLE | EPCTRL_RX_ENABLE)))) {
+ if (timer_expired(&t)) {
+ logf("TIMEOUT: enable ep");
+ return;
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+/* helpers for sending/receiving */
+
+static int td_enqueue(struct dtd* td, struct dqh* qh, unsigned int mask) {
+
+ struct timer t;
+
+ qh->dtd_ovrl.next_dtd = (unsigned int)td;
+ qh->dtd_ovrl.dtd_token &= ~0xc0;
+
+ timer_set(&t, PRIME_TIMER);
+ UDC_ENDPTPRIME |= mask;
+
+ while ((UDC_ENDPTPRIME & mask)) {
+ if (timer_expired(&t)) {
+ logf("timeout->prime");
+ }
+ }
+
+ if ((UDC_ENDPTSTAT & mask) == 0) {
+ logf("Endptstat 0x%x", UDC_ENDPTSTAT);
+ logf("HW_ERROR");
+ }
+
+ return 0;
+}
+
+static int td_wait(struct dtd* td, unsigned int mask) {
+
+ struct timer t;
+ timer_set(&t, TRANSFER_TIMER);
+
+ for (;;) {
+ if ((UDC_ENDPTCOMPLETE & mask) != 0) {
+ UDC_ENDPTCOMPLETE |= mask;
+ }
+
+ if ((td->dtd_token & (1 << 7)) == 0) {
+ return 0;
+ }
+
+ if (timer_expired(&t)) {
+ return ERROR_TIMEOUT;
+ }
+ }
+}
+
+static int usb_ack(struct usb_ctrlrequest * s, int error) {
+
+ if (error) {
+ logf("STALLing ep0");
+ UDC_ENDPTCTRL0 |= 1 << 16; /* stall */
+ return 0;
+ }
+
+ res.buf = NULL;
+ res.length = 0;
+
+ if (s->bRequestType & 0x80) {
+ logf("ack in");
+ return usb_arcotg_dcd_receive(NULL, &res);
+ } else {
+ logf("ack out");
+ return usb_arcotg_dcd_send(NULL, &res);
+ }
+}
diff --git a/firmware/drivers/usb/arcotg_dcd.h b/firmware/drivers/usb/arcotg_dcd.h
new file mode 100644
index 0000000..127ee43
--- /dev/null
+++ b/firmware/drivers/usb/arcotg_dcd.h
@@ -0,0 +1,173 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: $
+ *
+ * Copyright (C) 2007 by Christian Gmeiner
+ *
+ * Based on code from the Linux Target Image Builder from Freescale
+ * available at http://www.bitshrine.org/ and
+ * http://www.bitshrine.org/gpp/linux-2.6.16-mx31-usb-2.patch
+ * Adapted for Rockbox in January 2007
+ * Original file: drivers/usb/gadget/arcotg_udc.c
+ *
+ * USB Device Controller Driver
+ * Driver for ARC OTG USB module in the i.MX31 platform, etc.
+ *
+ * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Based on mpc-udc.h
+ * Author: Li Yang (leoli@freescale.com)
+ * Jiang Bo (Tanya.jiang@freescale.com)
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef _ARCOTG_DCD_H_
+#define _ARCOTG_DCD_H_
+
+#include "usbstack/core.h"
+#include "arcotg_udc.h"
+
+/*-------------------------------------------------------------------------*/
+
+#define ep_is_in(EP) (((EP)->desc->bEndpointAddress & USB_DIR_IN)==USB_DIR_IN)
+
+#define EP_DIR_IN 1
+#define EP_DIR_OUT 0
+
+/*-------------------------------------------------------------------------*/
+
+/* pipe direction macro from device view */
+#define USB_RECV (0) /* OUT EP */
+#define USB_SEND (1) /* IN EP */
+
+/* Shared Bit Masks for Endpoint Queue Head and Endpoint Transfer Descriptor */
+#define TERMINATE (1 << 0)
+#define STATUS_ACTIVE (1 << 7)
+#define STATUS_HALTED (1 << 6)
+#define STATUS_DATA_BUFF_ERR (1 << 5)
+#define STATUS_TRANSACTION_ERR (1 << 4)
+#define INTERRUPT_ON_COMPLETE (1 << 15)
+#define LENGTH_BIT_POS (16)
+#define ADDRESS_MASK (0xFFFFFFE0)
+#define ERROR_MASK (DTD_STATUS_HALTED | \
+ DTD_STATUS_DATA_BUFF_ERR | \
+ DTD_STATUS_TRANSACTION_ERR)
+
+#define RESERVED_FIELDS ((1 << 0) | (1 << 2) | (1 << 4) | \
+ (1 << 8) | (1 << 9) | (1 << 12)| \
+ (1 << 13)| (1 << 14)| (1 << 31))
+
+/* Endpoint Queue Head Bit Masks */
+#define EP_QUEUE_HEAD_MULT_POS (30)
+#define EP_QUEUE_HEAD_ZLT_SEL (0x20000000)
+#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff)
+#define EP_QUEUE_HEAD_MULTO (0x00000C00)
+#define EP_QUEUE_CURRENT_OFFSET_MASK (0x00000FFF)
+#define EP_QUEUE_FRINDEX_MASK (0x000007FF)
+#define EP_MAX_LENGTH_TRANSFER (0x4000)
+
+/*-------------------------------------------------------------------------*/
+
+/* ep name is important, it should obey the convention of ep_match() */
+/* even numbered EPs are OUT or setup, odd are IN/INTERRUPT */
+static const char* ep_name[] = {
+ "ep0-control", NULL, /* everyone has ep0 */
+ /* 7 configurable endpoints */
+ "ep1out",
+ "ep1in",
+ "ep2out",
+ "ep2in",
+ "ep3out",
+ "ep3in",
+ "ep4out",
+ "ep4in",
+ "ep5out",
+ "ep5in",
+ "ep6out",
+ "ep6in",
+ "ep7out",
+ "ep7in"
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Endpoint Transfer Descriptor data struct */
+struct dtd {
+ uint32_t next_dtd; /* Next TD pointer(31-5), T(0) set indicate invalid */
+ uint32_t dtd_token; /* Total bytes (30-16), IOC (15), MultO(11-10), STS (7-0) */
+ uint32_t buf_ptr0; /* Buffer pointer Page 0 */
+ uint32_t buf_ptr1; /* Buffer pointer Page 1 */
+ uint32_t buf_ptr2; /* Buffer pointer Page 2 */
+ uint32_t buf_ptr3; /* Buffer pointer Page 3 */
+ uint32_t buf_ptr4; /* Buffer pointer Page 4 */
+ uint32_t res; /* make it an even 8 words */
+} __attribute((packed));
+
+/* Endpoint Queue Head*/
+struct dqh {
+ uint32_t endpt_cap; /* Mult(31-30) , Zlt(29) , Max Pkt len
+ * and IOS(15) */
+ uint32_t cur_dtd; /* Current dTD Pointer(31-5) */
+ struct dtd dtd_ovrl; /* Transfer descriptor */
+ uint32_t setup_buffer[2]; /* Setup data 8 bytes */
+ uint32_t res2[4]; /* pad out to 64 bytes */
+} __attribute((packed));
+
+#define RESPONSE_SIZE 30
+
+/* our controller struct */
+struct arcotg_dcd {
+ struct usb_ctrlrequest local_setup_buff;
+ struct usb_ep endpoints[USB_MAX_PIPES];
+ struct usb_response response[RESPONSE_SIZE];
+ enum usb_device_state usb_state;
+ enum usb_device_state resume_state;
+ bool stopped;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* usb_controller functions */
+void usb_arcotg_dcd_init(void);
+void usb_arcotg_dcd_shutdown(void);
+void usb_arcotg_dcd_irq(void);
+void usb_arcotg_dcd_start(void);
+void usb_arcotg_dcd_stop(void);
+
+/* usb controller ops */
+int usb_arcotg_dcd_enable(struct usb_ep* ep);
+int usb_arcotg_dcd_disable(struct usb_ep* ep);
+int usb_arcotg_dcd_set_halt(struct usb_ep* ep, bool halt);
+int usb_arcotg_dcd_send(struct usb_ep* ep, struct usb_response* request);
+int usb_arcotg_dcd_receive(struct usb_ep* ep, struct usb_response* res);
+
+/* interrupt handlers */
+static void setup_received_int(struct usb_ctrlrequest* request);
+static void port_change_int(void);
+static void reset_int(void);
+static void suspend_int(void);
+static void resume_int(void);
+
+/* life cycle */
+static void qh_init(unsigned char ep_num, unsigned char dir, unsigned char ep_type,
+ unsigned int max_pkt_len, unsigned int zlt, unsigned char mult);
+static void td_init(struct dtd* td, void* buffer, uint32_t todo);
+static void ep_setup(unsigned char ep_num, unsigned char dir, unsigned char ep_type);
+
+/* helpers for tx/rx */
+static int td_enqueue(struct dtd* td, struct dqh* qh, unsigned int mask);
+static int td_wait(struct dtd* td, unsigned int mask);
+static int usb_ack(struct usb_ctrlrequest * s, int error);
+
+#endif /*_ARCOTG_DCD_H_*/