patch-2.3.2 linux/drivers/usb/uhci.c
Next file: linux/drivers/usb/usb.c
Previous file: linux/drivers/usb/ohci.h
Back to the patch index
Back to the overall index
- Lines: 238
- Date:
Fri May 14 18:36:17 1999
- Orig file:
v2.3.1/linux/drivers/usb/uhci.c
- Orig date:
Fri May 14 18:55:23 1999
diff -u --recursive --new-file v2.3.1/linux/drivers/usb/uhci.c linux/drivers/usb/uhci.c
@@ -418,8 +418,8 @@
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;
+ int i;
isodesc = kmalloc(sizeof(*isodesc), GFP_KERNEL);
if (!isodesc) {
@@ -700,6 +700,186 @@
return ret;
}
+
+/*
+ * Bulk thread operations: we just mark the last TD
+ * in a bulk thread as an interrupt TD, and wake up
+ * the front-end on completion.
+ *
+ * We need to remove the TD from the lists (both interrupt
+ * list and TD lists) by hand if something bad happens!
+ */
+static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup);
+
+static int uhci_bulk_completed(int status, void *buffer, void *dev_id)
+{
+ wake_up(&bulk_wakeup);
+ return 0; /* Don't re-instate */
+}
+
+/* td points to the last td in the list, which interrupts on completion */
+static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct uhci_qh *bulk_qh = uhci_qh_allocate(dev);
+ struct uhci_td *curtd;
+
+
+
+ current->state = TASK_UNINTERRUPTIBLE;
+ add_wait_queue(&bulk_wakeup, &wait);
+
+ uhci_add_irq_list(dev->uhci, last, uhci_bulk_completed, NULL);
+
+ /* FIXME: This is kinda kludged */
+ /* Walk the TD list and update the QH pointer */
+ {
+ int maxcount = 100;
+
+ curtd = first;
+ do {
+ curtd->qh = bulk_qh;
+ if (curtd->link & 1)
+ break;
+
+ curtd = bus_to_virt(curtd->link & ~0xF);
+ if (!--maxcount) {
+ printk("runaway tds!\n");
+ break;
+ }
+ } while (1);
+ }
+
+ uhci_insert_tds_in_qh(bulk_qh, first, last);
+
+// bulk0 is empty here...
+// show_status(dev->uhci);
+// show_queues(dev->uhci);
+
+ /* Add it into the skeleton */
+ /*WARNING! HUB HAS NO BULK QH TIL NOW!!!!!!!!!!!*/
+ uhci_insert_qh(&dev->uhci->root_hub->skel_bulk0_qh, bulk_qh);
+
+// now we're in the queue... but don't ask WHAT is in there ;-(
+// show_status(dev->uhci);
+// show_queues(dev->uhci);
+
+ schedule_timeout(HZ/10);
+
+ remove_wait_queue(&bulk_wakeup, &wait);
+
+ /* Clean up in case it failed.. */
+ uhci_remove_irq_list(last);
+
+#if 0
+ printk("Looking for tds [%p, %p]\n", dev->control_td, td);
+#endif
+
+ /* Remove it from the skeleton */
+ uhci_remove_qh(&dev->uhci->root_hub->skel_bulk0_qh, bulk_qh);
+
+ uhci_qh_deallocate(bulk_qh);
+
+ return uhci_td_result(dev, last);
+}
+
+/*
+ * Send or receive a bulk message on a pipe.
+ *
+ * Note that the "pipe" structure is set up to map
+ * easily to the uhci destination fields.
+ *
+ * A bulk message is only built up from
+ * the data phase
+ *
+ * The data phase can be an arbitrary number of TD's
+ * although we currently had better not have more than
+ * 31 TD's here.
+ *
+ * 31 TD's is a minimum of 248 bytes worth of bulk
+ * information.
+ */
+static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len)
+{
+ struct uhci_device *dev = usb_to_uhci(usb_dev);
+ struct uhci_td *first, *td, *prevtd;
+ unsigned long destination, status;
+ int ret;
+
+ if (len > usb_maxpacket(usb_dev->maxpacketsize) * 31)
+ printk("Warning, too much data for a bulk packet, crashing\n");
+
+ /* The "pipe" thing contains the destination in bits 8--18, 0x69 is IN */
+ /*
+ IS THIS NECCESARY? PERHAPS WE CAN JUST USE THE PIPE
+ LOOK AT: usb_pipeout and the pipe bits
+ I FORGOT WHAT IT EXACTLY DOES
+ */
+ if (usb_pipeout(pipe)) {
+ destination = (pipe & 0x0007ff00) | 0xE1;
+ }
+ else {
+ destination = (pipe & 0x0007ff00) | 0x69;
+ }
+
+ /* Status: slow/fast, Active, Short Packet Detect Three Errors */
+ status = (pipe & (1 << 26)) | (1 << 23) | (1 << 29) | (3 << 27);
+
+ /*
+ * Build the TDs for the bulk request
+ */
+ first = td = uhci_td_allocate(dev);
+ prevtd = first; //This is fake, but at least it's not NULL
+ while (len > 0) {
+ /* Build the TD for control status */
+ int pktsze = len;
+ int maxsze = usb_maxpacket(pipe);
+
+ if (pktsze > maxsze)
+ pktsze = maxsze;
+
+ td->status = status; /* Status */
+ td->info = destination | ((pktsze-1) << 21); /* pktsze bytes of data */
+ td->buffer = virt_to_bus(data);
+ td->backptr = &prevtd->link;
+
+ prevtd = td;
+ td = uhci_td_allocate(dev);
+ prevtd->link = 4 | virt_to_bus(td); /* Update previous TD */
+
+ data += maxsze;
+ len -= maxsze;
+ /* Alternate Data0/1 (start with Data0) */
+ destination ^= 1 << 19;
+ }
+ td->link = 1; /* Terminate */
+
+ /* Start it up.. */
+ ret = uhci_run_bulk(dev, first, td);
+
+ {
+ int maxcount = 100;
+ struct uhci_td *curtd = first;
+ unsigned int nextlink;
+
+ do {
+ nextlink = curtd->link;
+ uhci_remove_td(curtd);
+ uhci_td_deallocate(curtd);
+ if (nextlink & 1) /* Tail? */
+ break;
+
+ curtd = bus_to_virt(nextlink & ~0xF);
+ if (!--maxcount) {
+ printk("runaway td's!?\n");
+ break;
+ }
+ } while (1);
+ }
+
+ return ret;
+}
+
static struct usb_device *uhci_usb_allocate(struct usb_device *parent)
{
struct usb_device *usb_dev;
@@ -787,6 +967,7 @@
uhci_usb_allocate,
uhci_usb_deallocate,
uhci_control_msg,
+ uhci_bulk_msg,
uhci_request_irq,
};
@@ -1399,32 +1580,10 @@
if (retval < 0)
continue;
-#ifdef CONFIG_USB_MOUSE
- usb_mouse_init();
-#endif
-#ifdef CONFIG_USB_KBD
- usb_kbd_init();
-#endif
- hub_init();
-#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
-
return 0;
}
return retval;
-}
-
-void cleanup_drivers(void)
-{
- hub_cleanup();
-#ifdef CONFIG_USB_MOUSE
- usb_mouse_cleanup();
-#endif
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)