patch-2.3.1 linux/drivers/usb/uhci.c
Next file: linux/drivers/usb/uhci.h
Previous file: linux/drivers/usb/ohci.c
Back to the patch index
Back to the overall index
- Lines: 383
- Date:
Thu May 13 14:34:01 1999
- Orig file:
v2.3.0/linux/drivers/usb/uhci.c
- Orig date:
Fri Apr 30 08:20:53 1999
diff -u --recursive --new-file v2.3.0/linux/drivers/usb/uhci.c linux/drivers/usb/uhci.c
@@ -47,7 +47,7 @@
#define compile_assert(x) do { switch (0) { case 1: case !(x): } } while (0)
-static struct wait_queue *uhci_configure = NULL;
+static DECLARE_WAIT_QUEUE_HEAD(uhci_configure);
/*
* Return the result of a TD..
@@ -61,15 +61,17 @@
/* Some debugging code */
if (status) {
int i = 10;
- struct uhci_td *tmp = dev->control_td;
+ struct uhci_td *tmp = td->first;
printk("uhci_td_result() failed with status %d\n", status);
show_status(dev->uhci);
do {
show_td(tmp);
- tmp++;
+ if ((tmp->link & 1) || (tmp->link & 2))
+ break;
+ tmp = bus_to_virt(tmp->link & ~0xF);
if (!--i)
break;
- } while (tmp <= td);
+ } while (1);
}
return status;
}
@@ -279,7 +281,7 @@
unsigned int destination, status;
/* Destination: pipe destination with INPUT */
- destination = (pipe & 0x0007ff00) | 0x69;
+ destination = (pipe & 0x0007ff00) | 0x69;
/* Status: slow/fast, Interrupt, Active, Short Packet Detect Infinite Errors */
status = (pipe & (1 << 26)) | (1 << 24) | (1 << 23) | (1 << 29) | (0 << 27);
@@ -292,6 +294,7 @@
td->status = status; /* In */
td->info = destination | (7 << 21); /* 8 bytes of data */
td->buffer = virt_to_bus(dev->data);
+ td->first = td;
td->qh = interrupt_qh;
interrupt_qh->skel = &dev->uhci->root_hub->skel_int8_qh;
@@ -305,6 +308,202 @@
}
/*
+ * Isochronous thread operations
+ */
+
+int uhci_compress_isochronous(struct usb_device *usb_dev, void *_isodesc)
+{
+ struct uhci_iso_td *isodesc = (struct uhci_iso_td *)_isodesc;
+ char *data = isodesc->data;
+ int i, totlen = 0;
+
+ for (i = 0; i < isodesc->num; i++) {
+ char *cdata = bus_to_virt(isodesc->td[i].buffer & ~0xF);
+ int n = (isodesc->td[i].status + 1) & 0x7FF;
+
+ if ((cdata != data) && (n))
+ memmove(data, cdata, n);
+
+#if 0
+if (n && n != 960)
+ printk("underrun: %d %d\n", i, n);
+#endif
+if ((isodesc->td[i].status >> 16) & 0xFF)
+ printk("error: %d %X\n", i, (isodesc->td[i].status >> 16));
+
+ data += n;
+ totlen += n;
+ }
+
+ return totlen;
+}
+
+int uhci_unsched_isochronous(struct usb_device *usb_dev, void *_isodesc)
+{
+ struct uhci_device *dev = usb_to_uhci(usb_dev);
+ struct uhci *uhci = dev->uhci;
+ struct uhci_iso_td *isodesc = (struct uhci_iso_td *)_isodesc;
+ int i;
+
+ if ((isodesc->frame < 0) || (isodesc->frame > 1023))
+ return 1;
+
+ /* Remove from previous frames */
+ for (i = 0; i < isodesc->num; i++) {
+ /* Turn off Active and IOC bits */
+ isodesc->td[i].status &= ~(3 << 23);
+ uhci->fl->frame[(isodesc->frame + i) % 1024] = isodesc->td[i].link;
+ }
+
+ isodesc->frame = -1;
+
+ return 0;
+}
+
+/* td points to the one td we allocated for isochronous transfers */
+int uhci_sched_isochronous(struct usb_device *usb_dev, void *_isodesc, void *_pisodesc)
+{
+ struct uhci_device *dev = usb_to_uhci(usb_dev);
+ struct uhci *uhci = dev->uhci;
+ struct uhci_iso_td *isodesc = (struct uhci_iso_td *)_isodesc;
+ struct uhci_iso_td *pisodesc = (struct uhci_iso_td *)_pisodesc;
+ int frame, i;
+
+ if (isodesc->frame != -1) {
+ printk("isoc queue not removed\n");
+ uhci_unsched_isochronous(usb_dev, isodesc);
+ }
+
+ /* Insert TD into list */
+ if (!pisodesc) {
+ frame = inw(uhci->io_addr + USBFRNUM) % 1024;
+ /* HACK: Start 2 frames from now */
+ frame = (frame + 2) % 1024;
+ } else
+ frame = (pisodesc->endframe + 1) % 1024;
+
+#if 0
+printk("scheduling first at frame %d\n", frame);
+#endif
+
+ for (i = 0; i < isodesc->num; i++) {
+ /* Active */
+ isodesc->td[i].status |= (1 << 23);
+ isodesc->td[i].backptr = &uhci->fl->frame[(frame + i) % 1024];
+ isodesc->td[i].link = uhci->fl->frame[(frame + i) % 1024];
+ uhci->fl->frame[(frame + i) % 1024] = virt_to_bus(&isodesc->td[i]);
+ }
+
+#if 0
+printk("last at frame %d\n", (frame + i - 1) % 1024);
+#endif
+
+ /* Interrupt */
+ isodesc->td[i - 1].status |= (1 << 24);
+
+ isodesc->frame = frame;
+ isodesc->endframe = (frame + isodesc->num - 1) % 1024;
+
+#if 0
+ return uhci_td_result(dev, td[num - 1]);
+#endif
+ return 0;
+}
+
+/*
+ * Initialize isochronous queue
+ */
+void *uhci_alloc_isochronous(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int maxsze, usb_device_irq completed, void *dev_id)
+{
+ struct uhci_device *dev = usb_to_uhci(usb_dev);
+ unsigned long destination, status;
+ struct uhci_td *td;
+ int ret, i;
+ struct uhci_iso_td *isodesc;
+
+ isodesc = kmalloc(sizeof(*isodesc), GFP_KERNEL);
+ if (!isodesc) {
+ printk("Couldn't allocate isodesc!\n");
+ return NULL;
+ }
+ memset(isodesc, 0, sizeof(*isodesc));
+
+ /* Carefully work around the non contiguous pages */
+ isodesc->num = (len / PAGE_SIZE) * (PAGE_SIZE / maxsze);
+ isodesc->td = kmalloc(sizeof(struct uhci_td) * isodesc->num, GFP_KERNEL);
+ isodesc->frame = isodesc->endframe = -1;
+ isodesc->data = data;
+ isodesc->maxsze = maxsze;
+
+ if (!isodesc->td) {
+ printk("Couldn't allocate td's\n");
+ kfree(isodesc);
+ return NULL;
+ }
+
+ isodesc->frame = isodesc->endframe = -1;
+
+ /*
+ * Build the DATA TD's
+ */
+ i = 0;
+ do {
+ /* Build the TD for control status */
+ td = &isodesc->td[i];
+
+ /* The "pipe" thing contains the destination in bits 8--18 */
+ destination = (pipe & 0x0007ff00);
+
+ if (usb_pipeout(pipe))
+ destination |= 0xE1; /* OUT */
+ else
+ destination |= 0x69; /* IN */
+
+ /* Status: slow/fast, Active, Isochronous */
+ status = (pipe & (1 << 26)) | (1 << 23) | (1 << 25);
+
+ /*
+ * Build the TD for the control request
+ */
+ td->status = status;
+ td->info = destination | ((maxsze - 1) << 21);
+ td->buffer = virt_to_bus(data);
+ td->first = td;
+ td->backptr = NULL;
+
+ i++;
+
+ data += maxsze;
+
+ if (((int)data % PAGE_SIZE) + maxsze >= PAGE_SIZE)
+ data = (char *)(((int)data + maxsze) & ~(PAGE_SIZE - 1));
+
+ len -= maxsze;
+ } while (i < isodesc->num);
+
+ /* IOC on the last TD */
+ td->status |= (1 << 24);
+ uhci_add_irq_list(dev->uhci, td, completed, dev_id);
+
+ return isodesc;
+}
+
+void uhci_delete_isochronous(struct usb_device *usb_dev, void *_isodesc)
+{
+ struct uhci_iso_td *isodesc = (struct uhci_iso_td *)_isodesc;
+
+ /* If it's still scheduled, unschedule them */
+ if (isodesc->frame)
+ uhci_unsched_isochronous(usb_dev, isodesc);
+
+ /* Remove it from the IRQ list */
+ uhci_remove_irq_list(&isodesc->td[isodesc->num - 1]);
+
+ kfree(isodesc->td);
+ kfree(isodesc);
+}
+
+/*
* Control thread operations: we just mark the last TD
* in a control thread as an interrupt TD, and wake up
* the front-end on completion.
@@ -312,7 +511,7 @@
* We need to remove the TD from the lists (both interrupt
* list and TD lists) by hand if something bad happens!
*/
-static struct wait_queue *control_wakeup;
+static DECLARE_WAIT_QUEUE_HEAD(control_wakeup);
static int uhci_control_completed(int status, void *buffer, void *dev_id)
{
@@ -323,7 +522,7 @@
/* td points to the last td in the list, which interrupts on completion */
static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last)
{
- struct wait_queue wait = { current, NULL };
+ DECLARE_WAITQUEUE(wait, current);
struct uhci_qh *ctrl_qh = uhci_qh_allocate(dev);
struct uhci_td *curtd;
@@ -420,6 +619,7 @@
td->status = status; /* Try forever */
td->info = destination | (7 << 21); /* 8 bytes of data */
td->buffer = virt_to_bus(cmd);
+ td->first = td;
/*
* If direction is "send", change the frame from SETUP (0x2D)
@@ -450,6 +650,7 @@
td->status = status; /* Status */
td->info = destination | ((pktsze-1) << 21); /* pktsze bytes of data */
td->buffer = virt_to_bus(data);
+ td->first = first;
td->backptr = &prevtd->link;
prevtd = td;
@@ -470,6 +671,7 @@
td->status = status | (1 << 24); /* IOC */
td->info = destination | (0x7ff << 21); /* 0 bytes of data */
td->buffer = 0;
+ td->first = first;
td->backptr = &prevtd->link;
/* Start it up.. */
@@ -554,12 +756,16 @@
for (i = 0; i < UHCI_MAXTD; ++i) {
struct uhci_td *td = dev->td + i;
- /* And remove it from the irq list, if it's active */
- if (td->status & (1 << 23))
- uhci_remove_irq_list(td);
-
- if (td->inuse)
+ if (td->inuse) {
uhci_remove_td(td);
+
+ /* And remove it from the irq list, if it's active */
+ if (td->status & (1 << 23))
+ td->status &= ~(1 << 23);
+#if 0
+ uhci_remove_irq_list(td);
+#endif
+ }
}
/* Remove the td from any queues */
@@ -710,15 +916,18 @@
__list_del(tmp->prev, next);
INIT_LIST_HEAD(tmp);
if (td->completed(td->status, bus_to_virt(td->buffer), td->dev_id)) {
- struct uhci_qh *interrupt_qh = td->qh;
-
list_add(&td->irq_list, &uhci->interrupt_list);
- td->info ^= 1 << 19; /* toggle between data0 and data1 */
- td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24); /* active */
- /* Remove then readd? Is that necessary */
- uhci_remove_td(td);
- uhci_insert_td_in_qh(interrupt_qh, td);
+ if (!(td->status & (1 << 25))) {
+ struct uhci_qh *interrupt_qh = td->qh;
+
+ td->info ^= 1 << 19; /* toggle between data0 and data1 */
+ td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24); /* active */
+
+ /* Remove then readd? Is that necessary */
+ uhci_remove_td(td);
+ uhci_insert_td_in_qh(interrupt_qh, td);
+ }
}
/* If completed wants to not reactivate, then it's */
/* responsible for free'ing the TD's and QH's */
@@ -764,6 +973,9 @@
status = inw(io_addr + USBSTS);
outw(status, io_addr + USBSTS);
+ if ((status & ~0x21) != 0)
+ printk("interrupt: %X\n", status);
+
/* Walk the list of pending TD's to see which ones completed.. */
uhci_interrupt_notify(uhci);
@@ -787,6 +999,7 @@
td->status = (1 << 24); /* interrupt on completion */
td->info = (15 << 21) | 0x7f69; /* (ignored) input packet, 16 bytes, device 127 */
td->buffer = 0;
+ td->first = td;
td->qh = NULL;
uhci->fl->frame[0] = virt_to_bus(td);
@@ -1047,11 +1260,12 @@
}
}
-#if 0
+ {
+ int i;
if(uhci->root_hub)
for(i = 0; i < uhci->root_hub->usb->maxchild; i++)
usb_disconnect(uhci->root_hub->usb->children + i);
-#endif
+ }
cleanup_drivers();
@@ -1195,6 +1409,9 @@
#ifdef CONFIG_USB_AUDIO
usb_audio_init();
#endif
+#ifdef CONFIG_USB_CPIA
+ cpia_init();
+#endif
#ifdef CONFIG_APM
apm_register_callback(&handle_apm_event);
#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)