rockbox/firmware/drivers/usb/arcotg_dcd.c
Christian Gmeiner 3a0c78bb76 add usb state handling and other small fixes
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14541 a1c6a512-1295-4272-9138-f99709370657
2007-08-30 19:23:37 +00:00

1026 lines
26 KiB
C

/***************************************************************************
* __________ __ ___.
* 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");
/* 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;
}*/
}
for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
if ((UDC_ENDPTCOMPLETE & (1 << i)) == 1) {
logf("COMPLETE on %d", i);
UDC_ENDPTCOMPLETE |= (1 << i);
}
}
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;
bool set_config = false;
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_SET_CONFIGURATION:
set_config = true;
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);
/* set address and usb state after USB_REQ_SET_ADDRESS */
if (address != 0) {
logf("setting address to %d", address);
UDC_DEVICEADDR = address << 25;
dcd_controller.usb_state = USB_STATE_ADDRESS;
}
/* update usb state after successfull USB_REQ_SET_CONFIGURATION */
if (set_config) {
if (handled > 0) {
dcd_controller.usb_state = USB_STATE_CONFIGURED;
}
}
}
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;
}
}
if (arcotg_dcd.speed == speed) {
return;
}
/* 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)
{
struct timer t;
/* clear device address */
UDC_DEVICEADDR = 0 << 25;
/* update usb state */
dcd_controller.usb_state = USB_STATE_DEFAULT;
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,
struct usb_endpoint_descriptor* desc)
{
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;
}
max = desc->wMaxPacketSize;
retval = -EINVAL;
/* check the max package size validate for this endpoint */
/* Refer to USB2.0 spec table 9-13. */
switch (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:
zlt = 1;
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;
ep->desc = desc;
/* hardware special operation */
/* Init EPx Queue Head (Ep Capabilites field in QH
* according to max, zlt, mult) */
qh_init(ep->ep_num,
(desc->bEndpointAddress & USB_DIR_IN) ? USB_RECV : USB_SEND,
(unsigned char) (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK),
max, zlt, mult);
/* Init endpoint x at here */
ep_setup(ep->ep_num,
(unsigned char)(desc->bEndpointAddress & USB_DIR_IN) ?
USB_RECV : USB_SEND,
(unsigned char)(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 (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,
desc->bEndpointAddress & 0x0f,
(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_RECV : USB_SEND;
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);
}
}