diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/README.ohci | 39 | ||||
-rw-r--r-- | drivers/usb/acm.c | 1 | ||||
-rw-r--r-- | drivers/usb/audio.c | 1 | ||||
-rw-r--r-- | drivers/usb/cpia.c | 1 | ||||
-rw-r--r-- | drivers/usb/hub.c | 1 | ||||
-rw-r--r-- | drivers/usb/mouse.c | 1 | ||||
-rw-r--r-- | drivers/usb/ohci-debug.c | 24 | ||||
-rw-r--r-- | drivers/usb/ohci.c | 575 | ||||
-rw-r--r-- | drivers/usb/ohci.h | 24 | ||||
-rw-r--r-- | drivers/usb/printer.c | 19 | ||||
-rw-r--r-- | drivers/usb/uhci.c | 15 | ||||
-rw-r--r-- | drivers/usb/usb.c | 6 | ||||
-rw-r--r-- | drivers/usb/usb.h | 10 | ||||
-rw-r--r-- | drivers/usb/usb_scsi.c | 1 | ||||
-rw-r--r-- | drivers/usb/usb_scsi.h | 2 |
15 files changed, 566 insertions, 154 deletions
diff --git a/drivers/usb/README.ohci b/drivers/usb/README.ohci index faa650b8d..921d9a9a3 100644 --- a/drivers/usb/README.ohci +++ b/drivers/usb/README.ohci @@ -1,3 +1,21 @@ +[This is the readme for ohci.c, ohci-debug.c and ohci.h] + +June 23, 1999 00:31:20 PST + +I now have bulk support in a reasonably working state. The only +device I have tested it with at the moment is my Epson Stylus 740 +printer. I can print both small and large files. + +I have included code to support transfers of large amounts of data in +either control or bulk transfers. Check out the build_td_chain() and +add_td_chain_to_ed() functions. + +TODO: + +~ Get Michael Gee's mass storage driver working with my donated + YE-Data floppy drive over OHCI. +~ Drool on the Epson printer because its the new toy around the house. + June 08, 1999 01:23:34 Paul Mackerras went through the OHCI (& USB code) to fix most of the @@ -12,12 +30,6 @@ that occurred after removing a device. TODO: -~ Add the concept of a td_group to lump TDs associated with a single - data transfer request from the higher layers. This will be needed - for bulk and all larger transfers that will span multiple TDs but - which need to allocate/free them as a group as things happen. I - am thinking about create_td_group and free_td_group functions... -~ Add bulk transfer support. ~ Add Isochronous transfer support. These have their own special format TDs to allow for several DMA data pointers. Kinda neat, but likely harder to use through a generic interface in practice. @@ -32,7 +44,7 @@ KNOWN BUGS: called using the "IRQ handle" that should be returned by usb_request_irq(). -May 09, 1999 16:25:58 +May 09, 1999 16:25:58 PST Cool, things are working "well" now. (I'm not getting oops's from the OHCI code anyways.. ;). I can attach a usb hub and mouse in any @@ -43,18 +55,5 @@ acknowledged because /proc/interrupts usb-ohci goes up accordingly with mouse movements/events. That means the TD at least returns some data and requeues itself. -Device attach/detach from the root hub is not working well. Currently -every interrupt checks for root hub status changes and frame number -overflow interrupts are enabled. This means you shouldn't have to -wait more than 32-33 seconds for the change to occur, less if there is -other activity. (due to checking in the WDH caused interrupts) -My OHCI controller [SiS 5598 motherboard] doesn't seem to play well -with the RHSC interrupt so it has been disabled. The ohci_timer -should be polling but it not currently working, I haven't had time to -look into that problem. - -However, when I tried telling X to use /dev/psaux for the mouse my -machine locked up... - - greg@electricrain.com diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c index 10c837d5a..d5acef82f 100644 --- a/drivers/usb/acm.c +++ b/drivers/usb/acm.c @@ -26,7 +26,6 @@ #include <linux/poll.h> #include <linux/init.h> #include <linux/malloc.h> -#include <linux/config.h> #include <linux/module.h> #include <asm/spinlock.h> diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c index 9743ec89e..abbb73c4f 100644 --- a/drivers/usb/audio.c +++ b/drivers/usb/audio.c @@ -3,7 +3,6 @@ #include <linux/string.h> #include <linux/timer.h> #include <linux/sched.h> -#include <linux/config.h> #include <linux/module.h> #include "usb.h" diff --git a/drivers/usb/cpia.c b/drivers/usb/cpia.c index 2402d3425..ba89884a4 100644 --- a/drivers/usb/cpia.c +++ b/drivers/usb/cpia.c @@ -17,7 +17,6 @@ #include <linux/videodev.h> #include <linux/vmalloc.h> #include <linux/wrapper.h> -#include <linux/config.h> #include <linux/module.h> #include <asm/spinlock.h> diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c index 0a1ec1f01..20ff10427 100644 --- a/drivers/usb/hub.c +++ b/drivers/usb/hub.c @@ -10,7 +10,6 @@ #include <linux/list.h> #include <linux/malloc.h> #include <linux/smp_lock.h> -#include <linux/config.h> #include <linux/module.h> #include <asm/spinlock.h> diff --git a/drivers/usb/mouse.c b/drivers/usb/mouse.c index a79c10a07..7ebb03be4 100644 --- a/drivers/usb/mouse.c +++ b/drivers/usb/mouse.c @@ -33,7 +33,6 @@ #include <linux/poll.h> #include <linux/init.h> #include <linux/malloc.h> -#include <linux/config.h> #include <linux/module.h> #include <asm/spinlock.h> diff --git a/drivers/usb/ohci-debug.c b/drivers/usb/ohci-debug.c index 263f32c91..56b9fff0a 100644 --- a/drivers/usb/ohci-debug.c +++ b/drivers/usb/ohci-debug.c @@ -124,7 +124,9 @@ void show_ohci_td(struct ohci_td *td) td_cc_accessed(*td) ? "" : "Not ", td_active(*td) ? "" : "Not "); - printk(KERN_DEBUG " %s\n", td_allocated(*td) ? "Allocated" : "Free"); + printk(KERN_DEBUG " %s%s\n", + td_allocated(*td) ? "Allocated" : "Free", + td_dummy(*td) ? " DUMMY" : ""); printk(KERN_DEBUG " cur_buf = 0x%x\n", le32_to_cpup(&td->cur_buf)); printk(KERN_DEBUG " next_td = 0x%x\n", le32_to_cpup(&td->next_td)); @@ -141,6 +143,26 @@ void show_ohci_td(struct ohci_td *td) } /* show_ohci_td() */ +void show_ohci_td_chain(struct ohci_td *td) +{ + struct ohci_td *cur_td; + if (td == NULL) return; + + printk(KERN_DEBUG "+++ OHCI TD Chain %lx: +++\n", virt_to_bus(td)); + + cur_td = td; + for (;;) { + show_ohci_td(cur_td); + if (!cur_td->next_td) break; + cur_td = bus_to_virt(le32_to_cpup(&cur_td->next_td)); + /* we can't trust -anything- we find inside of a dummy TD */ + if (td_dummy(*cur_td)) break; + } + + printk(KERN_DEBUG "--- End TD Chain %lx: ---\n", virt_to_bus(td)); +} /* show_ohci_td_chain () */ + + void show_ohci_device(struct ohci_device *dev) { int idx; diff --git a/drivers/usb/ohci.c b/drivers/usb/ohci.c index 48191e11b..261306949 100644 --- a/drivers/usb/ohci.c +++ b/drivers/usb/ohci.c @@ -2,19 +2,14 @@ * Open Host Controller Interface driver for USB. * * (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com> + * Significant code from the following individuals has also been used: + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> [ohci-hcd.c] + * (C) Copyright 1999 Linus Torvalds [uhci.c] * * This is the "other" host controller interface for USB. You will * find this on many non-Intel based motherboards, and of course the - * Mac. As Linus hacked his UHCI driver together first, I modeled - * this after his.. (it should be obvious) - * - * From the programming standpoint the OHCI interface seems a little - * prettier and potentially less CPU intensive. This remains to be - * proven. In reality, I don't believe it'll make one darn bit of - * difference. USB v1.1 is a slow bus by today's standards. - * - * OHCI hardware takes care of most of the scheduling of different - * transfer types with the correct prioritization for us. + * Mac. As Linus hacked his UHCI driver together first, I originally + * modeled this after his.. (it should be obvious) * * To get started in USB, I used the "Universal Serial Bus System * Architecture" book by Mindshare, Inc. It was a reasonable introduction @@ -76,6 +71,7 @@ static spinlock_t ohci_edtd_lock = SPIN_LOCK_UNLOCKED; #define FIELDS_OF_TD(t) le32_to_cpup(&t->info), le32_to_cpup(&t->cur_buf), \ le32_to_cpup(&t->next_td), le32_to_cpup(&t->buf_end) +#ifdef OHCI_DEBUG static const char *cc_names[16] = { "no error", "CRC error", @@ -94,6 +90,7 @@ static const char *cc_names[16] = { "not accessed (14)", "not accessed" }; +#endif /* * Add a chain of TDs to the end of the TD list on a given ED. @@ -152,6 +149,36 @@ static struct ohci_td *ohci_add_td_to_ed(struct ohci_td *td, } /* ohci_add_td_to_ed() */ +/* + * Add a whole chain of TDs to an ED using the above function. + * The same restrictions apply. + * + * XXX This function is being removed in the future! XXX + */ +static struct ohci_td *ohci_add_td_chain_to_ed(struct ohci_td *td, struct ohci_ed *ed) +{ + struct ohci_td *cur_td; + if (!td) + return NULL; + + /* Find the last TD in this chain, storing its pointer in cur_td */ + cur_td = td; + for (;;) { + __u32 next_td = cur_td->next_td; + + /* advance to the next td, exit if there isn't one */ + if (!next_td) + break; + cur_td = bus_to_virt(le32_to_cpup(&next_td)); + } + + return td = ohci_add_td_to_ed(td, cur_td, ed); +} /* ohci_add_td_chain_to_ed() */ + + +/* .......... */ + + inline void ohci_start_control(struct ohci *ohci) { /* tell the HC to start processing the control list */ @@ -474,7 +501,7 @@ void ohci_remove_device(struct ohci *ohci, int devnum) } /* - * Remove a TD from the given EDs TD list. + * Remove a TD from the given EDs TD list. The TD is freed as well. */ static void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed) { @@ -484,11 +511,11 @@ static void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed) if ((td == NULL) || (ed == NULL)) return; - spin_lock_irqsave(&ohci_edtd_lock, flags); - if (ed_head_td(ed) == 0) return; + spin_lock_irqsave(&ohci_edtd_lock, flags); + /* set the "skip me bit" in this ED */ ed->status |= cpu_to_le32(OHCI_ED_SKIP); @@ -569,6 +596,10 @@ static struct ohci_td *ohci_get_free_td(struct ohci_device *dev) /* * Get a pointer (virtual) to an available TD from the given device's * pool. Return NULL if none are left. + * + * NOTE: This function does not allocate and attach the dummy_td. + * That is done in ohci_fill_ed(). FIXME: it should probably be moved + * into here. */ static struct ohci_ed *ohci_get_free_ed(struct ohci_device *dev) { @@ -593,6 +624,11 @@ static struct ohci_ed *ohci_get_free_ed(struct ohci_device *dev) } /* ohci_get_free_ed() */ +/* + * Free an OHCI ED and all of the TDs on its list. It is assumed that + * this ED is not active. You should call ohci_wait_for_ed_safe() + * beforehand if you can't guarantee that. + */ void ohci_free_ed(struct ohci_ed *ed) { if (!ed) @@ -692,6 +728,140 @@ struct ohci_ed *ohci_fill_ed(struct ohci_device *dev, struct ohci_ed *ed, } /* ohci_fill_ed() */ +/* + * Create a chain of Normal TDs to be used for a large data transfer + * (bulk or control). + * + * Returns the head TD in the chain. + */ +struct ohci_td *ohci_build_td_chain(struct ohci_device *dev, void *data, unsigned int len, int dir, __u32 toggle, int round, int auto_free, void* dev_id, usb_device_irq handler, __u32 next_td) +{ + struct ohci_td *head, *cur_td; + __u32 bus_data_start, bus_data_end; + unsigned short max_page0_len; + + if (!data || (len == 0)) + return NULL; + + /* Setup the first TD, leaving buf_end = 0 */ + head = ohci_get_free_td(dev); + if (head == NULL) { + printk(KERN_ERR "usb-ohci: out of TDs\n"); + return NULL; + } + + ohci_fill_new_td(head, + td_set_dir_out(dir), + toggle & OHCI_TD_DT, + (round ? OHCI_TD_ROUND : 0), + data, 0, + dev_id, handler); + if (!auto_free) + noauto_free_td(head); + + cur_td = head; + + /* AFICT, that the OHCI controller takes care of the innards of + * bulk & control data transfers by sending zero length + * packets as necessary if the transfer falls on an even packet + * size boundary, we don't need a special TD for that. */ + + while (len > 0) { + bus_data_start = virt_to_bus(data); + bus_data_end = virt_to_bus(data+(len-1)); + + /* check the 4096 byte alignment of the start of the data */ + max_page0_len = 0x1000 - (bus_data_start & 0xfff); + + /* check if the remaining data occupies more than two pages */ + if ((max_page0_len < len) && (len - max_page0_len > 0x1000)) { + struct ohci_td *new_td; + + /* Point this TD to data up through the end of + * the second page */ + cur_td->buf_end = bus_data_start + + (max_page0_len + 0xfff); + + /* adjust the data pointer & remaining length */ + data += (max_page0_len + 0x1000); + len -= (max_page0_len + 0x1000); + + /* TODO lookup effect of rounding bit on + * individual TDs vs. whole TD chain transfers; + * disable cur_td's rounding bit here if needed. */ + + /* mark that this is not the last TD... */ + clear_td_endofchain(cur_td); + + /* allocate another td */ + new_td = ohci_get_free_td(dev); + if (new_td == NULL) { + printk(KERN_ERR "usb-ohci: out of TDs\n"); + /* FIXME: free any allocated TDs */ + return NULL; + } + + ohci_fill_new_td(new_td, + td_set_dir_out(dir), + TOGGLE_AUTO, /* toggle Data0/1 via the ED */ + round ? OHCI_TD_ROUND : 0, + data, 0, + dev_id, handler); + if (!auto_free) + noauto_free_td(new_td); + + /* Link the new TD to the chain & advance */ + cur_td->next_td = virt_to_bus(new_td); + cur_td = new_td; + } else { + /* Last TD in this chain, normal buf_end is fine */ + cur_td->buf_end = bus_data_end; + + set_td_endofchain(cur_td); + + len = 0; + break; + } + } /* while */ + + /* link the given next_td to the end of this chain */ + cur_td->next_td = next_td; + + return head; +} /* ohci_build_td_chain() */ + + +/* + * Compute the number of bytes that have been transferred on a given + * TD. Do not call this on TDs that are active on the host + * controller. + */ +static __u16 ohci_td_bytes_done(struct ohci_td *td) +{ + __u16 result; + __u32 bus_data_start, bus_data_end; + + bus_data_start = virt_to_bus(td->data); + if (!td->data || !bus_data_start) + return 0; + + /* if cur_buf is 0, all data has been transferred */ + bus_data_end = td->cur_buf ? td->cur_buf : td->buf_end; + + /* is it on the same page? */ + if ((bus_data_start & ~0xfff) == (bus_data_end & ~0xfff)) { + result = bus_data_end - bus_data_start + 1; + } else { + /* compute the amount transferred on the first page */ + result = 0x1000 - (bus_data_start & 0xfff); + /* add the amount done in the second page */ + result += (bus_data_end & 0xfff) + 1; + } + + return result; +} /* ohci_td_bytes_done() */ + + /********************************** * OHCI interrupt list operations * **********************************/ @@ -762,6 +932,10 @@ static int ohci_request_irq(struct usb_device *usb, unsigned int pipe, /* Assimilate the new ED into the collective */ ohci_add_periodic_ed(dev->ohci, interrupt_ed, period); + /* FIXME: return a request handle that can be used by the + * caller to cancel this request. Be sure its guaranteed not + * to be re-used until the caller is guaranteed to know that + * the transfer has ended or been cancelled */ return 0; } /* ohci_request_irq() */ @@ -794,8 +968,7 @@ static int ohci_control_completed(int stats, void *buffer, int len, void *dev_id * Send or receive a control message on a "pipe" * * The cmd parameter is a pointer to the 8 byte setup command to be - * sent. FIXME: This is a devrequest in usb.h. The function - * should be updated to accept a devrequest* instead of void*.. + * sent. * * A control message contains: * - The command itself @@ -811,7 +984,6 @@ static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, struct ohci_ed *control_ed = ohci_get_free_ed(dev); struct ohci_td *setup_td, *data_td, *status_td; DECLARE_WAITQUEUE(wait, current); - unsigned long flags; int completion_status = -1; devrequest our_cmd; @@ -861,57 +1033,17 @@ static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, ohci_fill_new_td(setup_td, OHCI_TD_D_SETUP, TOGGLE_DATA0, OHCI_TD_IOC_OFF, &our_cmd, 8, /* cmd is always 8 bytes long */ - NULL, NULL); + &completion_status, NULL); - /* allocate the next TD */ - data_td = ohci_get_free_td(dev); - if (!data_td) { - printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl data]\n", dev); + /* Allocate a TD for the control xfer status */ + status_td = ohci_get_free_td(dev); + if (!status_td) { + printk("usb-ohci: couldn't get TD for dev %p [cntl status]\n", dev); ohci_free_td(setup_td); ohci_free_ed(control_ed); return -1; } - /* link to the next TD */ - setup_td->next_td = cpu_to_le32(virt_to_bus(data_td)); - - if (len > 0) { - - /* build the Control DATA TD, it starts with a DATA1. */ - ohci_fill_new_td(data_td, td_set_dir_out(usb_pipeout(pipe)), - TOGGLE_DATA1, - OHCI_TD_ROUND | OHCI_TD_IOC_OFF, - data, len, - NULL, NULL); - - /* - * TODO: Normal TDs can transfer up to 8192 bytes on OHCI. - * However, for that to happen, the data must -start- - * on a nice 4kb page. We need to check for data - * sizes > 4096 and, if they cross more than two 4096 - * byte pages of memory one or more additional TDs - * will need to be created. (repeat doing this in a - * loop until all of the DATA is on a TD) - * - * Control transfers are -highly unlikely- to need to - * transfer this much data.. but who knows.. sadistic - * hardware is sure to exist. - */ - - status_td = ohci_get_free_td(dev); /* TODO check for NULL */ - if (!status_td) { - printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl status]\n", dev); - ohci_free_td(setup_td); - ohci_free_td(data_td); - ohci_free_ed(control_ed); - return -1; - } - - data_td->next_td = cpu_to_le32(virt_to_bus(status_td)); - } else { - status_td = data_td; /* no data_td, use it for status */ - } - /* The control status packet always uses a DATA1 * Give "dev_id" the address of completion_status so that the * TDs status can be passed back to us from the IRQ. */ @@ -923,27 +1055,44 @@ static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, &completion_status, ohci_control_completed); status_td->next_td = 0; /* end of TDs */ + /* If there is data to transfer, create the chain of data TDs + * followed by the status TD. */ + if (len > 0) { + data_td = ohci_build_td_chain( dev, data, len, + usb_pipeout(pipe), TOGGLE_DATA1, + 1 /* round */, 1 /* autofree */, + &completion_status, NULL /* no handler here */, + virt_to_bus(status_td) ); + if (!data_td) { + printk(KERN_ERR "usb-ohci: couldn't allocate control data TDs for dev %p\n", dev); + ohci_free_td(setup_td); + ohci_free_td(status_td); + ohci_free_ed(control_ed); + return -1; + } + + /* link the to the data & status TDs */ + setup_td->next_td = virt_to_bus(data_td); + } else { + /* no data TDs, link to the status TD */ + setup_td->next_td = virt_to_bus(status_td); + } + /* - * Add the chain of 2-3 control TDs to the control ED's TD list + * Add the control TDs to the control ED (setup_td is the first) */ - spin_lock_irqsave(&ohci_edtd_lock, flags); - setup_td = ohci_add_td_to_ed(setup_td, status_td, control_ed); - spin_unlock_irqrestore(&ohci_edtd_lock, flags); + setup_td = ohci_add_td_chain_to_ed(setup_td, control_ed); + control_ed->status &= ~OHCI_ED_SKIP; + ohci_unhalt_ed(control_ed); #ifdef OHCI_DEBUG if (MegaDebug) { /* complete transaction debugging output (before) */ printk(KERN_DEBUG " Control ED %lx:\n", virt_to_bus(control_ed)); show_ohci_ed(control_ed); - printk(KERN_DEBUG " Setup TD %lx:\n", virt_to_bus(setup_td)); - show_ohci_td(setup_td); - if (data_td != status_td) { - printk(KERN_DEBUG " Data TD %lx:\n", virt_to_bus(data_td)); - show_ohci_td(data_td); - } - printk(KERN_DEBUG " Status TD %lx:\n", virt_to_bus(status_td)); - show_ohci_td(status_td); - printk(KERN_DEBUG " Controller Status:\n"); + printk(KERN_DEBUG " Control TD chain:\n"); + show_ohci_td_chain(setup_td); + printk(KERN_DEBUG " OHCI Controller Status:\n"); show_ohci_status(dev->ohci); } #endif @@ -966,19 +1115,15 @@ static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, /* complete transaction debugging output (after) */ printk(KERN_DEBUG " *after* Control ED %lx:\n", virt_to_bus(control_ed)); show_ohci_ed(control_ed); - printk(KERN_DEBUG " *after* Setup TD %lx:\n", virt_to_bus(setup_td)); - show_ohci_td(setup_td); - if (data_td != status_td) { - printk(KERN_DEBUG " *after* Data TD %lx:\n", virt_to_bus(data_td)); - show_ohci_td(data_td); - } - printk(KERN_DEBUG " *after* Status TD %lx:\n", virt_to_bus(status_td)); - show_ohci_td(status_td); - printk(KERN_DEBUG " *after* Controller Status:\n"); + printk(KERN_DEBUG " *after* Control TD chain:\n"); + show_ohci_td_chain(setup_td); + printk(KERN_DEBUG " *after* OHCI Controller Status:\n"); show_ohci_status(dev->ohci); } #endif + /* no TD cleanup, the TDs were auto-freed as they finished */ + /* remove the control ED from the HC */ ohci_remove_control_ed(dev->ohci, control_ed); ohci_free_ed(control_ed); /* return it to the pool */ @@ -1008,6 +1153,219 @@ static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, } /* ohci_control_msg() */ +/********************************************************************** + * Bulk transfer processing + **********************************************************************/ + +/* + * Internal state for an ohci_bulk_request + */ +struct ohci_bulk_request_state { + struct usb_device *usb_dev; + unsigned int pipe; /* usb "pipe" */ + void *data; /* ptr to data */ + int length; /* length to transfer */ + int _bytes_done; /* bytes transferred so far */ + unsigned long *bytes_transferred_p; /* where to increment */ + void *dev_id; /* pass to the completion handler */ + usb_device_irq completion; /* completion handler */ +}; + +/* + * this handles the individual TDs of a (possibly) larger bulk + * request. It keeps track of the total bytes transferred, calls the + * final completion handler, etc. + */ +static int ohci_bulk_td_handler(int stats, void *buffer, int len, void *dev_id) +{ + struct ohci_bulk_request_state *req; + + req = (struct ohci_bulk_request_state *) dev_id; + +#ifdef OHCI_DEBUG + printk(KERN_DEBUG "ohci_bulk_td_handler stats %x, buffer %p, len %d, req %p\n", stats, buffer, len, req); +#endif + + /* only count TDs that were completed successfully */ + if (stats == USB_ST_NOERROR) + req->_bytes_done += len; + +#ifdef OHCI_DEBUG + printk(KERN_DEBUG "ohci_bulk_td_handler %d bytes done\n", req->_bytes_done); +#endif + + /* call the real completion handler when done or on an error */ + if ((stats != USB_ST_NOERROR) || + (req->_bytes_done >= req->length && req->completion != NULL)) { + *req->bytes_transferred_p += req->_bytes_done; +#ifdef OHCI_DEBUG + printk(KERN_DEBUG "usb-ohci: bulk request %p ending after %d bytes\n", req, req->_bytes_done); +#endif + req->completion(stats, buffer, req->_bytes_done, req->dev_id); + } + + return 0; /* do not re-queue the TD */ +} /* ohci_bulk_td_handler() */ + + +/* + * Request to send or receive bulk data. The completion() function + * will be called when the transfer has completed or been aborted due + * to an error. + * + * bytes_transferred_p is a pointer to an integer that will be + * -incremented- by the number of bytes that have been successfully + * transferred. The interrupt handler will update it after each + * internal TD completes successfully. + * + * This function can NOT be called from an interrupt (?) + * (TODO: verify & fix this if needed). + * + * Returns: a pointer to the ED being used for this request. At the + * moment, removing & freeing it is the responsibilty of the caller. + */ +static struct ohci_ed* ohci_request_bulk(struct ohci_bulk_request_state *bulk_request) +{ + /* local names for the readonly fields */ + struct usb_device *usb_dev = bulk_request->usb_dev; + unsigned int pipe = bulk_request->pipe; + void *data = bulk_request->data; + int len = bulk_request->length; + + struct ohci_device *dev = usb_to_ohci(usb_dev); + struct ohci_ed *bulk_ed; + struct ohci_td *head_td; + unsigned long flags; + +#ifdef OHCI_DEBUG + printk(KERN_DEBUG "ohci_request_bulk(%p) ohci_dev %p, completion %p, pipe %x, data %p, len %d\n", bulk_request, dev, bulk_request->completion, pipe, data, len); +#endif + + bulk_ed = ohci_get_free_ed(dev); + if (!bulk_ed) { + printk("usb-ohci: couldn't get ED for dev %p\n", dev); + return NULL; + } + + /* allocate & fill in the TDs for this request */ + head_td = ohci_build_td_chain(dev, data, len, usb_pipeout(pipe), + TOGGLE_AUTO, + 0 /* round not required */, 1 /* autofree */, + bulk_request, /* dev_id: the bulk_request */ + ohci_bulk_td_handler, + 0 /* no additional TDs */); + if (!head_td) { + printk("usb-ohci: couldn't get TDs for dev %p\n", dev); + ohci_free_ed(bulk_ed); + return NULL; + } + + /* Set the max packet size, device speed, endpoint number, usb + * device number (function address), and type of TD. */ + ohci_fill_ed(dev, bulk_ed, + usb_maxpacket(usb_dev, pipe), + usb_pipeslow(pipe), + usb_pipe_endpdev(pipe), 0 /* bulk uses normal TDs */); + + /* initialize the internal counter */ + bulk_request->_bytes_done = 0; + + /* + * Add the TDs to the ED + */ + spin_lock_irqsave(&ohci_edtd_lock, flags); + bulk_ed->status |= OHCI_ED_SKIP; + head_td = ohci_add_td_chain_to_ed(head_td, bulk_ed); + bulk_ed->status &= ~OHCI_ED_SKIP; + ohci_unhalt_ed(bulk_ed); + spin_unlock_irqrestore(&ohci_edtd_lock, flags); + + +#ifdef OHCI_DEBUG +/* if (MegaDebug) { */ + /* complete transaction debugging output (before) */ + printk(KERN_DEBUG " Bulk ED %lx:\n", virt_to_bus(bulk_ed)); + show_ohci_ed(bulk_ed); + printk(KERN_DEBUG " Bulk TDs %lx:\n", virt_to_bus(head_td)); + show_ohci_td_chain(head_td); +/* } */ +#endif + + /* Give the ED to the HC */ + ohci_add_bulk_ed(dev->ohci, bulk_ed); + + return bulk_ed; +} /* ohci_request_bulk() */ + + +static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup); + + +static int ohci_bulk_msg_completed(int stats, void *buffer, int len, void *dev_id) +{ + if (dev_id != NULL) { + int *completion_status = (int *)dev_id; + *completion_status = stats; + } + + wake_up(&bulk_wakeup); + return 0; /* don't requeue the TD */ +} /* ohci_bulk_msg_completed() */ + + +static int ohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *bytes_transferred_p) +{ + DECLARE_WAITQUEUE(wait, current); + int completion_status = USB_ST_INTERNALERROR; + struct ohci_bulk_request_state req; + struct ohci_ed *req_ed; + + /* ....... */ + +#ifdef OHCI_DEBUG + printk(KERN_DEBUG "ohci_bulk_msg %p pipe %x, data %p, len %d, bytes_transferred %p\n", usb_dev, pipe, data, len, bytes_transferred_p); +#endif + + req.usb_dev = usb_dev; + req.pipe = pipe; + req.data = data; + req.length = len; + req.bytes_transferred_p = bytes_transferred_p; + req.dev_id = &completion_status; + req.completion = ohci_bulk_msg_completed; + + /* + * Start the transaction.. + */ + current->state = TASK_UNINTERRUPTIBLE; + add_wait_queue(&bulk_wakeup, &wait); + + req_ed = ohci_request_bulk(&req); + + /* FIXME this should to wait for a caller specified time... */ + schedule_timeout(HZ*5); + +#ifdef OHCI_DEBUG + printk(KERN_DEBUG "ohci_bulk_msg request completed or timed out w/ status %x\n", completion_status); +#endif + + remove_wait_queue(&bulk_wakeup, &wait); + + /* remove the ED from the HC */ + ohci_remove_bulk_ed(usb_to_ohci(usb_dev)->ohci, req_ed); + ohci_free_ed(req_ed); /* return it to the pool */ + +#ifdef OHCI_DEBUG + printk(KERN_DEBUG "ohci_bulk_msg done.\n"); +#endif + + return completion_status; +} /* ohci_bulk_msg() */ + + +/* .......... */ + + /* * Allocate a new USB device to be attached to an OHCI controller */ @@ -1079,8 +1437,6 @@ static int ohci_usb_deallocate(struct usb_device *usb_dev) return 0; } -/* FIXME! */ -#define ohci_bulk_msg NULL /* * functions for the generic USB driver @@ -1404,6 +1760,7 @@ static void ohci_reap_donelist(struct ohci *ohci) { struct ohci_td *td; /* used for walking the list */ + /* um... isn't this dangerous to do in an interrupt handler? -greg */ spin_lock(&ohci_edtd_lock); /* create the FIFO ordered donelist */ @@ -1416,18 +1773,12 @@ static void ohci_reap_donelist(struct ohci *ohci) if (td_dummy(*td)) printk(KERN_ERR "yikes! reaping a dummy TD\n"); - /* FIXME: munge td->info into a future standard status format */ - - if (cc != 0 && ohci_ed_halted(td->ed) && td->completed == 0) { + if (cc != 0 && ohci_ed_halted(td->ed) && !td_endofchain(*td)) { /* * There was an error on this TD and the ED * is halted, and this was not the last TD * of the transaction, so there will be TDs * to clean off the ED. - * (We assume that a TD with a non-NULL completed - * field is the last one of a transaction. - * Ultimately we should have a flag in the TD - * to say that it is the last one.) */ struct ohci_ed *ed = td->ed; struct ohci_td *tail_td = bus_to_virt(ed_tail_td(ed)); @@ -1437,17 +1788,27 @@ static void ohci_reap_donelist(struct ohci *ohci) td = ntd = bus_to_virt(ed_head_td(ed)); while (td != tail_td) { ntd = bus_to_virt(le32_to_cpup(&td->next_td)); - if (td->completed != 0) - break; - ohci_free_td(td); + + /* only deal with TDs from this ED, + * the host controller could have + * processed other endpoints at the + * same time as this one.. */ + if (td->ed == ed) { + if (td_endofchain(*td)) + break; + + /* FIXME: unlink this TD from the + * reverse donelist! */ + ohci_free_td(td); + } + td = ntd; } /* Set the ED head past the ones we cleaned off, and clear the halted flag */ set_ed_head_td(ed, virt_to_bus(ntd)); ohci_unhalt_ed(ed); - /* If we didn't find a TD with a completion - routine, give up */ + /* If we didn't find an endofchain TD, give up */ if (td == tail_td) { td = next_td; continue; @@ -1456,7 +1817,7 @@ static void ohci_reap_donelist(struct ohci *ohci) /* Check if TD should be re-queued */ if ((td->completed != NULL) && - (td->completed(cc, td->data, -1 /* XXX */, td->dev_id))) { + (td->completed(cc, td->data, ohci_td_bytes_done(td), td->dev_id))) { /* Mark the TD as active again: * Set the not accessed condition code * Reset the Error count @@ -1473,7 +1834,8 @@ static void ohci_reap_donelist(struct ohci *ohci) ohci_add_td_to_ed(td, td, td->ed); } else { /* return it to the pool of free TDs */ - ohci_free_td(td); + if (can_auto_free(*td)) + ohci_free_td(td); } td = next_td; @@ -1516,6 +1878,13 @@ static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r) /* Disable HC interrupts */ /* why? - paulus */ writel(OHCI_INTR_MIE, ®s->intrdisable); +#if 0 + /* Only do this for SERIOUS debugging, be sure kern.debug logs + * are not going to the console as this can cause your + * machine to lock up if so... -greg */ + show_ohci_status(ohci); +#endif + /* Process the done list */ if (context & OHCI_INTR_WDH) { /* See which TD's completed.. */ @@ -1856,9 +2225,11 @@ static int ohci_control_thread(void * __ohci) show_ohci_status(ohci); } else if (signr == SIGUSR2) { /* toggle mega TD/ED debugging output */ +#ifdef OHCI_DEBUG MegaDebug = !MegaDebug; printk(KERN_DEBUG "usb-ohci: Mega debugging %sabled.\n", MegaDebug ? "en" : "dis"); +#endif } else { /* unknown signal, exit the thread */ break; @@ -2084,9 +2455,6 @@ int ohci_init(void) } /* ohci_init */ -/* vim:sw=8 - */ - #ifdef MODULE /* * Clean up when unloading the module @@ -2103,4 +2471,5 @@ int init_module(void){ } #endif //MODULE - +/* vim:sw=8 + */ diff --git a/drivers/usb/ohci.h b/drivers/usb/ohci.h index 2fdc3583d..88b08750f 100644 --- a/drivers/usb/ohci.h +++ b/drivers/usb/ohci.h @@ -38,10 +38,12 @@ struct ohci_td { void *data; /* virt. address of the the buffer */ usb_device_irq completed; /* Completion handler routine */ int hcd_flags; /* Flags for the HCD: */ - /* bit0 = boolean: Is this TD allocated? */ - /* bit1 = boolean: Is this a dummy (end of list) TD? */ + /* bit0: Is this TD allocated? */ + /* bit1: Is this a dummy (end of list) TD? */ + /* bit2: do NOT automatically free this TD on completion */ + /* bit3: this is NOT the last TD in a contiguious TD chain + * on the indicated ED. (0 means it is the last) */ - /* User or Device class driver specific fields */ void *dev_id; /* user defined pointer passed to irq handler */ } __attribute((aligned(16))); @@ -54,6 +56,7 @@ struct ohci_td { #define td_set_dir_out(d) ((d) ? OHCI_TD_D_OUT : OHCI_TD_D_IN ) #define OHCI_TD_IOC_DELAY (7 << 21) /* frame delay allowed before int. */ #define OHCI_TD_IOC_OFF (OHCI_TD_IOC_DELAY) /* no interrupt on complete */ +#define td_set_ioc_delay(frames) (((frames) & 7) << 21) #define OHCI_TD_DT (3 << 24) /* data toggle bits */ #define TOGGLE_AUTO (0 << 24) /* automatic (from the ED) */ #define TOGGLE_DATA0 (2 << 24) /* force Data0 */ @@ -82,6 +85,19 @@ struct ohci_td { #define make_dumb_td(td) ((td)->hcd_flags |= 2) #define clear_dumb_td(td) ((td)->hcd_flags &= ~(__u32)2) +#define td_endofchain(td) (!((td).hcd_flags & (1 << 3))) +#define set_td_endofchain(td) ((td)->hcd_flags &= ~(1 << 3)) +#define clear_td_endofchain(td) ((td)->hcd_flags |= (1 << 3)) + +/* + * These control if the IRQ will call ohci_free_td after taking the TDs + * off of the donelist (assuming the completion function does not ask + * for the TD to be requeued). + */ +#define can_auto_free(td) (!((td).hcd_flags & 4)) +#define noauto_free_td(td) ((td)->hcd_flags |= 4) +#define auto_free_td(td) ((td)->hcd_flags &= ~(__u32)4) + /* * The endpoint descriptors also requires 16-byte alignment @@ -369,6 +385,7 @@ struct ohci { int irq; struct ohci_regs *regs; /* OHCI controller's memory */ struct usb_bus *bus; + struct ohci_device *root_hub; /* Root hub & controller */ struct list_head interrupt_list; /* List of interrupt active TDs for this OHCI */ }; @@ -380,6 +397,7 @@ struct ohci { /* Debugging code [ohci-debug.c] */ void show_ohci_ed(struct ohci_ed *ed); void show_ohci_td(struct ohci_td *td); +void show_ohci_td_chain(struct ohci_td *td); void show_ohci_status(struct ohci *ohci); void show_ohci_device(struct ohci_device *dev); void show_ohci_hcca(struct ohci_hcca *hcca); diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c index 88723c6ff..157d58898 100644 --- a/drivers/usb/printer.c +++ b/drivers/usb/printer.c @@ -25,7 +25,7 @@ #define MAX_RETRY_COUNT ((60*60*HZ)/NAK_TIMEOUT) /* should not take 1 minute a page! */ #ifndef USB_PRINTER_MAJOR -#define USB_PRINTER_MAJOR 0 +#define USB_PRINTER_MAJOR 63 #endif static int mymajor = USB_PRINTER_MAJOR; @@ -166,6 +166,7 @@ static ssize_t write_printer(struct file * file, do { char *obuf = p->obuf; unsigned long thistime; + partial = 0; thistime = copy_size = (count > p->maxout) ? p->maxout : count; if (copy_from_user(p->obuf, buffer, copy_size)) @@ -179,16 +180,19 @@ static ssize_t write_printer(struct file * file, } result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev, usb_sndbulkpipe(p->pusb_dev, 1), obuf, thistime, &partial); + if (partial) { + obuf += partial; + thistime -= partial; + maxretry = MAX_RETRY_COUNT; + } if (result == USB_ST_TIMEOUT) { /* NAK - so hold for a while */ if(!maxretry--) return -ETIME; interruptible_sleep_on_timeout(&p->wait_q, NAK_TIMEOUT); continue; - } else if (!result & partial) { - obuf += partial; - thistime -= partial; - } else + } else if (!result && !partial) { break; + } }; if (result) { /* whoops - let's reset and fail the request */ @@ -255,7 +259,8 @@ static int printer_probe(struct usb_device *dev) /* * FIXME - this will not cope with combined printer/scanners */ - if (dev->descriptor.bDeviceClass != 7 || + if ((dev->descriptor.bDeviceClass != 7 && + dev->descriptor.bDeviceClass != 0) || dev->descriptor.bNumConfigurations != 1 || dev->config[0].bNumInterfaces != 1) { return -1; @@ -408,6 +413,6 @@ void cleanup_module(void) unsigned int offset; usb_deregister(&printer_driver); - unregister_chrdev(mymajor, "usblplp"); + unregister_chrdev(mymajor, "usblp"); } #endif diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index c03ce5adf..48233703c 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -20,6 +20,7 @@ /* 4/4/1999 added data toggle for interrupt pipes -keryan */ /* 5/16/1999 added global toggles for bulk and control */ +/* 6/25/1999 added fix for data toggles on bidirectional bulk endpoints */ #include <linux/config.h> #include <linux/module.h> @@ -105,7 +106,7 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned if (uhci_debug) { printk("Set toggle from %x rval %d\n", (unsigned int)tmp, rval ? *rval : 0); } - usb_settoggle(dev->usb, usb_pipeendpoint(tmp->info), (tmp->info >> 19) & 1); + usb_settoggle(dev->usb, usb_pipeendpoint(tmp->info), usb_pipeout(tmp->info), (tmp->info >> 19) & 1); break; } else { if(rval) @@ -372,7 +373,7 @@ static int uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_d td->link = 1; td->status = status; /* In */ - td->info = destination | (7 << 21) | (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe)) << 19); /* 8 bytes of data */ + td->info = destination | (7 << 21) | (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << 19); /* 8 bytes of data */ td->buffer = virt_to_bus(dev->data); td->first = td; td->qh = interrupt_qh; @@ -991,7 +992,7 @@ static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da td->status = status; /* Status */ td->info = destination | ((pktsze-1) << 21) | - (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe)) << 19); /* pktsze bytes of data */ + (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << 19); /* pktsze bytes of data */ td->buffer = virt_to_bus(data); td->backptr = &prevtd->link; td->first = first; @@ -1006,7 +1007,7 @@ static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da } /* Alternate Data0/1 (start with Data0) */ - usb_dotoggle(usb_dev, usb_pipeendpoint(pipe)); + usb_dotoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); } td->link = 1; /* Terminate */ td->status |= (1 << 24); /* IOC */ @@ -1267,9 +1268,9 @@ static void uhci_interrupt_notify(struct uhci *uhci) if (!(td->status & (1 << 25))) { struct uhci_qh *interrupt_qh = td->qh; - usb_dotoggle(td->dev, usb_pipeendpoint(td->info)); + usb_dotoggle(td->dev, usb_pipeendpoint(td->info), usb_pipeout(td->info)); td->info &= ~(1 << 19); /* clear data toggle */ - td->info |= usb_gettoggle(td->dev, usb_pipeendpoint(td->info)) << 19; /* toggle between data0 and data1 */ + td->info |= usb_gettoggle(td->dev, usb_pipeendpoint(td->info), usb_pipeout(td->info)) << 19; /* toggle between data0 and data1 */ td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24); /* active */ /* Remove then readd? Is that necessary */ @@ -1280,7 +1281,7 @@ static void uhci_interrupt_notify(struct uhci *uhci) struct uhci_qh *interrupt_qh = td->qh; /* marked for removal */ td->inuse &= ~2; - usb_dotoggle(td->dev, usb_pipeendpoint(td->info)); + usb_dotoggle(td->dev, usb_pipeendpoint(td->info), usb_pipeout(td->info)); uhci_remove_qh(interrupt_qh->skel, interrupt_qh); uhci_qh_deallocate(interrupt_qh); uhci_td_deallocate(td); diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index b291c04c6..bbce88b64 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -36,7 +36,6 @@ * 6 wLength 2 Count Bytes for data */ -#include <linux/config.h> #include <linux/string.h> #include <linux/bitops.h> #include <linux/malloc.h> @@ -773,7 +772,7 @@ int usb_clear_halt(struct usb_device *dev, int endp) /* toggle is reset on clear */ - usb_settoggle(dev, endp & 0x0f, 0); + usb_settoggle(dev, endp & 0x0f, ((endp >> 7) & 1) ^ 1, 0); return 0; } @@ -823,7 +822,8 @@ int usb_set_configuration(struct usb_device *dev, int configuration) return -1; dev->actconfig = cp; - dev->toggle = 0; + dev->toggle[0] = 0; + dev->toggle[1] = 0; usb_set_maxpacket(dev); return 0; } diff --git a/drivers/usb/usb.h b/drivers/usb/usb.h index a6bf78e4a..5c36dad34 100644 --- a/drivers/usb/usb.h +++ b/drivers/usb/usb.h @@ -276,7 +276,7 @@ struct usb_device { int devnum; /* Device number on USB bus */ int slow; /* Slow device? */ int maxpacketsize; /* Maximum packet size */ - int toggle; /* one bit for each endpoint */ + int toggle[2]; /* one bit for each endpoint ([0] = IN, [1] = OUT) */ int halted; /* endpoint halts */ struct usb_config_descriptor *actconfig;/* the active configuration */ int epmaxpacket[16]; /* endpoint specific maximums */ @@ -362,7 +362,7 @@ extern void usb_destroy_configuration(struct usb_device *dev); #define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f) #define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf) #define usb_pipedata(pipe) (((pipe) >> 19) & 1) -#define usb_pipeout(pipe) (((pipe) & 0x80) == 0) +#define usb_pipeout(pipe) ((((pipe) >> 7) & 1) ^ 1) #define usb_pipeslow(pipe) (((pipe) >> 26) & 1) #define usb_pipetype(pipe) (((pipe) >> 30) & 3) @@ -374,9 +374,9 @@ extern void usb_destroy_configuration(struct usb_device *dev); #define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff) /* The D0/D1 toggle bits */ -#define usb_gettoggle(dev, ep) (((dev)->toggle >> ep) & 1) -#define usb_dotoggle(dev, ep) ((dev)->toggle ^= (1 << ep)) -#define usb_settoggle(dev, ep, bit) ((dev)->toggle = ((dev)->toggle & ~(1 << ep)) | ((bit) << ep)) +#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> ep) & 1) +#define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << ep)) +#define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << ep)) | ((bit) << ep)) /* Endpoint halt */ #define usb_endpoint_halt(dev, ep) ((dev)->halted |= (1 << (ep))) diff --git a/drivers/usb/usb_scsi.c b/drivers/usb/usb_scsi.c index 1a3e16b25..5701d2671 100644 --- a/drivers/usb/usb_scsi.c +++ b/drivers/usb/usb_scsi.c @@ -25,6 +25,7 @@ * */ +#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> diff --git a/drivers/usb/usb_scsi.h b/drivers/usb/usb_scsi.h index 58367d852..3e6386f4f 100644 --- a/drivers/usb/usb_scsi.h +++ b/drivers/usb/usb_scsi.h @@ -11,6 +11,8 @@ * */ +#include <linux/config.h> + #define USB_SCSI "usbscsi: " extern int usbscsi_debug; |