+typedef struct {
+ qh_t *qh;
+ td_t *tds;
+ td_t *last_td;
+ u8 *data;
+ int lastread;
+ int total;
+ int reqsize;
+} intr_q;
+
+/* create and hook-up an intr queue into device schedule */
+static void*
+uhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming)
+{
+ u8 *data = malloc(reqsize*reqcount);
+ td_t *tds = memalign(16, sizeof(td_t) * reqcount);
+ qh_t *qh = memalign(16, sizeof(qh_t));
+
+ qh->elementlinkptr.ptr = virt_to_phys(tds);
+ qh->elementlinkptr.terminate = 0;
+
+ intr_q *q = malloc(sizeof(intr_q));
+ q->qh = qh;
+ q->tds = tds;
+ q->data = data;
+ q->lastread = 0;
+ q->total = reqcount;
+ q->reqsize = reqsize;
+ q->last_td = &tds[reqcount - 1];
+
+ memset (tds, 0, sizeof (td_t) * reqcount);
+ 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;
+
+ tds[i].pid = ep->direction;
+ 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->lowspeed;
+ tds[i].bufptr = virt_to_phys (data);
+ tds[i].status_active = 1;
+ 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;
+ 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;
+ }
+ return q;
+}
+
+/* remove queue from device schedule, dropping all data that came in */
+static void
+uhci_destroy_intr_queue (endpoint_t *ep, void *q_)
+{
+ intr_q *q = (intr_q*)q_;
+ u32 val = virt_to_phys (q->qh);
+ u32 end = virt_to_phys (UHCI_INST (ep->dev->controller)->qh_intr);
+ int i;
+ for (i=0; i<1024; i++) {
+ u32 oldptr = 0;
+ u32 ptr = UHCI_INST (ep->dev->controller)->framelistptr[i].ptr;
+ 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;
+ free(phys_to_virt(ptr));
+ break;
+ }
+ oldptr = ptr;
+ ptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr.ptr;
+ }
+ }
+ free(q->data);
+ free(q->tds);
+ free(q->qh);
+ free(q);
+}
+
+/* read one intr-packet from queue, if available. extend the queue for new input.
+ return NULL if nothing new available.
+ Recommended use: while (data=poll_intr_queue(q)) process(data);
+ */
+static u8*
+uhci_poll_intr_queue (void *q_)
+{
+ intr_q *q = (intr_q*)q_;
+ if (q->tds[q->lastread].status_active == 0) {
+ /* FIXME: handle errors */
+ int current = q->lastread;
+ int previous;
+ if (q->lastread == 0) {
+ previous = q->total - 1;
+ } else {
+ previous = q->lastread - 1;
+ }
+ q->tds[previous].status = 0;
+ q->tds[previous].ptr = 0;
+ q->tds[previous].terminate = 1;
+ 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 = &q->tds[previous];
+ }
+ q->tds[previous].status_active = 1;
+ q->lastread = (q->lastread + 1) % q->total;
+ return &q->data[current*q->reqsize];
+ }
+ return NULL;
+}
+