From 16b5d462f73eb29d1f67fa01cc1ea66afdc72569 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 23 Mar 2000 02:25:38 +0000 Subject: Merge with Linux 2.3.99-pre2. --- drivers/usb/Config.in | 23 +- drivers/usb/Makefile | 2 +- drivers/usb/dc2xx.c | 23 +- drivers/usb/drivers.c | 6 +- drivers/usb/dsbr100.c | 6 - drivers/usb/hub.c | 104 ++++---- drivers/usb/hub.h | 13 +- drivers/usb/joydev.c | 4 +- drivers/usb/serial/Makefile | 2 +- drivers/usb/serial/usb-serial.c | 37 ++- drivers/usb/serial/usb-serial.h | 1 + drivers/usb/uhci.c | 34 ++- drivers/usb/usb-ohci.c | 25 +- drivers/usb/usb-storage.c | 514 +++++++++++++++++++++++----------------- drivers/usb/usb-uhci.c | 1 + 15 files changed, 455 insertions(+), 340 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index fbb7562dc..012a27187 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -8,7 +8,9 @@ tristate 'Support for USB' CONFIG_USB if [ ! "$CONFIG_USB" = "n" ]; then comment 'USB Controllers' - dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB + if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then + dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB + fi if [ "$CONFIG_USB_UHCI" != "y" ]; then dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB if [ "$CONFIG_USB_UHCI_ALT" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then @@ -34,21 +36,26 @@ comment 'USB Devices' bool ' USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO bool ' USB Keyspan PDA Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN_PDA fi + bool ' USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG fi dep_tristate ' USB CPiA Camera support' CONFIG_USB_CPIA $CONFIG_USB dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB - dep_tristate ' USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB - if [ "$CONFIG_USB_STORAGE" != "n" ]; then - bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' USB Mass Storage support (EXPERIMENTAL)' CONFIG_USB_STORAGE $CONFIG_USB m + if [ "$CONFIG_USB_STORAGE" != "n" ]; then + bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG + fi fi dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB - dep_tristate ' PLUSB Prolific USB-Network driver' CONFIG_USB_PLUSB $CONFIG_USB - dep_tristate ' USB ADMtek Pegasus-based device support' CONFIG_USB_PEGASUS $CONFIG_USB - dep_tristate ' USB Diamond Rio500 support' CONFIG_USB_RIO500 $CONFIG_USB - dep_tristate ' D-Link USB FM radio support' CONFIG_USB_DSBR $CONFIG_USB + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' PLUSB Prolific USB-Network driver (EXPERIMENTAL)' CONFIG_USB_PLUSB $CONFIG_USB + dep_tristate ' USB ADMtek Pegasus-based device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB + dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB + dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB + fi comment 'USB HID' dep_tristate ' USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 35bef0e45..6e9e99714 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -45,7 +45,7 @@ ifeq ($(CONFIG_USB_SERIAL),y) obj-y += serial/serial.o else ifeq ($(CONFIG_USB_SERIAL),m) - MOD_SUB_DIRS += serial + MOD_IN_SUB_DIRS += serial endif endif diff --git a/drivers/usb/dc2xx.c b/drivers/usb/dc2xx.c index 3a2df9155..9dff6bda1 100644 --- a/drivers/usb/dc2xx.c +++ b/drivers/usb/dc2xx.c @@ -25,7 +25,8 @@ * and have fun! * * This should also work for a number of other digital (non-Kodak) cameras, - * by adding the vendor and product IDs to the table below. + * by adding the vendor and product IDs to the table below. They'll need + * to be the sort using USB just as a fast bulk data channel. */ /* @@ -100,7 +101,8 @@ static const struct camera { // { 0x03f0, 0xffff }, // HP PhotoSmart C500 /* Other USB devices may well work here too, so long as they - * just stick to half duplex bulk packet exchanges. + * just stick to half duplex bulk packet exchanges. That + * means, among other things, no iso or interrupt endpoints. */ }; @@ -162,7 +164,7 @@ static ssize_t camera_read (struct file *file, usb_rcvbulkpipe (camera->dev, camera->inEP), camera->buf, len, &count, HZ*10); - dbg ("read (%d) - 0x%x %ld", len, result, count); + dbg ("read (%d) - 0x%x %d", len, result, count); if (!result) { if (copy_to_user (buf, camera->buf, count)) @@ -382,7 +384,7 @@ static void * camera_probe(struct usb_device *dev, unsigned int ifnum) err ("no memory!"); return NULL; } - camera->dev = dev; + camera->info = camera_info; camera->subminor = i; camera->isActive = 0; camera->buf = NULL; @@ -413,19 +415,22 @@ static void * camera_probe(struct usb_device *dev, unsigned int ifnum) || endpoint [1].bmAttributes != USB_ENDPOINT_XFER_BULK ) { dbg ("Bogus endpoints"); - camera->dev = NULL; - return NULL; + goto error; } if (usb_set_configuration (dev, dev->config[0].bConfigurationValue)) { err ("Failed usb_set_configuration"); - camera->dev = NULL; - return NULL; + goto error; } - camera->info = camera_info; + camera->dev = dev; return camera; + +error: + minor_data [camera->subminor] = NULL; + kfree (camera); + return NULL; } static void camera_disconnect(struct usb_device *dev, void *ptr) diff --git a/drivers/usb/drivers.c b/drivers/usb/drivers.c index 7682c7442..0dcc9f719 100644 --- a/drivers/usb/drivers.c +++ b/drivers/usb/drivers.c @@ -69,7 +69,11 @@ static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff pos = *ppos; for (; tmp != &usb_driver_list; tmp = tmp->next) { struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list); - start += sprintf (start, "%s\n", driver->name); + int minor = driver->fops ? driver->minor : -1; + if (minor == -1) + start += sprintf (start, " %s\n", driver->name); + else + start += sprintf (start, "%3d-%3d: %s\n", minor, minor + 15, driver->name); if (start > end) { start += sprintf(start, "(truncated)\n"); break; diff --git a/drivers/usb/dsbr100.c b/drivers/usb/dsbr100.c index 8b81e06c0..22c21bb33 100644 --- a/drivers/usb/dsbr100.c +++ b/drivers/usb/dsbr100.c @@ -48,12 +48,6 @@ #include - -#if CONFIG_MODVERSIONS==1 -#define MODVERSIONS -#include -#endif - #include #include #include diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c index 9138ee4da..8bd919d67 100644 --- a/drivers/usb/hub.c +++ b/drivers/usb/hub.c @@ -4,8 +4,6 @@ * (C) Copyright 1999 Linus Torvalds * (C) Copyright 1999 Johannes Erdfelt * (C) Copyright 1999 Gregory P. Smith - * - * $Id: hub.c,v 1.21 2000/01/16 21:19:44 acher Exp $ */ #include @@ -75,31 +73,29 @@ static int usb_get_port_status(struct usb_device *dev, int port, void *data) * the low-level driver that it wants to be re-activated, * or zero to say "I'm done". */ -static int hub_irq(int status, void *__buffer, int len, void *dev_id) +static void hub_irq(struct urb *urb) { - struct usb_hub *hub = dev_id; + struct usb_hub *hub = (struct usb_hub *)urb->context; unsigned long flags; - switch (status) { - case -ENODEV: - /* Just ignore it */ - break; - case 0: - /* Something happened, let khubd figure it out */ - if (waitqueue_active(&khubd_wait)) { - /* Add the hub to the event queue */ - spin_lock_irqsave(&hub_event_lock, flags); - if (hub->event_list.next == &hub->event_list) { - list_add(&hub->event_list, &hub_event_list); - /* Wake up khubd */ - wake_up(&khubd_wait); - } - spin_unlock_irqrestore(&hub_event_lock, flags); - } - break; + if (urb->status) { + if (urb->status != -ENOENT) + dbg("nonzero status in irq %d", urb->status); + + return; } - return 1; + /* Something happened, let khubd figure it out */ + if (waitqueue_active(&khubd_wait)) { + /* Add the hub to the event queue */ + spin_lock_irqsave(&hub_event_lock, flags); + if (hub->event_list.next == &hub->event_list) { + list_add(&hub->event_list, &hub_event_list); + /* Wake up khubd */ + wake_up(&khubd_wait); + } + spin_unlock_irqrestore(&hub_event_lock, flags); + } } static void usb_hub_power_on(struct usb_hub *hub) @@ -196,13 +192,14 @@ static int usb_hub_configure(struct usb_hub *hub) return 0; } -static void * hub_probe(struct usb_device *dev, unsigned int i) +static void *hub_probe(struct usb_device *dev, unsigned int i) { struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; struct usb_hub *hub; unsigned long flags; - int ret; + unsigned int pipe; + int maxp, ret; interface = &dev->actconfig->interface[i].altsetting[0]; @@ -233,7 +230,8 @@ static void * hub_probe(struct usb_device *dev, unsigned int i) /* We found a hub */ info("USB hub found"); - if ((hub = kmalloc(sizeof(*hub), GFP_KERNEL)) == NULL) { + hub = kmalloc(sizeof(*hub), GFP_KERNEL); + if (!hub) { err("couldn't kmalloc hub struct"); return NULL; } @@ -250,26 +248,24 @@ static void * hub_probe(struct usb_device *dev, unsigned int i) spin_unlock_irqrestore(&hub_event_lock, flags); if (usb_hub_configure(hub) >= 0) { - hub->irqpipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - ret = usb_request_irq(dev, hub->irqpipe, - hub_irq, endpoint->bInterval, - hub, &hub->irq_handle); - if (ret) { - err("usb_request_irq failed (%d)", ret); - /* free hub, but first clean up its list. */ - spin_lock_irqsave(&hub_event_lock, flags); + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - /* Delete it and then reset it */ - list_del(&hub->event_list); - INIT_LIST_HEAD(&hub->event_list); - list_del(&hub->hub_list); - INIT_LIST_HEAD(&hub->hub_list); + if (maxp > sizeof(hub->buffer)) + maxp = sizeof(hub->buffer); - spin_unlock_irqrestore(&hub_event_lock, flags); - - kfree(hub); + hub->urb = usb_alloc_urb(0); + if (!hub->urb) { + err("couldn't allocate interrupt urb"); + goto fail; + } - return NULL; + FILL_INT_URB(hub->urb, dev, pipe, hub->buffer, maxp, hub_irq, + hub, endpoint->bInterval); + ret = usb_submit_urb(hub->urb); + if (ret) { + err("usb_submit_urb failed (%d)", ret); + goto fail; } /* Wake up khubd */ @@ -277,11 +273,27 @@ static void * hub_probe(struct usb_device *dev, unsigned int i) } return hub; + +fail: + /* free hub, but first clean up its list. */ + spin_lock_irqsave(&hub_event_lock, flags); + + /* Delete it and then reset it */ + list_del(&hub->event_list); + INIT_LIST_HEAD(&hub->event_list); + list_del(&hub->hub_list); + INIT_LIST_HEAD(&hub->hub_list); + + spin_unlock_irqrestore(&hub_event_lock, flags); + + kfree(hub); + + return NULL; } static void hub_disconnect(struct usb_device *dev, void *ptr) { - struct usb_hub *hub = ptr; + struct usb_hub *hub = (struct usb_hub *)ptr; unsigned long flags; spin_lock_irqsave(&hub_event_lock, flags); @@ -294,8 +306,10 @@ static void hub_disconnect(struct usb_device *dev, void *ptr) spin_unlock_irqrestore(&hub_event_lock, flags); - if (hub->irq_handle) { - usb_release_irq(hub->dev, hub->irq_handle, hub->irqpipe); + if (hub->urb) { + usb_unlink_urb(hub->urb); + usb_free_urb(hub->urb); + hub->urb = NULL; } /* Free the memory */ diff --git a/drivers/usb/hub.h b/drivers/usb/hub.h index 0da7eb87c..913c44a2d 100644 --- a/drivers/usb/hub.h +++ b/drivers/usb/hub.h @@ -78,16 +78,10 @@ struct usb_hub_descriptor { __u8 bDescriptorType; __u8 bNbrPorts; __u16 wHubCharacteristics; -#if 0 - __u8 wHubCharacteristics[2]; /* __u16 but not aligned! */ -#endif __u8 bPwrOn2PwrGood; __u8 bHubContrCurrent; /* DeviceRemovable and PortPwrCtrlMask want to be variable-length bitmaps that hold max 256 entries, but for now they're ignored */ -#if 0 - __u8 filler; -#endif } __attribute__ ((packed)); struct usb_device; @@ -112,9 +106,10 @@ struct usb_hub { /* Device structure */ struct usb_device *dev; - /* Reference to the hub's polling IRQ and its associated pipe */ - void *irq_handle; - unsigned int irqpipe; + /* Interrupt polling pipe */ + struct urb *urb; + + char buffer[USB_MAXCHILDREN / 8]; /* List of hubs */ struct list_head hub_list; diff --git a/drivers/usb/joydev.c b/drivers/usb/joydev.c index a5dcabdab..9b54300e2 100644 --- a/drivers/usb/joydev.c +++ b/drivers/usb/joydev.c @@ -224,8 +224,8 @@ static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *p struct JS_DATA_TYPE data; - data.buttons = (joydev->nkey > 0 && test_bit(joydev->keypam[0], input->key)) ? 1 : 0 | - (joydev->nkey > 1 && test_bit(joydev->keypam[1], input->key)) ? 2 : 0; + data.buttons = ((joydev->nkey > 0 && test_bit(joydev->keypam[0], input->key)) ? 1 : 0) | + ((joydev->nkey > 1 && test_bit(joydev->keypam[1], input->key)) ? 2 : 0); data.x = ((joydev_correct(input->abs[ABS_X], &joydev->corr[0]) / 256) + 128) >> joydev->glue.JS_CORR.x; data.y = ((joydev_correct(input->abs[ABS_Y], &joydev->corr[1]) / 256) + 128) >> joydev->glue.JS_CORR.y; diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 20dc5dde5..02bd7aad6 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -14,7 +14,7 @@ ALL_SUB_DIRS := $(SUB_DIRS) O_TARGET := serial.o M_OBJS := usb-serial.o O_OBJS := usb-serial.o -#MOD_LIST_NAME := USB_MODULES +MOD_LIST_NAME := USB_SERIAL_MODULES # Objects that export symbols. diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index ad86c6a80..6effcc048 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -14,6 +14,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (03/17/2000) gkh + * Added config option for debugging messages. + * Added patch for keyspan pda from Brian Warner. + * * (03/06/2000) gkh * Added the keyspan pda code from Brian Warner * Moved a bunch of the port specific stuff into its own structure. This @@ -175,7 +179,12 @@ #include #include #include -#define DEBUG + +#ifdef CONFIG_USB_SERIAL_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif #include #ifdef CONFIG_USB_SERIAL_WHITEHEAT @@ -1428,6 +1437,7 @@ static void keyspan_pda_rx_interrupt (struct urb *urb) case 2: /* tx unthrottle interrupt */ serial->tx_throttled = 0; wake_up(&serial->write_wait); /* wake up writer */ + wake_up(&tty->write_wait); /* them too */ break; default: break; @@ -1846,25 +1856,12 @@ static int keyspan_pda_write_room (struct tty_struct *tty) static int keyspan_pda_chars_in_buffer (struct tty_struct *tty) { struct usb_serial *serial = (struct usb_serial *)tty->driver_data; - unsigned char count; - int rc; - - /* used by tty stuff to wait for output to drain. Go ask the - device how much is still queued in the tx ring */ - rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), - 6, /* write_room */ - USB_TYPE_VENDOR | USB_RECIP_INTERFACE - | USB_DIR_IN, - 1, /* value: 1 means chars_in_buffer */ - 0, /* index */ - &count, - 1, - 2*HZ); - if (rc < 0) - return rc; /* failed */ - if (rc == 0) - return -EIO; /* device didn't return any data */ - return (count); + + /* when throttled, return at least WAKEUP_CHARS to tell select() (via + n_tty.c:normal_poll() ) that we're not writeable. */ + if (serial->tx_throttled) + return 256; + return 0; } diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h index f02eeedf5..71fecb0ec 100644 --- a/drivers/usb/serial/usb-serial.h +++ b/drivers/usb/serial/usb-serial.h @@ -371,6 +371,7 @@ static struct usb_serial_device_type keyspan_pda_fake_device = { num_interrupt_in: NUM_DONT_CARE, num_bulk_in: NUM_DONT_CARE, num_bulk_out: NUM_DONT_CARE, + num_ports: 1, startup: keyspan_pda_fake_startup }; static struct usb_serial_device_type keyspan_pda_device = { diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index 480c6252d..f5e67acec 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -689,19 +689,18 @@ status_phase: return 0; td_error: - /* Some debugging code */ - if (debug) { + if (status & TD_CTRL_STALLED) + /* endpoint has stalled - mark it halted */ + usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info)); + else if (debug) { + /* Some debugging code */ dbg("uhci_result_control() failed with status %x", status); /* Print the chain for debugging purposes */ uhci_show_queue(urbp->qh); } - if (status & TD_CTRL_STALLED) - /* endpoint has stalled - mark it halted */ - usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), - uhci_packetout(td->info)); - return uhci_map_status(status, uhci_packetout(td->info)); } @@ -818,8 +817,12 @@ static int uhci_result_interrupt(urb_t *urb) return 0; td_error: - /* Some debugging code */ - if (debug) { + if (status & TD_CTRL_STALLED) + /* endpoint has stalled - mark it halted */ + usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info)); + else if (debug) { + /* Some debugging code */ dbg("uhci_result_interrupt/bulk() failed with status %x", status); @@ -830,11 +833,6 @@ td_error: uhci_show_td(td); } - if (status & TD_CTRL_STALLED) - /* endpoint has stalled - mark it halted */ - usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), - uhci_packetout(td->info)); - return uhci_map_status(status, uhci_packetout(td->info)); } @@ -1251,12 +1249,14 @@ static int uhci_unlink_urb(urb_t *urb) uhci_unlink_generic(urb); if (urb->transfer_flags & USB_ASYNC_UNLINK) { + urb->status = -ECONNABORTED; + spin_lock_irqsave(&uhci->urb_remove_lock, flags); list_add(&urb->urb_list, &uhci->urb_remove_list); spin_unlock_irqrestore(&uhci->urb_remove_lock, flags); - - urb->status = -ECONNABORTED; } else { + urb->status = -ENOENT; + if (in_interrupt()) { /* wait at least 1 frame */ static int errorcount = 10; @@ -1268,8 +1268,6 @@ static int uhci_unlink_urb(urb_t *urb) if (urb->complete) urb->complete(urb); - - urb->status = -ENOENT; } } diff --git a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c index cf457762a..78ae0a0ea 100644 --- a/drivers/usb/usb-ohci.c +++ b/drivers/usb/usb-ohci.c @@ -252,6 +252,9 @@ static int sohci_submit_urb (urb_t * urb) if (urb->hcpriv) return -EINVAL; /* urb already in use */ +// if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) +// return -EPIPE; + usb_inc_dev_use (urb->dev); ohci = (ohci_t *) urb->dev->bus->hcpriv; @@ -838,28 +841,36 @@ static void td_submit_urb (urb_t * urb) int data_len = urb->transfer_buffer_length; int cnt = 0; __u32 info = 0; - + unsigned int toggle = 0; + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ + if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1); + } + urb_priv->td_cnt = 0; switch (usb_pipetype (urb->pipe)) { case PIPE_BULK: info = usb_pipeout (urb->pipe)? - TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_DP_IN | TD_T_TOGGLE; + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; while(data_len > 4096) { - td_fill (info, data, 4096, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); + td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); data += 4096; data_len -= 4096; cnt++; } info = usb_pipeout (urb->pipe)? - TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE; - td_fill (info, data, data_len, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); cnt++; writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ break; case PIPE_INTERRUPT: info = usb_pipeout (urb->pipe)? - TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE; + TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle; td_fill (info, data, data_len, urb, ST_ADDR | ADD_LEN, cnt++); break; @@ -1059,6 +1070,8 @@ static void dl_done_list (ohci_t * ohci, td_t * td_list) } /* error code of transfer */ cc = TD_CC_GET (tdINFO); + if( cc == TD_CC_STALL) usb_endpoint_halt(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN)) cc = TD_CC_NOERROR; if (++(urb_priv->td_cnt) == urb_priv->length) { diff --git a/drivers/usb/usb-storage.c b/drivers/usb/usb-storage.c index 956e80a0a..0cac2ef13 100644 --- a/drivers/usb/usb-storage.c +++ b/drivers/usb/usb-storage.c @@ -52,6 +52,10 @@ /* direction table -- this indicates the direction of the data * transfer for each command code -- a 1 indicates input */ +/* FIXME: we need to use the new direction indicators in the Scsi_Cmnd + * structure, not this table. First we need to evaluate if it's being set + * correctly for us, though + */ unsigned char us_direction[256/8] = { 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, @@ -81,13 +85,12 @@ struct us_data { __u8 ep_int; /* interrupt . */ __u8 subclass; /* as in overview */ __u8 protocol; /* .............. */ - __u8 attention_done; /* force attn on first cmd */ trans_cmnd transport; /* protocol specific do cmd */ trans_reset transport_reset; /* .......... device reset */ proto_cmnd proto_handler; /* protocol handler */ GUID(guid); /* unique dev id */ struct Scsi_Host *host; /* our dummy host data */ - Scsi_Host_Template *htmplt; /* own host template */ + Scsi_Host_Template htmplt; /* own host template */ int host_number; /* to find us */ int host_no; /* allocated by scsi */ Scsi_Cmnd *srb; /* current srb */ @@ -114,7 +117,9 @@ struct us_data { #define US_ACT_BUS_RESET 4 #define US_ACT_HOST_RESET 5 +/* The list of structures and the protective lock for them */ static struct us_data *us_list; +spinlock_t us_list_spinlock = SPIN_LOCK_UNLOCKED; static void * storage_probe(struct usb_device *dev, unsigned int ifnum); static void storage_disconnect(struct usb_device *dev, void *ptr); @@ -225,7 +230,7 @@ static void us_transfer(Scsi_Cmnd *srb, int dir_in) srb->result = result; } -/* calculate the length of the data transfer (not the command) for any +/* Calculate the length of the data transfer (not the command) for any * given SCSI command */ static unsigned int us_transfer_length(Scsi_Cmnd *srb) @@ -243,14 +248,18 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb) case TEST_UNIT_READY: return 0; + /* FIXME: these should be removed and tested */ case REQUEST_SENSE: case INQUIRY: case MODE_SENSE: return srb->cmnd[4]; + /* FIXME: this needs to come out when the other + * fix is in place */ case READ_CAPACITY: return 8; + /* FIXME: these should be removed and tested */ case LOG_SENSE: case MODE_SENSE_10: return (srb->cmnd[7] << 8) + srb->cmnd[8]; @@ -795,7 +804,7 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_ERROR; } - /* FIXME: we need to handle NAKs here */ + /* FIXME: we need to handle NAKs here */ return USB_STOR_TRANSPORT_ERROR; } @@ -812,7 +821,9 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us) /* STATUS STAGE */ /* go to sleep until we get this interrup */ - /* FIXME: this should be changed to use a timeout */ + /* FIXME: this should be changed to use a timeout -- or let the + * device reset routine up() this for us to unjam us + */ down(&(us->ip_waitq)); /* FIXME: currently this code is unreachable, but the idea is @@ -827,8 +838,8 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us) US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data); /* UFI gives us ASC and ASCQ, like a request sense */ - /* FIXME: is this right? Do REQUEST_SENSE and INQUIRY need special - * case handling? + /* REQUEST_SENSE and INQUIRY don't affect the sense data, so we + * ignore the information for those commands */ if (us->subclass == US_SC_UFI) { if (srb->cmnd[0] == REQUEST_SENSE || @@ -887,7 +898,7 @@ static int CB_transport(Scsi_Cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_ERROR; } - /* FIXME: we need to handle NAKs here */ + /* FIXME: we need to handle NAKs here */ return USB_STOR_TRANSPORT_ERROR; } @@ -1103,7 +1114,8 @@ static int us_detect(struct SHT *sht) static int us_release(struct Scsi_Host *psh) { struct us_data *us = (struct us_data *)psh->hostdata[0]; - struct us_data *prev = (struct us_data *)&us_list; + struct us_data *prev; + unsigned long flags; if (us->irq_handle) { usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe); @@ -1111,18 +1123,20 @@ static int us_release(struct Scsi_Host *psh) } /* FIXME: release the interface claim here? */ - // if (us->pusb_dev) - // usb_deregister(&storage_driver); - /* FIXME - leaves hanging host template copy */ - /* (because scsi layer uses it after removal !!!) */ + /* FIXME: we need to move this elsewhere -- + * the remove function only gets called to remove the module + */ + spin_lock_irqsave(&us_list_spinlock, flags); if (us_list == us) us_list = us->next; else { + prev = us_list; while (prev->next != us) prev = prev->next; prev->next = us->next; } + spin_unlock_irqrestore(&us_list_spinlock, flags); return 0; } @@ -1190,11 +1204,20 @@ static int us_host_reset( Scsi_Cmnd *srb ) int usb_stor_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout) { - struct us_data *us = us_list; + struct us_data *us; char *pos = buffer; char *tmp_ptr; + unsigned long flags; + + /* if someone is sending us data, just throw it away */ + if (inout) + return length; + + /* lock the data structures */ + spin_lock_irqsave(&us_list_spinlock, flags); /* find our data from hostno */ + us = us_list; while (us) { if (us->host_no == hostno) break; @@ -1202,13 +1225,11 @@ int usb_stor_proc_info (char *buffer, char **start, off_t offset, } /* if we couldn't find it, we return an error */ - if (!us) + if (!us) { + spin_unlock_irqrestore(&us_list_spinlock, flags); return -ESRCH; - - /* if someone is sending us data, just throw it away */ - if (inout) - return length; - + } + /* print the controler name */ SPRINTF ("Host scsi%d: usb-storage\n", hostno); @@ -1254,6 +1275,9 @@ int usb_stor_proc_info (char *buffer, char **start, off_t offset, /* show the GUID of the device */ SPRINTF(" GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid)); + /* release our lock on the data structures */ + spin_unlock_irqrestore(&us_list_spinlock, flags); + /* * Calculate start of next buffer, and return value. */ @@ -1365,6 +1389,7 @@ static int usb_stor_control_thread(void * __us) switch (action) { case US_ACT_COMMAND: /* bad device */ + /* FIXME: we need to enable and test multiple LUNs */ if (us->srb->target || us->srb->lun) { US_DEBUGP( "Bad device number (%d/%d) or dev 0x%x\n", us->srb->target, us->srb->lun, (unsigned int)us->pusb_dev); @@ -1378,9 +1403,12 @@ static int usb_stor_control_thread(void * __us) /* our device has gone - pretend not ready */ /* FIXME: we also need to handle INQUIRY here, * probably */ + /* FIXME: fix return codes and sense buffer handling */ if (!us->pusb_dev) { + US_DEBUGP("Request is for removed device\n"); if (us->srb->cmnd[0] == REQUEST_SENSE) { - memcpy(us->srb->request_buffer, sense_notready, + memcpy(us->srb->request_buffer, + sense_notready, sizeof(sense_notready)); us->srb->result = DID_OK << 16; } else { @@ -1443,34 +1471,29 @@ static int usb_stor_control_thread(void * __us) /* Probe to see if a new device is actually a SCSI device */ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) { - struct usb_interface_descriptor *interface; int i; char mf[32]; /* manufacturer */ char prod[32]; /* product */ char serial[32]; /* serial number */ struct us_data *ss = NULL; - unsigned int flags = 0; GUID(guid); /* Global Unique Identifier */ - struct us_data *prev; - int protocol = 0; - int subclass = 0; + int result; + unsigned long flags; + + /* these are temporary copies -- we test on these, then put them + * in the us-data structure + */ + __u8 ep_in = 0; + __u8 ep_out = 0; + __u8 ep_int = 0; + __u8 subclass = 0; + __u8 protocol = 0; + + /* the altsettting 0 on the interface we're probing */ struct usb_interface_descriptor *altsetting = &(dev->actconfig->interface[ifnum].altsetting[0]); - /* clear the GUID and fetch the strings */ - GUID_CLEAR(guid); - memset(mf, 0, sizeof(mf)); - memset(prod, 0, sizeof(prod)); - memset(serial, 0, sizeof(serial)); - if (dev->descriptor.iManufacturer) - usb_string(dev, dev->descriptor.iManufacturer, mf, sizeof(mf)); - if (dev->descriptor.iProduct) - usb_string(dev, dev->descriptor.iProduct, prod, sizeof(prod)); - if (dev->descriptor.iSerialNumber) - usb_string(dev, dev->descriptor.iSerialNumber, serial, sizeof(serial)); - - /* let's examine the device now */ - + /* FIXME: this isn't quite right... */ /* We make an exception for the shuttle E-USB */ if (dev->descriptor.idVendor == 0x04e6 && dev->descriptor.idProduct == 0x0001) { @@ -1487,6 +1510,91 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) /* At this point, we know we've got a live one */ US_DEBUGP("USB Mass Storage device detected\n"); + /* + * We are expecting a minimum of 2 endpoints - in and out (bulk). + * An optional interrupt is OK (necessary for CBI protocol). + * We will ignore any others. + */ + for (i = 0; i < altsetting->bNumEndpoints; i++) { + /* is it an BULK endpoint? */ + if ((altsetting->endpoint[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { + if (altsetting->endpoint[i].bEndpointAddress & + USB_DIR_IN) + ep_in = altsetting->endpoint[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + else + ep_out = altsetting->endpoint[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + } + + /* is it an interrupt endpoint? */ + if ((altsetting->endpoint[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + ep_int = altsetting->endpoint[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + } + } + US_DEBUGP("Endpoints In %d Out %d Int %d\n", + ep_in, ep_out, ep_int); + + /* set the interface -- STALL is an acceptable response here */ + result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0); + US_DEBUGP("Result from usb_set_interface is %d\n", result); + if (result == -EPIPE) { + usb_clear_halt(dev, usb_sndctrlpipe(dev, 0)); + } else if (result != 0) { + /* it's not a stall, but another error -- time to bail */ + return NULL; + } + + /* shuttle E-USB */ + /* FIXME: all we should need to do here is determine the protocol */ + if (dev->descriptor.idVendor == 0x04e6 && + dev->descriptor.idProduct == 0x0001) { + __u8 qstat[2]; + + result = usb_control_msg(ss->pusb_dev, + usb_rcvctrlpipe(dev,0), + 1, 0xC0, + 0, ss->ifnum, + qstat, 2, HZ*5); + US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]); + init_MUTEX_LOCKED(&(ss->ip_waitq)); + ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); + result = usb_request_irq(ss->pusb_dev, ss->irqpipe, + CBI_irq, 255, (void *)ss, + &ss->irq_handle); + if (result < 0) + return NULL; + + /* FIXME: what is this?? */ + down(&(ss->ip_waitq)); + } + + /* Do some basic sanity checks, and bail if we find a problem */ + if (!ep_in || !ep_out || (protocol == US_PR_CBI && !ep_int)) { + US_DEBUGP("Problems with device\n"); + return NULL; + } + + /* At this point, we're committed to using the device */ + + /* clear the GUID and fetch the strings */ + GUID_CLEAR(guid); + memset(mf, 0, sizeof(mf)); + memset(prod, 0, sizeof(prod)); + memset(serial, 0, sizeof(serial)); + if (dev->descriptor.iManufacturer) + usb_string(dev, dev->descriptor.iManufacturer, mf, + sizeof(mf)); + if (dev->descriptor.iProduct) + usb_string(dev, dev->descriptor.iProduct, prod, + sizeof(prod)); + if (dev->descriptor.iSerialNumber) + usb_string(dev, dev->descriptor.iSerialNumber, serial, + sizeof(serial)); + /* Create a GUID for this device */ if (dev->descriptor.iSerialNumber && serial[0]) { /* If we have a serial number, and it's a non-NULL string */ @@ -1498,23 +1606,50 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) dev->descriptor.idProduct, "0"); } - /* Now check if we have seen this GUID before, and restore - * the flags if we find it + /* lock access to the data structures */ + spin_lock_irqsave(&us_list_spinlock, flags); + + /* + * Now check if we have seen this GUID before + * We're looking for a device with a matching GUID that isn't + * allready on the system */ - for (ss = us_list; ss != NULL; ss = ss->next) { - if (!ss->pusb_dev && GUID_EQUAL(guid, ss->guid)) { - US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", - GUID_ARGS(guid)); - flags = ss->flags; - break; + ss = us_list; + while ((ss != NULL) && + ((ss->pusb_dev) || !GUID_EQUAL(guid, ss->guid))) + ss = ss->next; + + if (ss != NULL) { + /* Existing device -- re-connect */ + US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", + GUID_ARGS(guid)); + + /* establish the connection to the new device upon reconnect */ + ss->ifnum = ifnum; + ss->pusb_dev = dev; + + /* hook up the IRQ handler again */ + if (ss->protocol == US_PR_CBI) { + /* set up so we'll wait for notification */ + init_MUTEX_LOCKED(&(ss->ip_waitq)); + + /* set up the IRQ pipe and handler */ + /* FIXME: This needs to get period from the device */ + US_DEBUGP("Allocating IRQ for CBI transport\n"); + ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); + result = usb_request_irq(ss->pusb_dev, ss->irqpipe, + CBI_irq, 255, + (void *)ss, &ss->irq_handle); + US_DEBUGP("usb_request_irq returned %d\n", result); } - } - - /* If ss == NULL, then this is a new device. Allocate memory for it */ - if (!ss) { - if ((ss = (struct us_data *)kmalloc(sizeof(*ss), + } else { + /* New device -- Allocate memory and initialize */ + US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); + + if ((ss = (struct us_data *)kmalloc(sizeof(struct us_data), GFP_KERNEL)) == NULL) { printk(KERN_WARNING USB_STORAGE "Out of memory\n"); + spin_unlock_irqrestore(&us_list_spinlock, flags); return NULL; } memset(ss, 0, sizeof(struct us_data)); @@ -1522,104 +1657,66 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) /* Initialize the mutexes only when the struct is new */ init_MUTEX_LOCKED(&(ss->sleeper)); init_MUTEX(&(ss->queue_exclusion)); - } - - /* establish the connection to the new device */ - interface = altsetting; - ss->flags = flags; - ss->ifnum = ifnum; - ss->attention_done = 0; - ss->pusb_dev = dev; - - /* If the device has subclass and protocol, then use that. Otherwise, - * take data from the specific interface. - */ - if (subclass) { - ss->subclass = subclass; - ss->protocol = protocol; - } else { - ss->subclass = interface->bInterfaceSubClass; - ss->protocol = interface->bInterfaceProtocol; - } - /* set the handler pointers based on the protocol */ - US_DEBUGP("Transport: "); - switch (ss->protocol) { - case US_PR_CB: - US_DEBUGPX("Control/Bulk\n"); - ss->transport = CB_transport; - ss->transport_reset = CB_reset; - break; - - case US_PR_CBI: - US_DEBUGPX("Control/Bulk/Interrupt\n"); - ss->transport = CBI_transport; - ss->transport_reset = CB_reset; - break; - - case US_PR_BULK: - US_DEBUGPX("Bulk\n"); - ss->transport = Bulk_transport; - ss->transport_reset = Bulk_reset; - break; - - default: - US_DEBUGPX("Unknown\n"); - kfree(ss); - return NULL; - break; - } - - /* - * We are expecting a minimum of 2 endpoints - in and out (bulk). - * An optional interrupt is OK (necessary for CBI protocol). - * We will ignore any others. - */ - for (i = 0; i < interface->bNumEndpoints; i++) { - /* is it an BULK endpoint? */ - if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK) { - if (interface->endpoint[i].bEndpointAddress & USB_DIR_IN) - ss->ep_in = interface->endpoint[i].bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK; - else - ss->ep_out = interface->endpoint[i].bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK; - } - - /* is it an interrupt endpoint? */ - if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_INT) { - ss->ep_int = interface->endpoint[i].bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK; + /* + * If we've allready determined the subclass and protocol, + * use that. Otherwise, use the interface ones. This + * allows us to support devices which are compliant but + * don't announce it. Note that this information is + * maintained in the us_data struct so we only have to do + * this for new devices. + */ + if (subclass) { + ss->subclass = subclass; + ss->protocol = protocol; + } else { + ss->subclass = altsetting->bInterfaceSubClass; + ss->protocol = altsetting->bInterfaceProtocol; } - } - US_DEBUGP("Endpoints In %d Out %d Int %d\n", - ss->ep_in, ss->ep_out, ss->ep_int); - /* Do some basic sanity checks, and bail if we find a problem */ - if (usb_set_interface(dev, interface->bInterfaceNumber, 0) || - !ss->ep_in || !ss->ep_out || - (ss->protocol == US_PR_CBI && ss->ep_int == 0)) { - US_DEBUGP("Problems with device\n"); - if (ss->host) { - kfree(ss->htmplt->name); - kfree(ss->htmplt); - } + /* copy over the endpoint data */ + ss->ep_in = ep_in; + ss->ep_out = ep_out; + ss->ep_int = ep_int; - kfree(ss); - return NULL; - } + /* establish the connection to the new device */ + ss->ifnum = ifnum; + ss->pusb_dev = dev; - /* If this is a new device (i.e. we haven't seen it before), we need to - * generate a scsi host definition, and register with scsi above us - */ - if (!ss->host) { /* copy the GUID we created before */ - US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); memcpy(ss->guid, guid, sizeof(guid)); + + /* + * Set the handler pointers based on the protocol + * Again, this data is persistant across reattachments + */ + US_DEBUGP("Transport: "); + switch (ss->protocol) { + case US_PR_CB: + US_DEBUGPX("Control/Bulk\n"); + ss->transport = CB_transport; + ss->transport_reset = CB_reset; + break; + + case US_PR_CBI: + US_DEBUGPX("Control/Bulk/Interrupt\n"); + ss->transport = CBI_transport; + ss->transport_reset = CB_reset; + break; + + case US_PR_BULK: + US_DEBUGPX("Bulk\n"); + ss->transport = Bulk_transport; + ss->transport_reset = Bulk_reset; + break; + + default: + US_DEBUGPX("Unknown\n"); + kfree(ss); + return NULL; + break; + } - /* set class specific stuff */ US_DEBUGP("Protocol: "); switch (ss->subclass) { case US_SC_RBC: @@ -1656,97 +1753,75 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) break; } - /* Allocate memory for the SCSI Host Template */ - if ((ss->htmplt = (Scsi_Host_Template *) - kmalloc(sizeof(Scsi_Host_Template),GFP_KERNEL))==NULL ) { - printk(KERN_WARNING USB_STORAGE "Out of memory\n"); - - kfree(ss); - return NULL; + if (ss->protocol == US_PR_CBI) { + /* set up so we'll wait for notification */ + init_MUTEX_LOCKED(&(ss->ip_waitq)); + + /* set up the IRQ pipe and handler */ + /* FIXME: This needs to get period from the device */ + US_DEBUGP("Allocating IRQ for CBI transport\n"); + ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); + result = usb_request_irq(ss->pusb_dev, ss->irqpipe, + CBI_irq, 255, + (void *)ss, &ss->irq_handle); + US_DEBUGP("usb_request_irq returned %d", result); } + + /* + * Since this is a new device, we need to generate a scsi + * host definition, and register with the higher SCSI layers + */ /* Initialize the host template based on the default one */ - memcpy(ss->htmplt, &my_host_template, sizeof(my_host_template)); + memcpy(&(ss->htmplt), &my_host_template, + sizeof(my_host_template)); /* Grab the next host number */ ss->host_number = my_host_number++; - - /* MDD: FIXME: this is bad. We abuse this pointer so we + + /* FIXME: this is bad. We abuse this pointer so we * can pass the ss pointer to the host controler thread * in us_detect */ - (struct us_data *)ss->htmplt->proc_dir = ss; - - /* shuttle E-USB */ - if (dev->descriptor.idVendor == 0x04e6 && - dev->descriptor.idProduct == 0x0001) { - __u8 qstat[2]; - int result; - - result = usb_control_msg(ss->pusb_dev, - usb_rcvctrlpipe(dev,0), - 1, 0xC0, - 0, ss->ifnum, - qstat, 2, HZ*5); - US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]); - init_MUTEX_LOCKED(&(ss->ip_waitq)); - ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); - result = usb_request_irq(ss->pusb_dev, ss->irqpipe, - CBI_irq, 255, (void *)ss, - &ss->irq_handle); - if (result < 0) - return NULL; - /* FIXME: what is this?? */ - down(&(ss->ip_waitq)); - } else if (ss->protocol == US_PR_CBI) { - int result; - - /* set up so we'll wait for notification */ - init_MUTEX_LOCKED(&(ss->ip_waitq)); - - /* set up the IRQ pipe and handler */ - /* FIXME: This needs to get the period from the device */ - ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); - result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq, - 255, (void *)ss, &ss->irq_handle); - if (result) { - US_DEBUGP("usb_request_irq failed (0x%x), No interrupt for CBI\n", - result); - } - } - - - /* start up our thread */ + (struct us_data *)ss->htmplt.proc_dir = ss; + + /* start up our thread */ { DECLARE_MUTEX_LOCKED(sem); - + ss->notify = &sem; ss->pid = kernel_thread(usb_stor_control_thread, ss, - CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + CLONE_FS | CLONE_FILES | + CLONE_SIGHAND); if (ss->pid < 0) { - printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n"); - kfree(ss->htmplt); - + printk(KERN_WARNING USB_STORAGE + "Unable to start control thread\n"); kfree(ss); return NULL; } - - /* wait for it to start */ + + /* wait for it to start */ down(&sem); } - + /* now register - our detect function will be called */ - ss->htmplt->module = &__this_module; - scsi_register_module(MODULE_SCSI_HA, ss->htmplt); - + ss->htmplt.module = &__this_module; + scsi_register_module(MODULE_SCSI_HA, &(ss->htmplt)); + /* put us in the list */ ss->next = us_list; - us_list = ss; + us_list = ss; } - printk(KERN_DEBUG "WARNING: USB Mass Storage data integrity not assured\n"); - printk(KERN_DEBUG "USB Mass Storage device found at %d\n", dev->devnum); + /* release the data structure lock */ + spin_unlock_irqrestore(&us_list_spinlock, flags); + printk(KERN_DEBUG + "WARNING: USB Mass Storage data integrity not assured\n"); + printk(KERN_DEBUG + "USB Mass Storage device found at %d\n", dev->devnum); + + /* return a pointer for the disconnect function */ return ss; } @@ -1758,6 +1833,14 @@ static void storage_disconnect(struct usb_device *dev, void *ptr) if (!ss) return; + /* FIXME: we need mututal exclusion and resource freeing here */ + + /* release the IRQ, if we have one */ + if (ss->irq_handle) { + usb_release_irq(ss->pusb_dev, ss->irq_handle, ss->irqpipe); + ss->irq_handle = NULL; + } + ss->pusb_dev = NULL; } @@ -1788,12 +1871,15 @@ void __exit usb_stor_exit(void) { static struct us_data *ptr; - // FIXME: this needs to be put back to free _all_ the hosts - // for (ptr = us_list; ptr != NULL; ptr = ptr->next) - // scsi_unregister_module(MODULE_SCSI_HA, ptr->htmplt); - printk("MDD: us_list->htmplt is 0x%x\n", (unsigned int)(us_list->htmplt)); - scsi_unregister_module(MODULE_SCSI_HA, us_list->htmplt); + /* unregister all the virtual hosts */ + for (ptr = us_list; ptr != NULL; ptr = ptr->next) + scsi_unregister_module(MODULE_SCSI_HA, &(ptr->htmplt)); + + /* free up the data structures */ + + /* kill the threads */ + /* deregister the driver */ usb_deregister(&storage_driver) ; } diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c index 85a5cd476..5dbde2959 100644 --- a/drivers/usb/usb-uhci.c +++ b/drivers/usb/usb-uhci.c @@ -28,6 +28,7 @@ #include #include /* for in_interrupt() */ #include +#include #if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44) #include #endif -- cgit v1.2.3