diff options
-rw-r--r-- | payloads/libpayload/drivers/usb/uhci.c | 250 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/uhci_private.h | 73 |
2 files changed, 133 insertions, 190 deletions
diff --git a/payloads/libpayload/drivers/usb/uhci.c b/payloads/libpayload/drivers/usb/uhci.c index b964ee7a0b..07a074291d 100644 --- a/payloads/libpayload/drivers/usb/uhci.c +++ b/payloads/libpayload/drivers/usb/uhci.c @@ -66,7 +66,7 @@ td_dump (td_t *td) { char td_value[3]; const char *td_type; - switch (td->pid) { + switch (td->token & TD_PID_MASK) { case UHCI_SETUP: td_type="SETUP"; break; @@ -77,23 +77,24 @@ td_dump (td_t *td) td_type="OUT"; break; default: - sprintf(td_value, "%x", td->pid); + sprintf(td_value, "%x", td->token & TD_PID_MASK); td_type=td_value; } debug ("%s packet (at %lx) to %x.%x failed\n", td_type, - virt_to_phys (td), td->dev_addr, td->endp); - debug ("td (counter at %x) returns: ", td->counter); - debug (" bitstuff err: %x, ", td->status_bitstuff_err); - debug (" CRC err: %x, ", td->status_crc_err); - debug (" NAK rcvd: %x, ", td->status_nakrcvd); - debug (" Babble: %x, ", td->status_babble); - debug (" Data Buffer err: %x, ", td->status_databuf_err); - debug (" Stalled: %x, ", td->status_stalled); - debug (" Active: %x\n", td->status_active); - if (td->status_babble) + virt_to_phys (td), (td->token & TD_DEVADDR_MASK) >> TD_DEVADDR_SHIFT, + (td->token & TD_EP_MASK) >> TD_EP_SHIFT); + debug ("td (counter at %x) returns: ", td->ctrlsts >> TD_COUNTER_SHIFT); + debug (" bitstuff err: %x, ", !!(td->ctrlsts & TD_STATUS_BITSTUFF_ERR)); + debug (" CRC err: %x, ", !!(td->ctrlsts & TD_STATUS_CRC_ERR)); + debug (" NAK rcvd: %x, ", !!(td->ctrlsts & TD_STATUS_NAK_RCVD)); + debug (" Babble: %x, ", !!(td->ctrlsts & TD_STATUS_BABBLE)); + debug (" Data Buffer err: %x, ", !!(td->ctrlsts & TD_STATUS_DATABUF_ERR)); + debug (" Stalled: %x, ", !!(td->ctrlsts & TD_STATUS_STALLED)); + debug (" Active: %x\n", !!(td->ctrlsts & TD_STATUS_ACTIVE)); + if (td->ctrlsts & TD_STATUS_BABBLE) debug (" Babble because of %s\n", - td->status_bitstuff_err ? "host" : "device"); - if (td->status_active) + (td->ctrlsts & TD_STATUS_BITSTUFF_ERR) ? "host" : "device"); + if (td->ctrlsts & TD_STATUS_ACTIVE) debug (" still active - timeout?\n"); } @@ -197,34 +198,24 @@ uhci_init (pcidev_t addr) ! UHCI_INST (controller)->qh_last) fatal("Not enough memory for USB controller queues.\n"); - UHCI_INST (controller)->qh_prei->headlinkptr.ptr = - virt_to_phys (UHCI_INST (controller)->qh_intr); - UHCI_INST (controller)->qh_prei->headlinkptr.queue_head = 1; - UHCI_INST (controller)->qh_prei->elementlinkptr.ptr = 0; - UHCI_INST (controller)->qh_prei->elementlinkptr.terminate = 1; - - UHCI_INST (controller)->qh_intr->headlinkptr.ptr = - virt_to_phys (UHCI_INST (controller)->qh_data); - UHCI_INST (controller)->qh_intr->headlinkptr.queue_head = 1; - UHCI_INST (controller)->qh_intr->elementlinkptr.ptr = 0; - UHCI_INST (controller)->qh_intr->elementlinkptr.terminate = 1; - - UHCI_INST (controller)->qh_data->headlinkptr.ptr = - virt_to_phys (UHCI_INST (controller)->qh_last); - UHCI_INST (controller)->qh_data->headlinkptr.queue_head = 1; - UHCI_INST (controller)->qh_data->elementlinkptr.ptr = 0; - UHCI_INST (controller)->qh_data->elementlinkptr.terminate = 1; - - UHCI_INST (controller)->qh_last->headlinkptr.ptr = virt_to_phys (UHCI_INST (controller)->qh_data); - UHCI_INST (controller)->qh_last->headlinkptr.terminate = 1; - UHCI_INST (controller)->qh_last->elementlinkptr.ptr = virt_to_phys (antiberserk); - UHCI_INST (controller)->qh_last->elementlinkptr.terminate = 1; + UHCI_INST (controller)->qh_prei->headlinkptr = + virt_to_phys (UHCI_INST (controller)->qh_intr) | FLISTP_QH; + UHCI_INST (controller)->qh_prei->elementlinkptr = 0 | FLISTP_TERMINATE; + + UHCI_INST (controller)->qh_intr->headlinkptr = + virt_to_phys (UHCI_INST (controller)->qh_data) | FLISTP_QH; + UHCI_INST (controller)->qh_intr->elementlinkptr = 0 | FLISTP_TERMINATE; + + UHCI_INST (controller)->qh_data->headlinkptr = + virt_to_phys (UHCI_INST (controller)->qh_last) | FLISTP_QH; + UHCI_INST (controller)->qh_data->elementlinkptr = 0 | FLISTP_TERMINATE; + + UHCI_INST (controller)->qh_last->headlinkptr = virt_to_phys (UHCI_INST (controller)->qh_data) | FLISTP_TERMINATE; + UHCI_INST (controller)->qh_last->elementlinkptr = virt_to_phys (antiberserk) | FLISTP_TERMINATE; for (i = 0; i < 1024; i++) { - UHCI_INST (controller)->framelistptr[i].ptr = - virt_to_phys (UHCI_INST (controller)->qh_prei); - UHCI_INST (controller)->framelistptr[i].terminate = 0; - UHCI_INST (controller)->framelistptr[i].queue_head = 1; + UHCI_INST (controller)->framelistptr[i] = + virt_to_phys (UHCI_INST (controller)->qh_prei) | FLISTP_QH; } controller->devices[0]->controller = controller; controller->devices[0]->init = uhci_rh_init; @@ -272,18 +263,18 @@ static td_t * wait_for_completed_qh (hci_t *controller, qh_t *qh) { int timeout = 1000000; /* max 30 ms. */ - void *current = GET_TD (qh->elementlinkptr.ptr); - while ((qh->elementlinkptr.terminate == 0) && (timeout-- > 0)) { - if (current != GET_TD (qh->elementlinkptr.ptr)) { - current = GET_TD (qh->elementlinkptr.ptr); + void *current = GET_TD (qh->elementlinkptr); + while (((qh->elementlinkptr & FLISTP_TERMINATE) == 0) && (timeout-- > 0)) { + if (current != GET_TD (qh->elementlinkptr)) { + current = GET_TD (qh->elementlinkptr); timeout = 1000000; } uhci_reg_write16(controller, USBSTS, uhci_reg_read16(controller, USBSTS) | 0); // clear resettable registers udelay (30); } - return (GET_TD (qh->elementlinkptr.ptr) == - 0) ? 0 : GET_TD (phys_to_virt (qh->elementlinkptr.ptr)); + return (GET_TD (qh->elementlinkptr) == + 0) ? 0 : GET_TD (phys_to_virt (qh->elementlinkptr)); } static int @@ -314,57 +305,51 @@ uhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen memset (tds, 0, sizeof (td_t) * count); count--; /* to compensate for 0-indexed array */ for (i = 0; i < count; i++) { - tds[i].ptr = virt_to_phys (&tds[i + 1]); - tds[i].depth_first = 1; - tds[i].terminate = 0; + tds[i].ptr = virt_to_phys (&tds[i + 1]) | TD_DEPTH_FIRST; } - tds[count].ptr = 0; - tds[count].depth_first = 1; - tds[count].terminate = 1; - - tds[0].pid = UHCI_SETUP; - tds[0].dev_addr = dev->address; - tds[0].endp = endp; - tds[0].maxlen = maxlen (drlen); - tds[0].counter = 3; - tds[0].data_toggle = 0; - tds[0].lowspeed = dev->speed; + tds[count].ptr = 0 | TD_DEPTH_FIRST | TD_TERMINATE; + + tds[0].token = UHCI_SETUP | + dev->address << TD_DEVADDR_SHIFT | + endp << TD_EP_SHIFT | + TD_TOGGLE_DATA0 | + maxlen(drlen) << TD_MAXLEN_SHIFT; tds[0].bufptr = virt_to_phys (devreq); - tds[0].status_active = 1; + tds[0].ctrlsts = (3 << TD_COUNTER_SHIFT) | + (dev->speed?TD_LOWSPEED:0) | + TD_STATUS_ACTIVE; int toggle = 1; for (i = 1; i < count; i++) { switch (dir) { - case SETUP: tds[i].pid = UHCI_SETUP; break; - case IN: tds[i].pid = UHCI_IN; break; - case OUT: tds[i].pid = UHCI_OUT; break; + case SETUP: tds[i].token = UHCI_SETUP; break; + case IN: tds[i].token = UHCI_IN; break; + case OUT: tds[i].token = UHCI_OUT; break; } - tds[i].dev_addr = dev->address; - tds[i].endp = endp; - tds[i].maxlen = maxlen (min (mlen, dalen)); - tds[i].counter = 3; - tds[i].data_toggle = toggle; - tds[i].lowspeed = dev->speed; + tds[i].token |= dev->address << TD_DEVADDR_SHIFT | + endp << TD_EP_SHIFT | + maxlen (min (mlen, dalen)) << TD_MAXLEN_SHIFT | + toggle << TD_TOGGLE_SHIFT; tds[i].bufptr = virt_to_phys (data); - tds[i].status_active = 1; + tds[i].ctrlsts = (3 << TD_COUNTER_SHIFT) | + (dev->speed?TD_LOWSPEED:0) | + TD_STATUS_ACTIVE; toggle ^= 1; dalen -= mlen; data += mlen; } - tds[count].pid = (dir == OUT) ? UHCI_IN : UHCI_OUT; - tds[count].dev_addr = dev->address; - tds[count].endp = endp; - tds[count].maxlen = maxlen (0); - tds[count].counter = 0; /* as per linux 2.4.10 */ - tds[count].data_toggle = 1; - tds[count].lowspeed = dev->speed; + tds[count].token = (dir == OUT) ? UHCI_IN : UHCI_OUT | + dev->address << TD_DEVADDR_SHIFT | + endp << TD_EP_SHIFT | + maxlen(0) << TD_MAXLEN_SHIFT | + TD_TOGGLE_DATA1; tds[count].bufptr = 0; - tds[count].status_active = 1; - UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr = - virt_to_phys (tds); - UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0; - UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0; + tds[0].ctrlsts = (0 << TD_COUNTER_SHIFT) | /* as Linux 2.4.10 does */ + (dev->speed?TD_LOWSPEED:0) | + TD_STATUS_ACTIVE; + UHCI_INST (dev->controller)->qh_data->elementlinkptr = + virt_to_phys (tds) & ~(FLISTP_QH | FLISTP_TERMINATE); td_t *td = wait_for_completed_qh (dev->controller, UHCI_INST (dev->controller)-> qh_data); @@ -389,15 +374,9 @@ create_schedule (int numpackets) memset (tds, 0, sizeof (td_t) * numpackets); int i; for (i = 0; i < numpackets; i++) { - tds[i].ptr = virt_to_phys (&tds[i + 1]); - tds[i].terminate = 0; - tds[i].queue_head = 0; - tds[i].depth_first = 1; + tds[i].ptr = virt_to_phys (&tds[i + 1]) | TD_DEPTH_FIRST; } - tds[numpackets - 1].ptr = 0; - tds[numpackets - 1].terminate = 1; - tds[numpackets - 1].queue_head = 0; - tds[numpackets - 1].depth_first = 0; + tds[numpackets - 1].ptr = 0 | TD_TERMINATE; return tds; } @@ -406,32 +385,26 @@ fill_schedule (td_t *td, endpoint_t *ep, int length, unsigned char *data, int *toggle) { switch (ep->direction) { - case IN: td->pid = UHCI_IN; break; - case OUT: td->pid = UHCI_OUT; break; - case SETUP: td->pid = UHCI_SETUP; break; + case IN: td->token = UHCI_IN; break; + case OUT: td->token = UHCI_OUT; break; + case SETUP: td->token = UHCI_SETUP; break; } - td->dev_addr = ep->dev->address; - td->endp = ep->endpoint & 0xf; - td->maxlen = maxlen (length); - if (ep->direction == SETUP) - td->counter = 3; - else - td->counter = 0; - td->data_toggle = *toggle & 1; - td->lowspeed = ep->dev->speed; + td->token |= ep->dev->address << TD_DEVADDR_SHIFT | + (ep->endpoint & 0xf) << TD_EP_SHIFT | + maxlen (length) << TD_MAXLEN_SHIFT | + (*toggle & 1) << TD_TOGGLE_SHIFT; td->bufptr = virt_to_phys (data); - - td->status_active = 1; + td->ctrlsts = ((ep->direction == SETUP?3:0) << TD_COUNTER_SHIFT) | + ep->dev->speed?TD_LOWSPEED:0 | + TD_STATUS_ACTIVE; *toggle ^= 1; } static int run_schedule (usbdev_t *dev, td_t *td) { - UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr = - virt_to_phys (td); - UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0; - UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0; + UHCI_INST (dev->controller)->qh_data->elementlinkptr = + virt_to_phys (td) | ~(FLISTP_QH | FLISTP_TERMINATE); td = wait_for_completed_qh (dev->controller, UHCI_INST (dev->controller)->qh_data); if (td == 0) { @@ -493,9 +466,7 @@ uhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming if (!data || !tds || !qh) fatal("Not enough memory to create USB intr queue prerequisites.\n"); - qh->elementlinkptr.ptr = virt_to_phys(tds); - qh->elementlinkptr.queue_head = 0; - qh->elementlinkptr.terminate = 0; + qh->elementlinkptr = virt_to_phys(tds); intr_q *q = malloc(sizeof(intr_q)); if (!q) @@ -512,37 +483,28 @@ uhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming int i; for (i = 0; i < reqcount; i++) { tds[i].ptr = virt_to_phys (&tds[i + 1]); - tds[i].terminate = 0; - tds[i].queue_head = 0; - tds[i].depth_first = 0; switch (ep->direction) { - case IN: tds[i].pid = UHCI_IN; break; - case OUT: tds[i].pid = UHCI_OUT; break; - case SETUP: tds[i].pid = UHCI_SETUP; break; + case IN: tds[i].token = UHCI_IN; break; + case OUT: tds[i].token = UHCI_OUT; break; + case SETUP: tds[i].token = UHCI_SETUP; break; } - tds[i].dev_addr = ep->dev->address; - tds[i].endp = ep->endpoint & 0xf; - tds[i].maxlen = maxlen (reqsize); - tds[i].counter = 0; - tds[i].data_toggle = ep->toggle & 1; - tds[i].lowspeed = ep->dev->speed; + tds[i].token |= ep->dev->address << TD_DEVADDR_SHIFT | + (ep->endpoint & 0xf) << TD_EP_SHIFT | + maxlen (reqsize) << TD_MAXLEN_SHIFT | + (ep->toggle & 1) << TD_TOGGLE_SHIFT; tds[i].bufptr = virt_to_phys (data); - tds[i].status_active = 1; + tds[i].ctrlsts = (0 << TD_COUNTER_SHIFT) | + ep->dev->speed?TD_LOWSPEED:0 | + TD_STATUS_ACTIVE; ep->toggle ^= 1; data += reqsize; } - tds[reqcount - 1].ptr = 0; - tds[reqcount - 1].terminate = 1; - tds[reqcount - 1].queue_head = 0; - tds[reqcount - 1].depth_first = 0; + tds[reqcount - 1].ptr = 0 | TD_TERMINATE; for (i = reqtiming; i < 1024; i += reqtiming) { /* FIXME: wrap in another qh, one for each occurance of the qh in the framelist */ - qh->headlinkptr.ptr = UHCI_INST (ep->dev->controller)->framelistptr[i].ptr; - qh->headlinkptr.terminate = 0; - UHCI_INST (ep->dev->controller)->framelistptr[i].ptr = virt_to_phys(qh); - UHCI_INST (ep->dev->controller)->framelistptr[i].terminate = 0; - UHCI_INST (ep->dev->controller)->framelistptr[i].queue_head = 1; + qh->headlinkptr = UHCI_INST (ep->dev->controller)->framelistptr[i] & ~FLISTP_TERMINATE; + UHCI_INST (ep->dev->controller)->framelistptr[i] = virt_to_phys(qh) | FLISTP_QH; } return q; } @@ -557,15 +519,15 @@ uhci_destroy_intr_queue (endpoint_t *ep, void *q_) int i; for (i=0; i<1024; i++) { u32 oldptr = 0; - u32 ptr = UHCI_INST (ep->dev->controller)->framelistptr[i].ptr; + u32 ptr = UHCI_INST (ep->dev->controller)->framelistptr[i]; while (ptr != end) { - if (((qh_t*)phys_to_virt(ptr))->elementlinkptr.ptr == val) { - ((qh_t*)phys_to_virt(oldptr))->headlinkptr.ptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr.ptr; + if (((qh_t*)phys_to_virt(ptr))->elementlinkptr == val) { + ((qh_t*)phys_to_virt(oldptr))->headlinkptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr; free(phys_to_virt(ptr)); break; } oldptr = ptr; - ptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr.ptr; + ptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr; } } free(q->data); @@ -582,7 +544,7 @@ static u8* uhci_poll_intr_queue (void *q_) { intr_q *q = (intr_q*)q_; - if (q->tds[q->lastread].status_active == 0) { + if ((q->tds[q->lastread].ctrlsts & TD_STATUS_ACTIVE) == 0) { /* FIXME: handle errors */ int current = q->lastread; int previous; @@ -591,15 +553,13 @@ uhci_poll_intr_queue (void *q_) } else { previous = q->lastread - 1; } - q->tds[previous].status = 0; - q->tds[previous].ptr = 0; - q->tds[previous].terminate = 1; + q->tds[previous].ctrlsts &= ~TD_STATUS_MASK; + q->tds[previous].ptr = 0 | TD_TERMINATE; if (q->last_td != &q->tds[previous]) { - q->last_td->ptr = virt_to_phys(&q->tds[previous]); - q->last_td->terminate = 0; + q->last_td->ptr = virt_to_phys(&q->tds[previous]) & ~TD_TERMINATE; q->last_td = &q->tds[previous]; } - q->tds[previous].status_active = 1; + q->tds[previous].ctrlsts |= TD_STATUS_ACTIVE; q->lastread = (q->lastread + 1) % q->total; return &q->data[current*q->reqsize]; } diff --git a/payloads/libpayload/drivers/usb/uhci_private.h b/payloads/libpayload/drivers/usb/uhci_private.h index adcd91c7b3..f650c4a468 100644 --- a/payloads/libpayload/drivers/usb/uhci_private.h +++ b/payloads/libpayload/drivers/usb/uhci_private.h @@ -32,55 +32,38 @@ typedef enum { UHCI_SETUP = 0x2d, UHCI_IN = 0x69, UHCI_OUT = 0xe1 } uhci_pid_t; -typedef union { - struct { - unsigned long terminate:1; - unsigned long queue_head:1; - unsigned long:2; - unsigned long ptr_part:28; - }; - u32 ptr; -} __attribute__ ((packed)) flistp_t; +typedef u32 flistp_t; +#define FLISTP_TERMINATE 1 +#define FLISTP_QH 2 typedef struct { - union { - struct { - unsigned long terminate:1; - unsigned long queue_head:1; - unsigned long depth_first:1; - unsigned long:29; - } __attribute__ ((packed)); - u32 ptr; - } __attribute__ ((packed)); + u32 ptr; +#define TD_TERMINATE 1 +#define TD_QH 2 +#define TD_DEPTH_FIRST 4 - volatile unsigned long actlen:11; - volatile unsigned long:5; - union { - struct { - unsigned long:1; // bit 0 - unsigned long status_bitstuff_err:1; - unsigned long status_crc_err:1; - unsigned long status_nakrcvd:1; - unsigned long status_babble:1; - unsigned long status_databuf_err:1; - unsigned long status_stalled:1; - unsigned long status_active:1; // bit 7 - } __attribute__ ((packed)); - unsigned char status; - } __attribute__ ((packed)); - volatile unsigned long ioc:1; /* interrupt on complete */ - volatile unsigned long isochronous:1; - volatile unsigned long lowspeed:1; - volatile unsigned long counter:2; - volatile unsigned long shortpck:1; - volatile unsigned long:2; + u32 ctrlsts; +#define TD_STATUS_MASK (0xff << 16) +#define TD_STATUS_BITSTUFF_ERR (1 << 17) +#define TD_STATUS_CRC_ERR (1 << 18) +#define TD_STATUS_NAK_RCVD (1 << 19) +#define TD_STATUS_BABBLE (1 << 20) +#define TD_STATUS_DATABUF_ERR (1 << 21) +#define TD_STATUS_STALLED (1 << 22) +#define TD_STATUS_ACTIVE (1 << 23) +#define TD_LOWSPEED (1 << 26) +#define TD_COUNTER_SHIFT 27 - unsigned long pid:8; - unsigned long dev_addr:7; - unsigned long endp:4; - unsigned long data_toggle:1; - unsigned long:1; - unsigned long maxlen:11; + u32 token; +#define TD_PID_MASK 0xff +#define TD_DEVADDR_SHIFT 8 +#define TD_DEVADDR_MASK (((1<<7)-1) << TD_DEVADDR_SHIFT) +#define TD_EP_SHIFT 15 +#define TD_EP_MASK (0xf << TD_EP_SHIFT) +#define TD_TOGGLE_SHIFT 19 +#define TD_MAXLEN_SHIFT 21 +#define TD_TOGGLE_DATA0 0 +#define TD_TOGGLE_DATA1 (1 << TD_TOGGLE_SHIFT) u32 bufptr; |