aboutsummaryrefslogtreecommitdiffstats
path: root/usb
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2005-12-21 16:03:09 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2005-12-21 16:03:09 -0800
commit8f201a7bdcb0d5b5a2987d01e1433f0a09968d6d (patch)
tree8d5d5a77750fd9dc81aee6bcea57b49569333d06 /usb
parent7c772cc08c3d4b7d2437c1d69b88bb9cfac954cf (diff)
downloadpatches-8f201a7bdcb0d5b5a2987d01e1433f0a09968d6d.tar.gz
usb and pci patches added
Diffstat (limited to 'usb')
-rw-r--r--usb/usb-asix.c-add-linksys-usb200m-rev-2-ids.patch30
-rw-r--r--usb/usb-limiting-of-resource-use-in-skeleton-driver.patch118
-rw-r--r--usb/usb-replace-__setup-with-__module_param_call.patch56
-rw-r--r--usb/usb-storage-fix-unusual_devs.h-order.patch47
-rw-r--r--usb/usb-ub-00-implement-retries-and-resets.patch748
-rw-r--r--usb/usb-ub-01-rename.patch44
-rw-r--r--usb/usb-ub-02-removed-unused-variable.patch34
-rw-r--r--usb/usb-zd1201-make-sysfs-device-symlink.patch43
8 files changed, 1120 insertions, 0 deletions
diff --git a/usb/usb-asix.c-add-linksys-usb200m-rev-2-ids.patch b/usb/usb-asix.c-add-linksys-usb200m-rev-2-ids.patch
new file mode 100644
index 0000000000000..41922512223cc
--- /dev/null
+++ b/usb/usb-asix.c-add-linksys-usb200m-rev-2-ids.patch
@@ -0,0 +1,30 @@
+From dhollis@davehollis.com Mon Dec 19 11:01:27 2005
+From: David Hollis <dhollis@davehollis.com>
+Subject: USB: asix.c - Add Linksys USB200M Rev 2 ids
+To: Greg KH <greg@kroah.com>
+Date: Mon, 19 Dec 2005 13:58:38 -0500
+Message-Id: <1135018718.26911.3.camel@dhollis-lnx.sunera.com>
+
+Attached patch adds device IDs for the Linksys USB200M Rev 2 device
+which uses the AX88772 chipset.
+
+Signed-off-by: David Hollis <dhollis@davehollis.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/net/asix.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- gregkh-2.6.orig/drivers/usb/net/asix.c
++++ gregkh-2.6/drivers/usb/net/asix.c
+@@ -912,6 +912,10 @@ static const struct usb_device_id produc
+ // ASIX AX88772 10/100
+ USB_DEVICE (0x0b95, 0x7720),
+ .driver_info = (unsigned long) &ax88772_info,
++}, {
++ // Linksys USB200M Rev 2
++ USB_DEVICE (0x13b1, 0x0018),
++ .driver_info = (unsigned long) &ax88772_info,
+ },
+ { }, // END
+ };
diff --git a/usb/usb-limiting-of-resource-use-in-skeleton-driver.patch b/usb/usb-limiting-of-resource-use-in-skeleton-driver.patch
new file mode 100644
index 0000000000000..816d8321232ff
--- /dev/null
+++ b/usb/usb-limiting-of-resource-use-in-skeleton-driver.patch
@@ -0,0 +1,118 @@
+From oliver@neukum.org Wed Dec 21 12:26:29 2005
+From: Oliver Neukum <oliver@neukum.org>
+To: Greg KH <greg@kroah.com>
+Subject: USB: Limiting of resource use in skeleton driver
+Date: Wed, 21 Dec 2005 19:27:29 +0100
+Content-Disposition: inline
+Message-Id: <200512211927.29612.oliver@neukum.org>
+
+this introduces limits whose lack in the skeleton driver someone recently
+complained about.
+
+Signed-off-by: Oliver Neukum <oliver@neukum.name>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/usb-skeleton.c | 25 ++++++++++++++++++-------
+ 1 file changed, 18 insertions(+), 7 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/usb-skeleton.c
++++ gregkh-2.6/drivers/usb/usb-skeleton.c
+@@ -39,10 +39,15 @@ MODULE_DEVICE_TABLE (usb, skel_table);
+ /* Get a minor range for your devices from the usb maintainer */
+ #define USB_SKEL_MINOR_BASE 192
+
++/* our private defines. if this grows any larger, use your own .h file */
++#define MAX_TRANSFER ( PAGE_SIZE - 512 )
++#define WRITES_IN_FLIGHT 8
++
+ /* Structure to hold all of our device specific stuff */
+ struct usb_skel {
+ struct usb_device * udev; /* the usb device for this device */
+ struct usb_interface * interface; /* the interface for this device */
++ struct semaphore limit_sem; /* limiting the number of writes in progress */
+ unsigned char * bulk_in_buffer; /* the buffer to receive data */
+ size_t bulk_in_size; /* the size of the receive buffer */
+ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
+@@ -152,6 +157,7 @@ static void skel_write_bulk_callback(str
+ /* free up our allocated buffer */
+ usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
++ up(&dev->limit_sem);
+ }
+
+ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos)
+@@ -160,6 +166,7 @@ static ssize_t skel_write(struct file *f
+ int retval = 0;
+ struct urb *urb = NULL;
+ char *buf = NULL;
++ size_t writesize = max(count, MAX_TRANSFER);
+
+ dev = (struct usb_skel *)file->private_data;
+
+@@ -167,6 +174,9 @@ static ssize_t skel_write(struct file *f
+ if (count == 0)
+ goto exit;
+
++ /* limit the number of URBs in flight to stop a user from using up all RAM */
++ down (&dev->limit_sem);
++
+ /* create a urb, and a buffer for it, and copy the data to the urb */
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+@@ -174,13 +184,13 @@ static ssize_t skel_write(struct file *f
+ goto error;
+ }
+
+- buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
++ buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL, &urb->transfer_dma);
+ if (!buf) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+- if (copy_from_user(buf, user_buffer, count)) {
++ if (copy_from_user(buf, user_buffer, writesize)) {
+ retval = -EFAULT;
+ goto error;
+ }
+@@ -188,7 +198,7 @@ static ssize_t skel_write(struct file *f
+ /* initialize the urb properly */
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
+- buf, count, skel_write_bulk_callback, dev);
++ buf, writesize, skel_write_bulk_callback, dev);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* send the data out the bulk port */
+@@ -202,11 +212,12 @@ static ssize_t skel_write(struct file *f
+ usb_free_urb(urb);
+
+ exit:
+- return count;
++ return writesize;
+
+ error:
+- usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
++ usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
+ usb_free_urb(urb);
++ up(&dev->limit_sem);
+ return retval;
+ }
+
+@@ -238,13 +249,13 @@ static int skel_probe(struct usb_interfa
+ int retval = -ENOMEM;
+
+ /* allocate memory for our device state and initialize it */
+- dev = kmalloc(sizeof(*dev), GFP_KERNEL);
++ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ err("Out of memory");
+ goto error;
+ }
+- memset(dev, 0x00, sizeof(*dev));
+ kref_init(&dev->kref);
++ sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
+
+ dev->udev = usb_get_dev(interface_to_usbdev(interface));
+ dev->interface = interface;
diff --git a/usb/usb-replace-__setup-with-__module_param_call.patch b/usb/usb-replace-__setup-with-__module_param_call.patch
new file mode 100644
index 0000000000000..e4166b02d2a98
--- /dev/null
+++ b/usb/usb-replace-__setup-with-__module_param_call.patch
@@ -0,0 +1,56 @@
+From zaitcev@redhat.com Tue Dec 20 14:19:40 2005
+Date: Tue, 20 Dec 2005 14:15:04 -0800
+From: Pete Zaitcev <zaitcev@redhat.com>
+To: greg@kroah.com
+Cc: zaitcev@redhat.com
+Subject: usb: replace __setup("nousb") with __module_param_call
+Message-Id: <20051220141504.31441a41.zaitcev@redhat.com>
+
+Fedora users complain that passing "nousbstorage" to the installer causes
+the rest of the USB support to disappear. The installer uses kernel command
+line as a way to pass options through Syslinux. The problem stems from the
+use of strncmp() in obsolete_checksetup().
+
+I used __module_param_call() instead of module_param because I wanted to
+preserve the old syntax in grub.conf, and it's the only macro which allows
+to remove the prefix.
+
+The fix is tested to accept the option "nousb" correctly now.
+
+Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/usb.c | 13 +------------
+ 1 file changed, 1 insertion(+), 12 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/core/usb.c
++++ gregkh-2.6/drivers/usb/core/usb.c
+@@ -46,7 +46,6 @@
+ const char *usbcore_name = "usbcore";
+
+ static int nousb; /* Disable USB when built into kernel image */
+- /* Not honored on modular build */
+
+
+ /**
+@@ -1093,18 +1092,8 @@ struct bus_type usb_bus_type = {
+ .resume = usb_generic_resume,
+ };
+
+-#ifndef MODULE
+-
+-static int __init usb_setup_disable(char *str)
+-{
+- nousb = 1;
+- return 1;
+-}
+-
+ /* format to disable USB on kernel command line is: nousb */
+-__setup("nousb", usb_setup_disable);
+-
+-#endif
++__module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444);
+
+ /*
+ * for external read access to <nousb>
diff --git a/usb/usb-storage-fix-unusual_devs.h-order.patch b/usb/usb-storage-fix-unusual_devs.h-order.patch
new file mode 100644
index 0000000000000..ace2f3beb8942
--- /dev/null
+++ b/usb/usb-storage-fix-unusual_devs.h-order.patch
@@ -0,0 +1,47 @@
+From phil@ipom.com Sun Dec 18 21:36:08 2005
+Message-ID: <43A6455A.9020406@ipom.com>
+Date: Sun, 18 Dec 2005 21:30:02 -0800
+From: Phil Dibowitz <phil@ipom.com>
+To: Greg KH <greg@kroah.com>, USB Storage list <usb-storage@lists.one-eyed-alien.net>
+Subject: USB Storage: Fix unusual_devs.h order
+
+Alan Stern pointed out there was an ordering issue in unusual_devs.h,
+and this patch fixes it.
+
+Signed-off-by: Phil Dibowitz <phil@ipom.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/storage/unusual_devs.h | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/storage/unusual_devs.h
++++ gregkh-2.6/drivers/usb/storage/unusual_devs.h
+@@ -79,13 +79,6 @@ UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x
+ US_SC_8070, US_PR_USBAT, init_usbat, 0),
+ #endif
+
+-/* Patch submitted by Mihnea-Costin Grigore <mihnea@zulu.ro> */
+-UNUSUAL_DEV( 0x040d, 0x6205, 0x0003, 0x0003,
+- "VIA Technologies Inc.",
+- "USB 2.0 Card Reader",
+- US_SC_DEVICE, US_PR_DEVICE, NULL,
+- US_FL_IGNORE_RESIDUE ),
+-
+ /* Reported by Sebastian Kapfer <sebastian_kapfer@gmx.net>
+ * and Olaf Hering <olh@suse.de> (different bcd's, same vendor/product)
+ * for USB floppies that need the SINGLE_LUN enforcement.
+@@ -96,6 +89,13 @@ UNUSUAL_DEV( 0x0409, 0x0040, 0x0000, 0x
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_SINGLE_LUN ),
+
++/* Patch submitted by Mihnea-Costin Grigore <mihnea@zulu.ro> */
++UNUSUAL_DEV( 0x040d, 0x6205, 0x0003, 0x0003,
++ "VIA Technologies Inc.",
++ "USB 2.0 Card Reader",
++ US_SC_DEVICE, US_PR_DEVICE, NULL,
++ US_FL_IGNORE_RESIDUE ),
++
+ /* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
+ * Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message
+ * always fails and confuses drive.
diff --git a/usb/usb-ub-00-implement-retries-and-resets.patch b/usb/usb-ub-00-implement-retries-and-resets.patch
new file mode 100644
index 0000000000000..e3c976cac065b
--- /dev/null
+++ b/usb/usb-ub-00-implement-retries-and-resets.patch
@@ -0,0 +1,748 @@
+From zaitcev@redhat.com Sat Dec 17 02:17:55 2005
+Date: Sat, 17 Dec 2005 02:16:43 -0800
+From: Pete Zaitcev <zaitcev@redhat.com>
+To: greg@kroah.com
+Cc: zaitcev@redhat.com
+Subject: usb: ub 00 implement retries and resets
+Message-Id: <20051217021643.01ff8b56.zaitcev@redhat.com>
+
+Implement command retries and resets in ub. It is advantageous for users
+to know if their devices are getting bad. However, failing every I/O
+is not practical if you have a external USB enclosure with a hard drive.
+
+Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/block/ub.c | 404 +++++++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 287 insertions(+), 117 deletions(-)
+
+--- gregkh-2.6.orig/drivers/block/ub.c
++++ gregkh-2.6/drivers/block/ub.c
+@@ -9,7 +9,6 @@
+ *
+ * TODO (sorted by decreasing priority)
+ * -- Kill first_open (Al Viro fixed the block layer now)
+- * -- Do resets with usb_device_reset (needs a thread context, use khubd)
+ * -- set readonly flag for CDs, set removable flag for CF readers
+ * -- do inquiry and verify we got a disk and not a tape (for LUN mismatch)
+ * -- special case some senses, e.g. 3a/0 -> no media present, reduce retries
+@@ -234,6 +233,13 @@ struct ub_scsi_cmd {
+ void *back;
+ };
+
++struct ub_request {
++ struct request *rq;
++ unsigned int current_try;
++ unsigned int nsg; /* sgv[nsg] */
++ struct scatterlist sgv[UB_MAX_REQ_SG];
++};
++
+ /*
+ */
+ struct ub_capacity {
+@@ -329,6 +335,8 @@ struct ub_lun {
+ int readonly;
+ int first_open; /* Kludge. See ub_bd_open. */
+
++ struct ub_request urq;
++
+ /* Use Ingo's mempool if or when we have more than one command. */
+ /*
+ * Currently we never need more than one command for the whole device.
+@@ -349,6 +357,7 @@ struct ub_dev {
+ atomic_t poison; /* The USB device is disconnected */
+ int openc; /* protected by ub_lock! */
+ /* kref is too implicit for our taste */
++ int reset; /* Reset is running */
+ unsigned int tagcnt;
+ char name[12];
+ struct usb_device *dev;
+@@ -376,6 +385,9 @@ struct ub_dev {
+ struct bulk_cs_wrap work_bcs;
+ struct usb_ctrlrequest work_cr;
+
++ struct work_struct reset_work;
++ wait_queue_head_t reset_wait;
++
+ int sg_stat[6];
+ struct ub_scsi_trace tr;
+ };
+@@ -384,12 +396,14 @@ struct ub_dev {
+ */
+ static void ub_cleanup(struct ub_dev *sc);
+ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq);
+-static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
+- struct ub_scsi_cmd *cmd, struct request *rq);
+-static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
+- struct ub_scsi_cmd *cmd, struct request *rq);
++static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
++ struct ub_scsi_cmd *cmd, struct ub_request *urq);
++static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
++ struct ub_scsi_cmd *cmd, struct ub_request *urq);
+ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
+ static void ub_end_rq(struct request *rq, int uptodate);
++static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
++ struct ub_request *urq, struct ub_scsi_cmd *cmd);
+ static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
+ static void ub_urb_complete(struct urb *urb, struct pt_regs *pt);
+ static void ub_scsi_action(unsigned long _dev);
+@@ -404,6 +418,8 @@ static void ub_state_sense(struct ub_dev
+ static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
+ int stalled_pipe);
+ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd);
++static void ub_reset_enter(struct ub_dev *sc);
++static void ub_reset_task(void *arg);
+ static int ub_sync_tur(struct ub_dev *sc, struct ub_lun *lun);
+ static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun,
+ struct ub_capacity *ret);
+@@ -516,6 +532,9 @@ static ssize_t ub_diag_show(struct devic
+ spin_lock_irqsave(&sc->lock, flags);
+
+ cnt += sprintf(page + cnt,
++ "poison %d reset %d\n",
++ atomic_read(&sc->poison), sc->reset);
++ cnt += sprintf(page + cnt,
+ "qlen %d qmax %d\n",
+ sc->cmd_queue.qlen, sc->cmd_queue.qmax);
+ cnt += sprintf(page + cnt,
+@@ -764,7 +783,8 @@ static int ub_request_fn_1(struct ub_lun
+ {
+ struct ub_dev *sc = lun->udev;
+ struct ub_scsi_cmd *cmd;
+- int rc;
++ struct ub_request *urq;
++ int n_elem;
+
+ if (atomic_read(&sc->poison) || lun->changed) {
+ blkdev_dequeue_request(rq);
+@@ -772,65 +792,70 @@ static int ub_request_fn_1(struct ub_lun
+ return 0;
+ }
+
++ if (lun->urq.rq != NULL)
++ return -1;
+ if ((cmd = ub_get_cmd(lun)) == NULL)
+ return -1;
+ memset(cmd, 0, sizeof(struct ub_scsi_cmd));
+
+ blkdev_dequeue_request(rq);
++
++ urq = &lun->urq;
++ memset(urq, 0, sizeof(struct ub_request));
++ urq->rq = rq;
++
++ /*
++ * get scatterlist from block layer
++ */
++ n_elem = blk_rq_map_sg(lun->disk->queue, rq, &urq->sgv[0]);
++ if (n_elem < 0) {
++ printk(KERN_INFO "%s: failed request map (%d)\n",
++ lun->name, n_elem); /* P3 */
++ goto drop;
++ }
++ if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */
++ printk(KERN_WARNING "%s: request with %d segments\n",
++ lun->name, n_elem);
++ goto drop;
++ }
++ urq->nsg = n_elem;
++ sc->sg_stat[n_elem < 5 ? n_elem : 5]++;
++
+ if (blk_pc_request(rq)) {
+- rc = ub_cmd_build_packet(sc, lun, cmd, rq);
++ ub_cmd_build_packet(sc, lun, cmd, urq);
+ } else {
+- rc = ub_cmd_build_block(sc, lun, cmd, rq);
+- }
+- if (rc != 0) {
+- ub_put_cmd(lun, cmd);
+- ub_end_rq(rq, 0);
+- return 0;
++ ub_cmd_build_block(sc, lun, cmd, urq);
+ }
+ cmd->state = UB_CMDST_INIT;
+ cmd->lun = lun;
+ cmd->done = ub_rw_cmd_done;
+- cmd->back = rq;
++ cmd->back = urq;
+
+ cmd->tag = sc->tagcnt++;
+- if (ub_submit_scsi(sc, cmd) != 0) {
+- ub_put_cmd(lun, cmd);
+- ub_end_rq(rq, 0);
+- return 0;
+- }
++ if (ub_submit_scsi(sc, cmd) != 0)
++ goto drop;
+
+ return 0;
++
++drop:
++ ub_put_cmd(lun, cmd);
++ ub_end_rq(rq, 0);
++ return 0;
+ }
+
+-static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
+- struct ub_scsi_cmd *cmd, struct request *rq)
++static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
++ struct ub_scsi_cmd *cmd, struct ub_request *urq)
+ {
+- int ub_dir;
+- int n_elem;
++ struct request *rq = urq->rq;
+ unsigned int block, nblks;
+
+ if (rq_data_dir(rq) == WRITE)
+- ub_dir = UB_DIR_WRITE;
++ cmd->dir = UB_DIR_WRITE;
+ else
+- ub_dir = UB_DIR_READ;
+- cmd->dir = ub_dir;
++ cmd->dir = UB_DIR_READ;
+
+- /*
+- * get scatterlist from block layer
+- */
+- n_elem = blk_rq_map_sg(lun->disk->queue, rq, &cmd->sgv[0]);
+- if (n_elem <= 0) {
+- printk(KERN_INFO "%s: failed request map (%d)\n",
+- sc->name, n_elem); /* P3 */
+- return -1; /* request with no s/g entries? */
+- }
+- if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */
+- printk(KERN_WARNING "%s: request with %d segments\n",
+- sc->name, n_elem);
+- return -1;
+- }
+- cmd->nsg = n_elem;
+- sc->sg_stat[n_elem < 5 ? n_elem : 5]++;
++ cmd->nsg = urq->nsg;
++ memcpy(cmd->sgv, urq->sgv, sizeof(struct scatterlist) * cmd->nsg);
+
+ /*
+ * build the command
+@@ -841,7 +866,7 @@ static int ub_cmd_build_block(struct ub_
+ block = rq->sector >> lun->capacity.bshift;
+ nblks = rq->nr_sectors >> lun->capacity.bshift;
+
+- cmd->cdb[0] = (ub_dir == UB_DIR_READ)? READ_10: WRITE_10;
++ cmd->cdb[0] = (cmd->dir == UB_DIR_READ)? READ_10: WRITE_10;
+ /* 10-byte uses 4 bytes of LBA: 2147483648KB, 2097152MB, 2048GB */
+ cmd->cdb[2] = block >> 24;
+ cmd->cdb[3] = block >> 16;
+@@ -852,14 +877,12 @@ static int ub_cmd_build_block(struct ub_
+ cmd->cdb_len = 10;
+
+ cmd->len = rq->nr_sectors * 512;
+-
+- return 0;
+ }
+
+-static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
+- struct ub_scsi_cmd *cmd, struct request *rq)
++static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
++ struct ub_scsi_cmd *cmd, struct ub_request *urq)
+ {
+- int n_elem;
++ struct request *rq = urq->rq;
+
+ if (rq->data_len == 0) {
+ cmd->dir = UB_DIR_NONE;
+@@ -868,40 +891,26 @@ static int ub_cmd_build_packet(struct ub
+ cmd->dir = UB_DIR_WRITE;
+ else
+ cmd->dir = UB_DIR_READ;
+-
+ }
+
+- /*
+- * get scatterlist from block layer
+- */
+- n_elem = blk_rq_map_sg(lun->disk->queue, rq, &cmd->sgv[0]);
+- if (n_elem < 0) {
+- printk(KERN_INFO "%s: failed request map (%d)\n",
+- sc->name, n_elem); /* P3 */
+- return -1;
+- }
+- if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */
+- printk(KERN_WARNING "%s: request with %d segments\n",
+- sc->name, n_elem);
+- return -1;
+- }
+- cmd->nsg = n_elem;
+- sc->sg_stat[n_elem < 5 ? n_elem : 5]++;
++ cmd->nsg = urq->nsg;
++ memcpy(cmd->sgv, urq->sgv, sizeof(struct scatterlist) * cmd->nsg);
+
+ memcpy(&cmd->cdb, rq->cmd, rq->cmd_len);
+ cmd->cdb_len = rq->cmd_len;
+
+ cmd->len = rq->data_len;
+-
+- return 0;
+ }
+
+ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
+ {
+- struct request *rq = cmd->back;
+ struct ub_lun *lun = cmd->lun;
++ struct ub_request *urq = cmd->back;
++ struct request *rq;
+ int uptodate;
+
++ rq = urq->rq;
++
+ if (cmd->error == 0) {
+ uptodate = 1;
+
+@@ -922,9 +931,16 @@ static void ub_rw_cmd_done(struct ub_dev
+ rq->errors = SAM_STAT_CHECK_CONDITION;
+ else
+ rq->errors = DID_ERROR << 16;
++ } else {
++ if (cmd->error == -EIO) {
++ if (ub_rw_cmd_retry(sc, lun, urq, cmd) == 0)
++ return;
++ }
+ }
+ }
+
++ urq->rq = NULL;
++
+ ub_put_cmd(lun, cmd);
+ ub_end_rq(rq, uptodate);
+ blk_start_queue(lun->disk->queue);
+@@ -939,6 +955,41 @@ static void ub_end_rq(struct request *rq
+ end_that_request_last(rq);
+ }
+
++static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
++ struct ub_request *urq, struct ub_scsi_cmd *cmd)
++{
++
++ if (atomic_read(&sc->poison))
++ return -ENXIO;
++
++ ub_reset_enter(sc);
++
++ if (urq->current_try >= 3)
++ return -EIO;
++ urq->current_try++;
++ /* P3 */ printk("%s: dir %c len/act %d/%d "
++ "[sense %x %02x %02x] retry %d\n",
++ sc->name, UB_DIR_CHAR(cmd->dir), cmd->len, cmd->act_len,
++ cmd->key, cmd->asc, cmd->ascq, urq->current_try);
++
++ memset(cmd, 0, sizeof(struct ub_scsi_cmd));
++ ub_cmd_build_block(sc, lun, cmd, urq);
++
++ cmd->state = UB_CMDST_INIT;
++ cmd->lun = lun;
++ cmd->done = ub_rw_cmd_done;
++ cmd->back = urq;
++
++ cmd->tag = sc->tagcnt++;
++
++#if 0 /* Wasteful */
++ return ub_submit_scsi(sc, cmd);
++#else
++ ub_cmdq_add(sc, cmd);
++ return 0;
++#endif
++}
++
+ /*
+ * Submit a regular SCSI operation (not an auto-sense).
+ *
+@@ -1069,7 +1120,7 @@ static void ub_scsi_dispatch(struct ub_d
+ struct ub_scsi_cmd *cmd;
+ int rc;
+
+- while ((cmd = ub_cmdq_peek(sc)) != NULL) {
++ while (!sc->reset && (cmd = ub_cmdq_peek(sc)) != NULL) {
+ if (cmd->state == UB_CMDST_DONE) {
+ ub_cmdq_pop(sc);
+ (*cmd->done)(sc, cmd);
+@@ -1092,11 +1143,12 @@ static void ub_scsi_urb_compl(struct ub_
+ {
+ struct urb *urb = &sc->work_urb;
+ struct bulk_cs_wrap *bcs;
++ int len;
+ int rc;
+
+ if (atomic_read(&sc->poison)) {
+- /* A little too simplistic, I feel... */
+- goto Bad_End;
++ ub_state_done(sc, cmd, -ENODEV);
++ return;
+ }
+
+ if (cmd->state == UB_CMDST_CLEAR) {
+@@ -1104,7 +1156,6 @@ static void ub_scsi_urb_compl(struct ub_
+ /*
+ * STALL while clearning STALL.
+ * The control pipe clears itself - nothing to do.
+- * XXX Might try to reset the device here and retry.
+ */
+ printk(KERN_NOTICE "%s: stall on control pipe\n",
+ sc->name);
+@@ -1123,11 +1174,6 @@ static void ub_scsi_urb_compl(struct ub_
+
+ } else if (cmd->state == UB_CMDST_CLR2STS) {
+ if (urb->status == -EPIPE) {
+- /*
+- * STALL while clearning STALL.
+- * The control pipe clears itself - nothing to do.
+- * XXX Might try to reset the device here and retry.
+- */
+ printk(KERN_NOTICE "%s: stall on control pipe\n",
+ sc->name);
+ goto Bad_End;
+@@ -1145,11 +1191,6 @@ static void ub_scsi_urb_compl(struct ub_
+
+ } else if (cmd->state == UB_CMDST_CLRRS) {
+ if (urb->status == -EPIPE) {
+- /*
+- * STALL while clearning STALL.
+- * The control pipe clears itself - nothing to do.
+- * XXX Might try to reset the device here and retry.
+- */
+ printk(KERN_NOTICE "%s: stall on control pipe\n",
+ sc->name);
+ goto Bad_End;
+@@ -1166,7 +1207,12 @@ static void ub_scsi_urb_compl(struct ub_
+ ub_state_stat_counted(sc, cmd);
+
+ } else if (cmd->state == UB_CMDST_CMD) {
+- if (urb->status == -EPIPE) {
++ switch (urb->status) {
++ case 0:
++ break;
++ case -EOVERFLOW:
++ goto Bad_End;
++ case -EPIPE:
+ rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
+ if (rc != 0) {
+ printk(KERN_NOTICE "%s: "
+@@ -1176,17 +1222,20 @@ static void ub_scsi_urb_compl(struct ub_
+ * This is typically ENOMEM or some other such shit.
+ * Retrying is pointless. Just do Bad End on it...
+ */
+- goto Bad_End;
++ ub_state_done(sc, cmd, rc);
++ return;
+ }
+ cmd->state = UB_CMDST_CLEAR;
+ ub_cmdtr_state(sc, cmd);
+ return;
+- }
+- if (urb->status != 0) {
++ case -ESHUTDOWN: /* unplug */
++ case -EILSEQ: /* unplug timeout on uhci */
++ ub_state_done(sc, cmd, -ENODEV);
++ return;
++ default:
+ goto Bad_End;
+ }
+ if (urb->actual_length != US_BULK_CB_WRAP_LEN) {
+- /* XXX Must do reset here to unconfuse the device */
+ goto Bad_End;
+ }
+
+@@ -1205,11 +1254,8 @@ static void ub_scsi_urb_compl(struct ub_
+ printk(KERN_NOTICE "%s: "
+ "unable to submit clear (%d)\n",
+ sc->name, rc);
+- /*
+- * This is typically ENOMEM or some other such shit.
+- * Retrying is pointless. Just do Bad End on it...
+- */
+- goto Bad_End;
++ ub_state_done(sc, cmd, rc);
++ return;
+ }
+ cmd->state = UB_CMDST_CLR2STS;
+ ub_cmdtr_state(sc, cmd);
+@@ -1218,14 +1264,50 @@ static void ub_scsi_urb_compl(struct ub_
+ if (urb->status == -EOVERFLOW) {
+ /*
+ * A babble? Failure, but we must transfer CSW now.
+- * XXX This is going to end in perpetual babble. Reset.
+ */
+ cmd->error = -EOVERFLOW; /* A cheap trick... */
+ ub_state_stat(sc, cmd);
+ return;
+ }
+- if (urb->status != 0)
+- goto Bad_End;
++
++ if (cmd->dir == UB_DIR_WRITE) {
++ /*
++ * Do not continue writes in case of a failure.
++ * Doing so would cause sectors to be mixed up,
++ * which is worse than sectors lost.
++ *
++ * We must try to read the CSW, or many devices
++ * get confused.
++ */
++ len = urb->actual_length;
++ if (urb->status != 0 ||
++ len != cmd->sgv[cmd->current_sg].length) {
++ cmd->act_len += len;
++ ub_cmdtr_act_len(sc, cmd);
++
++ cmd->error = -EIO;
++ ub_state_stat(sc, cmd);
++ return;
++ }
++
++ } else {
++ /*
++ * If an error occurs on read, we record it, and
++ * continue to fetch data in order to avoid bubble.
++ *
++ * As a small shortcut, we stop if we detect that
++ * a CSW mixed into data.
++ */
++ if (urb->status != 0)
++ cmd->error = -EIO;
++
++ len = urb->actual_length;
++ if (urb->status != 0 ||
++ len != cmd->sgv[cmd->current_sg].length) {
++ if ((len & 0x1FF) == US_BULK_CS_WRAP_LEN)
++ goto Bad_End;
++ }
++ }
+
+ cmd->act_len += urb->actual_length;
+ ub_cmdtr_act_len(sc, cmd);
+@@ -1243,11 +1325,8 @@ static void ub_scsi_urb_compl(struct ub_
+ printk(KERN_NOTICE "%s: "
+ "unable to submit clear (%d)\n",
+ sc->name, rc);
+- /*
+- * This is typically ENOMEM or some other such shit.
+- * Retrying is pointless. Just do Bad End on it...
+- */
+- goto Bad_End;
++ ub_state_done(sc, cmd, rc);
++ return;
+ }
+
+ /*
+@@ -1260,14 +1339,8 @@ static void ub_scsi_urb_compl(struct ub_
+ ub_cmdtr_state(sc, cmd);
+ return;
+ }
+- if (urb->status == -EOVERFLOW) {
+- /*
+- * XXX We are screwed here. Retrying is pointless,
+- * because the pipelined data will not get in until
+- * we read with a big enough buffer. We must reset XXX.
+- */
+- goto Bad_End;
+- }
++
++ /* Catch everything, including -EOVERFLOW and other nasties. */
+ if (urb->status != 0)
+ goto Bad_End;
+
+@@ -1313,15 +1386,15 @@ static void ub_scsi_urb_compl(struct ub_
+ return;
+ }
+
+- rc = le32_to_cpu(bcs->Residue);
+- if (rc != cmd->len - cmd->act_len) {
++ len = le32_to_cpu(bcs->Residue);
++ if (len != cmd->len - cmd->act_len) {
+ /*
+ * It is all right to transfer less, the caller has
+ * to check. But it's not all right if the device
+ * counts disagree with our counts.
+ */
+ /* P3 */ printk("%s: resid %d len %d act %d\n",
+- sc->name, rc, cmd->len, cmd->act_len);
++ sc->name, len, cmd->len, cmd->act_len);
+ goto Bad_End;
+ }
+
+@@ -1332,13 +1405,13 @@ static void ub_scsi_urb_compl(struct ub_
+ ub_state_sense(sc, cmd);
+ return;
+ case US_BULK_STAT_PHASE:
+- /* XXX We must reset the transport here */
+ /* P3 */ printk("%s: status PHASE\n", sc->name);
+ goto Bad_End;
+ default:
+ printk(KERN_INFO "%s: unknown CSW status 0x%x\n",
+ sc->name, bcs->Status);
+- goto Bad_End;
++ ub_state_done(sc, cmd, -EINVAL);
++ return;
+ }
+
+ /* Not zeroing error to preserve a babble indicator */
+@@ -1358,7 +1431,8 @@ static void ub_scsi_urb_compl(struct ub_
+ printk(KERN_WARNING "%s: "
+ "wrong command state %d\n",
+ sc->name, cmd->state);
+- goto Bad_End;
++ ub_state_done(sc, cmd, -EINVAL);
++ return;
+ }
+ return;
+
+@@ -1606,6 +1680,93 @@ static void ub_top_sense_done(struct ub_
+ }
+
+ /*
++ * Reset management
++ */
++
++static void ub_reset_enter(struct ub_dev *sc)
++{
++
++ if (sc->reset) {
++ /* This happens often on multi-LUN devices. */
++ return;
++ }
++ sc->reset = 1;
++
++#if 0 /* Not needed because the disconnect waits for us. */
++ unsigned long flags;
++ spin_lock_irqsave(&ub_lock, flags);
++ sc->openc++;
++ spin_unlock_irqrestore(&ub_lock, flags);
++#endif
++
++#if 0 /* We let them stop themselves. */
++ struct list_head *p;
++ struct ub_lun *lun;
++ list_for_each(p, &sc->luns) {
++ lun = list_entry(p, struct ub_lun, link);
++ blk_stop_queue(lun->disk->queue);
++ }
++#endif
++
++ schedule_work(&sc->reset_work);
++}
++
++static void ub_reset_task(void *arg)
++{
++ struct ub_dev *sc = arg;
++ unsigned long flags;
++ struct list_head *p;
++ struct ub_lun *lun;
++ int lkr, rc;
++
++ if (!sc->reset) {
++ printk(KERN_WARNING "%s: Running reset unrequested\n",
++ sc->name);
++ return;
++ }
++
++ if (atomic_read(&sc->poison)) {
++ printk(KERN_NOTICE "%s: Not resetting disconnected device\n",
++ sc->name); /* P3 This floods. Remove soon. XXX */
++ } else if (sc->dev->actconfig->desc.bNumInterfaces != 1) {
++ printk(KERN_NOTICE "%s: Not resetting multi-interface device\n",
++ sc->name); /* P3 This floods. Remove soon. XXX */
++ } else {
++ if ((lkr = usb_lock_device_for_reset(sc->dev, sc->intf)) < 0) {
++ printk(KERN_NOTICE
++ "%s: usb_lock_device_for_reset failed (%d)\n",
++ sc->name, lkr);
++ } else {
++ rc = usb_reset_device(sc->dev);
++ if (rc < 0) {
++ printk(KERN_NOTICE "%s: "
++ "usb_lock_device_for_reset failed (%d)\n",
++ sc->name, rc);
++ }
++
++ if (lkr)
++ usb_unlock_device(sc->dev);
++ }
++ }
++
++ /*
++ * In theory, no commands can be running while reset is active,
++ * so nobody can ask for another reset, and so we do not need any
++ * queues of resets or anything. We do need a spinlock though,
++ * to interact with block layer.
++ */
++ spin_lock_irqsave(&sc->lock, flags);
++ sc->reset = 0;
++ tasklet_schedule(&sc->tasklet);
++ list_for_each(p, &sc->luns) {
++ lun = list_entry(p, struct ub_lun, link);
++ blk_start_queue(lun->disk->queue);
++ }
++ wake_up(&sc->reset_wait);
++ spin_unlock_irqrestore(&sc->lock, flags);
++}
++
++/*
+ * This is called from a process context.
+ */
+ static void ub_revalidate(struct ub_dev *sc, struct ub_lun *lun)
+@@ -2140,7 +2301,7 @@ static int ub_get_pipes(struct ub_dev *s
+ if (ep_in == NULL || ep_out == NULL) {
+ printk(KERN_NOTICE "%s: failed endpoint check\n",
+ sc->name);
+- return -EIO;
++ return -ENODEV;
+ }
+
+ /* Calculate and store the pipe values */
+@@ -2178,6 +2339,8 @@ static int ub_probe(struct usb_interface
+ usb_init_urb(&sc->work_urb);
+ tasklet_init(&sc->tasklet, ub_scsi_action, (unsigned long)sc);
+ atomic_set(&sc->poison, 0);
++ INIT_WORK(&sc->reset_work, ub_reset_task, sc);
++ init_waitqueue_head(&sc->reset_wait);
+
+ init_timer(&sc->work_timer);
+ sc->work_timer.data = (unsigned long) sc;
+@@ -2198,7 +2361,8 @@ static int ub_probe(struct usb_interface
+
+ /* XXX Verify that we can handle the device (from descriptors) */
+
+- ub_get_pipes(sc, sc->dev, intf);
++ if (ub_get_pipes(sc, sc->dev, intf) != 0)
++ goto err_dev_desc;
+
+ if (device_create_file(&sc->intf->dev, &dev_attr_diag) != 0)
+ goto err_diag;
+@@ -2269,6 +2433,7 @@ static int ub_probe(struct usb_interface
+
+ /* device_remove_file(&sc->intf->dev, &dev_attr_diag); */
+ err_diag:
++err_dev_desc:
+ usb_set_intfdata(intf, NULL);
+ // usb_put_intf(sc->intf);
+ usb_put_dev(sc->dev);
+@@ -2376,6 +2541,11 @@ static void ub_disconnect(struct usb_int
+ atomic_set(&sc->poison, 1);
+
+ /*
++ * Wait for reset to end, if any.
++ */
++ wait_event(sc->reset_wait, !sc->reset);
++
++ /*
+ * Blow away queued commands.
+ *
+ * Actually, this never works, because before we get here
+@@ -2388,7 +2558,7 @@ static void ub_disconnect(struct usb_int
+ {
+ struct ub_scsi_cmd *cmd;
+ int cnt = 0;
+- while ((cmd = ub_cmdq_pop(sc)) != NULL) {
++ while ((cmd = ub_cmdq_peek(sc)) != NULL) {
+ cmd->error = -ENOTCONN;
+ cmd->state = UB_CMDST_DONE;
+ ub_cmdtr_state(sc, cmd);
diff --git a/usb/usb-ub-01-rename.patch b/usb/usb-ub-01-rename.patch
new file mode 100644
index 0000000000000..b57ba7bb04e5e
--- /dev/null
+++ b/usb/usb-ub-01-rename.patch
@@ -0,0 +1,44 @@
+From zaitcev@redhat.com Sat Dec 17 02:38:38 2005
+Date: Sat, 17 Dec 2005 02:34:12 -0800
+From: Pete Zaitcev <zaitcev@redhat.com>
+To: greg@kroah.com
+Cc: zaitcev@redhat.com, jgarzik@redhat.com
+Subject: usb: ub 01 rename
+Message-Id: <20051217023412.54e37b60.zaitcev@redhat.com>
+
+Rename misleading UB_MINORS_PER_MAJOR into UB_PARTS_PER_LUN.
+
+Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/block/ub.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- gregkh-2.6.orig/drivers/block/ub.c
++++ gregkh-2.6/drivers/block/ub.c
+@@ -113,7 +113,7 @@
+ /*
+ */
+
+-#define UB_MINORS_PER_MAJOR 8
++#define UB_PARTS_PER_LUN 8
+
+ #define UB_MAX_CDB_SIZE 16 /* Corresponds to Bulk */
+
+@@ -2471,13 +2471,13 @@ static int ub_probe_lun(struct ub_dev *s
+ ub_revalidate(sc, lun);
+
+ rc = -ENOMEM;
+- if ((disk = alloc_disk(UB_MINORS_PER_MAJOR)) == NULL)
++ if ((disk = alloc_disk(UB_PARTS_PER_LUN)) == NULL)
+ goto err_diskalloc;
+
+ lun->disk = disk;
+ sprintf(disk->disk_name, DRV_NAME "%c", lun->id + 'a');
+ disk->major = UB_MAJOR;
+- disk->first_minor = lun->id * UB_MINORS_PER_MAJOR;
++ disk->first_minor = lun->id * UB_PARTS_PER_LUN;
+ disk->fops = &ub_bd_fops;
+ disk->private_data = lun;
+ disk->driverfs_dev = &sc->intf->dev;
diff --git a/usb/usb-ub-02-removed-unused-variable.patch b/usb/usb-ub-02-removed-unused-variable.patch
new file mode 100644
index 0000000000000..54f0dcea5196d
--- /dev/null
+++ b/usb/usb-ub-02-removed-unused-variable.patch
@@ -0,0 +1,34 @@
+From zaitcev@redhat.com Sat Dec 17 02:43:51 2005
+Date: Sat, 17 Dec 2005 02:38:46 -0800
+From: Pete Zaitcev <zaitcev@redhat.com>
+To: greg@kroah.com
+Cc: zaitcev@redhat.com
+Subject: usb: ub 02 Removed unused variable
+Message-Id: <20051217023846.371889c1.zaitcev@redhat.com>
+
+From: Daniel Marjamaki <daniel.marjamaki@comhem.se>
+
+Removed an unused variable
+
+Signed-off-by: Daniel Marjamaki <daniel.marjamaki@comhem.se>
+Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/block/ub.c | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+--- gregkh-2.6.orig/drivers/block/ub.c
++++ gregkh-2.6/drivers/block/ub.c
+@@ -948,10 +948,7 @@ static void ub_rw_cmd_done(struct ub_dev
+
+ static void ub_end_rq(struct request *rq, int uptodate)
+ {
+- int rc;
+-
+- rc = end_that_request_first(rq, uptodate, rq->hard_nr_sectors);
+- // assert(rc == 0);
++ end_that_request_first(rq, uptodate, rq->hard_nr_sectors);
+ end_that_request_last(rq);
+ }
+
diff --git a/usb/usb-zd1201-make-sysfs-device-symlink.patch b/usb/usb-zd1201-make-sysfs-device-symlink.patch
new file mode 100644
index 0000000000000..8003d211c9f11
--- /dev/null
+++ b/usb/usb-zd1201-make-sysfs-device-symlink.patch
@@ -0,0 +1,43 @@
+From linux-usb-devel-admin@lists.sourceforge.net Sun Dec 18 21:48:07 2005
+From: Nathan Lynch <ntl@pobox.com>
+Cc: pe1rxq@amsat.org
+Message-ID: <20051219054137.GA17414@localhost.localdomain>
+Content-Disposition: inline
+Subject: USB: zd1201: make sysfs device symlink
+Date: Sun, 18 Dec 2005 23:41:38 -0600
+
+Noticed that my zd1201 adapter isn't "seen" by hal and NetworkManager.
+The problem seems to be that unlike other network device drivers I
+checked, zd1201 does not do a SET_NETDEV_DEV(), which makes it so a
+"device" symlink is created under /sys/class/net/wlan0.
+
+With the following patch the device symlink shows up, and now I am
+happily using NetworkManager to control the adapter:
+
+$ ls -l /sys/class/net/wlan0
+total 0
+-r--r--r-- 1 root root 4096 Dec 18 13:42 address
+-r--r--r-- 1 root root 4096 Dec 18 13:42 addr_len
+-r--r--r-- 1 root root 4096 Dec 18 13:42 broadcast
+-r--r--r-- 1 root root 4096 Dec 18 13:42 carrier
+lrwxrwxrwx 1 root root 0 Dec 18 13:42 device -> ../../../devices/pci0001:10/0001:10:1b.1/usb4/4-1
+-r--r--r-- 1 root root 4096 Dec 18 13:42 features
+
+Signed-off-by: Nathan Lynch <ntl@pobox.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/net/zd1201.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- gregkh-2.6.orig/drivers/usb/net/zd1201.c
++++ gregkh-2.6/drivers/usb/net/zd1201.c
+@@ -1829,6 +1829,8 @@ static int zd1201_probe(struct usb_inter
+ if (err)
+ goto err_net;
+
++ SET_NETDEV_DEV(zd->dev, &usb->dev);
++
+ err = register_netdev(zd->dev);
+ if (err)
+ goto err_net;