diff options
| author | Christian Gmeiner <christian.gmeiner@gmail.com> | 2007-08-27 16:04:32 +0000 |
|---|---|---|
| committer | Christian Gmeiner <christian.gmeiner@gmail.com> | 2007-08-27 16:04:32 +0000 |
| commit | 8181a0c905a591caef684a2d7487feedbec84c10 (patch) | |
| tree | 3939183e8e73928d1b5dcfc97b40f33b6baa309b /firmware/drivers | |
| parent | 9305c86f5b8fdfd60882428f884ba29bded8da78 (diff) | |
| download | rockbox-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.c | 983 | ||||
| -rw-r--r-- | firmware/drivers/usb/arcotg_dcd.h | 173 |
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_*/ |