/*************************************************************************** * __________ __ ___. * 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 #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) { 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); } }