diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2008-06-27 15:31:36 -0700 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-06-27 15:31:36 -0700 |
| commit | fac3bd7fb0ab462289f9763a71730b4443d71f10 (patch) | |
| tree | 44c74bfe4f09223a6b28e09c2fd2c433e67b2dce | |
| parent | d3d8f7cbd2aa88b680430dfea88dea6bb3a73250 (diff) | |
| download | patches-fac3bd7fb0ab462289f9763a71730b4443d71f10.tar.gz | |
lots of new patches
25 files changed, 3045 insertions, 0 deletions
diff --git a/driver-core/always-enable-fw_loader-unless-embedded-y.patch b/driver-core/always-enable-fw_loader-unless-embedded-y.patch new file mode 100644 index 00000000000000..850768771587a9 --- /dev/null +++ b/driver-core/always-enable-fw_loader-unless-embedded-y.patch @@ -0,0 +1,51 @@ +From bunk@kernel.org Fri Jun 27 15:13:04 2008 +From: Adrian Bunk <bunk@kernel.org> +Date: Tue, 10 Jun 2008 19:04:08 +0300 +Subject: always enable FW_LOADER unless EMBEDDED=y +To: linux-kernel@vger.kernel.org +Cc: David Woodhouse <dwmw2@infradead.org>, James Bottomley <James.Bottomley@HansenPartnership.com>, Greg KH <greg@kroah.com>, Andrew Morton <akpm@linux-foundation.org> +Message-ID: <20080610160408.GB11685@cs181133002.pp.htv.fi> +Content-Disposition: inline + + +James Bottomley recently discovered that we have +{request,release}_firmware() dummies for the case of the actual +functions not being available and has a fix for the bug that was +actually causing build errors for built-in users with +CONFIG_FW_LOADER=m. + +But now missing selects on FW_LOADER are no longer visible at +compile-time at all and can become runtime problems. + +FW_LOADER is infrastructure with relatively small codesize we can safely +enable for everyone, and only for people who really need small kernels +(and can be expected to know what they are doing) it matters being able +to disable it. + +This patch therefore always sets FW_LOADER=y and allows users only to +disable it with EMBEDDED=y. + +As a bonus, we can then get rid of all "select FW_LOADER" plus the due +to it required "depends on HOTPLUG" which removes some complexity from +our Kconfig files. + +Signed-off-by: Adrian Bunk <bunk@kernel.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/base/Kconfig | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/base/Kconfig ++++ b/drivers/base/Kconfig +@@ -27,8 +27,9 @@ config PREVENT_FIRMWARE_BUILD + If unsure say Y here. + + config FW_LOADER +- tristate "Userspace firmware loading support" ++ tristate "Userspace firmware loading support" if EMBEDDED + depends on HOTPLUG ++ default y + ---help--- + This option is provided for the case where no in-kernel-tree modules + require userspace firmware loading support, but a module built outside diff --git a/driver-core/howto-change-email-addresses-of-james-in-howto.patch b/driver-core/howto-change-email-addresses-of-james-in-howto.patch new file mode 100644 index 00000000000000..cda9901bfbc87c --- /dev/null +++ b/driver-core/howto-change-email-addresses-of-james-in-howto.patch @@ -0,0 +1,27 @@ +From tshibata@ab.jp.nec.com Fri Jun 27 14:41:46 2008 +From: Tsugikazu Shibata <tshibata@ab.jp.nec.com> +Date: Fri, 20 Jun 2008 10:59:52 +0900 (JST) +Subject: HOWTO: change email addresses of James in HOWTO +To: greg@kroah.com, James.Bottomley@HansenPartnership.com +Cc: tshibata@ab.jp.nec.com +Message-ID: <20080620.105952.95887859.tshibata@ab.jp.nec.com> + + +Signed-off-by: Tsugikazu Shibata <tshibata@ab.jp.nec.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/HOWTO | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/Documentation/HOWTO ++++ b/Documentation/HOWTO +@@ -358,7 +358,7 @@ Here is a list of some of the different + - pcmcia, Dominik Brodowski <linux@dominikbrodowski.net> + git.kernel.org:/pub/scm/linux/kernel/git/brodo/pcmcia-2.6.git + +- - SCSI, James Bottomley <James.Bottomley@SteelEye.com> ++ - SCSI, James Bottomley <James.Bottomley@hansenpartnership.com> + git.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git + + - x86, Ingo Molnar <mingo@elte.hu> diff --git a/driver-core/uio-howto.tmpl-use-standard-copyright-legal-markings.patch b/driver-core/uio-howto.tmpl-use-standard-copyright-legal-markings.patch new file mode 100644 index 00000000000000..bb884ef3c91973 --- /dev/null +++ b/driver-core/uio-howto.tmpl-use-standard-copyright-legal-markings.patch @@ -0,0 +1,64 @@ +From akpm@linux-foundation.org Fri Jun 27 14:39:54 2008 +From: Mike Frysinger <vapier@gentoo.org> +Date: Tue, 24 Jun 2008 14:24:57 -0700 +Subject: uio-howto.tmpl: use standard copyright/legal markings +To: mm-commits@vger.kernel.org +Cc: vapier@gentoo.org, greg@kroah.com, hjk@linutronix.de, randy.dunlap@oracle.com +Message-ID: <200806242124.m5OLOw69006858@imap1.linux-foundation.org> + + +From: Mike Frysinger <vapier@gentoo.org> + +The Userspace I/O HOWTO document uses straight <sect1> tags and plain text +to describe copyright/legal information. It should instead use the +<copyright> and <legalnotice> tags like all other documents in the kernel. + +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Acked-by: Hans J. Koch <hjk@linutronix.de> +Cc: Randy Dunlap <randy.dunlap@oracle.com> +Signed-off-by: Andrew Morton <akpm@linux-foundation.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/DocBook/uio-howto.tmpl | 23 ++++++++++++----------- + 1 file changed, 12 insertions(+), 11 deletions(-) + +--- a/Documentation/DocBook/uio-howto.tmpl ++++ b/Documentation/DocBook/uio-howto.tmpl +@@ -21,6 +21,18 @@ + </affiliation> + </author> + ++<copyright> ++ <year>2006-2008</year> ++ <holder>Hans-Jürgen Koch.</holder> ++</copyright> ++ ++<legalnotice> ++<para> ++This documentation is Free Software licensed under the terms of the ++GPL version 2. ++</para> ++</legalnotice> ++ + <pubdate>2006-12-11</pubdate> + + <abstract> +@@ -66,17 +78,6 @@ + <?dbhtml filename="about.html"?> + <title>About this document</title> + +-<sect1 id="copyright"> +-<?dbhtml filename="copyright.html"?> +-<title>Copyright and License</title> +-<para> +- Copyright (c) 2006-2008 by Hans-Jürgen Koch.</para> +-<para> +-This documentation is Free Software licensed under the terms of the +-GPL version 2. +-</para> +-</sect1> +- + <sect1 id="translations"> + <?dbhtml filename="translations.html"?> + <title>Translations</title> diff --git a/driver-core/uio-howto.tmpl-use-unique-output-names.patch b/driver-core/uio-howto.tmpl-use-unique-output-names.patch new file mode 100644 index 00000000000000..6a910dfe2bc9ac --- /dev/null +++ b/driver-core/uio-howto.tmpl-use-unique-output-names.patch @@ -0,0 +1,35 @@ +From akpm@linux-foundation.org Fri Jun 27 14:40:23 2008 +From: Mike Frysinger <vapier@gentoo.org> +Date: Tue, 24 Jun 2008 14:25:00 -0700 +Subject: uio-howto.tmpl: use unique output names +To: mm-commits@vger.kernel.org +Cc: vapier@gentoo.org, greg@kroah.com, hjk@linutronix.de, randy.dunlap@oracle.com +Message-ID: <200806242125.m5OLP26K006869@imap1.linux-foundation.org> + +From: Mike Frysinger <vapier@gentoo.org> + +The Userspace I/O HOWTO template sets two different sections with the same +html output name (about.html). This clearly won't work, so change the +first one to a unique "aboutthis.html" to prevent clobbering. + +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Acked-by: Hans J. Koch <hjk@linutronix.de> +Cc: Randy Dunlap <randy.dunlap@oracle.com> +Signed-off-by: Andrew Morton <akpm@linux-foundation.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/DocBook/uio-howto.tmpl | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/Documentation/DocBook/uio-howto.tmpl ++++ b/Documentation/DocBook/uio-howto.tmpl +@@ -75,7 +75,7 @@ GPL version 2. + </bookinfo> + + <chapter id="aboutthisdoc"> +-<?dbhtml filename="about.html"?> ++<?dbhtml filename="aboutthis.html"?> + <title>About this document</title> + + <sect1 id="translations"> @@ -23,6 +23,8 @@ usb.current/usb-fix-cdc-acm-resume.patch usb.current/usb-ehci-fix-timer-regression.patch usb.current/usb-ohci-record-data-toggle-after-unlink.patch usb.current/usb-mass-storage-new-id-for-us_sc_cyp_atacb.patch +usb.current/sisusbvga-fix-oops-on-disconnect.patch +usb.current/usb-new-device-id-for-ftdi_sio-driver.patch ##################################################################### # Stuff to be merged after 2.6.26 is out @@ -101,6 +103,10 @@ driver-core/kobject-reorder-kobject-to-save-space-on-64-bit-builds.patch driver-core/kobject-should-use-kobject_put-in-kset-example.patch driver-core/sysdev-fix-debugging-statements-in-registration-code.patch driver-core/sysfs-don-t-call-notify_change.patch +driver-core/uio-howto.tmpl-use-standard-copyright-legal-markings.patch +driver-core/uio-howto.tmpl-use-unique-output-names.patch +driver-core/always-enable-fw_loader-unless-embedded-y.patch +driver-core/howto-change-email-addresses-of-james-in-howto.patch # bus_id fun driver-core/pci-make-pci_name-use-dev_name.patch @@ -229,6 +235,24 @@ usb/usb-fix-comment-of-usb_set_configuration.patch usb/usb-isp1760-support-board-specific-hardware-configurations.patch usb/usb-usb-serial-fix-a-sparse-warning-about-different-signedness.patch usb/usb-fix-usb_reset_device-and-usb_reset_composite_device.patch +usb/usb-r8a66597-hcd-fix-interrupt-trigger.patch +usb/usb-r8a66597-hcd-fix-iinterval-for-full-low-speed-device.patch +usb/usb-fix-uninitialized-variable-warning-in-keyspan_pda.patch +usb/usb-keyspan-remove-duplicate-device-entries.patch +usb/usb-additional-power-savings-for-cdc-acm-devices-that-support-remote-wakeup.patch +usb/usb-autosuspend-for-cdc-wdm.patch +usb/usb-fix-usb-serial-pm-counter-decrement-for-disconnected-interfaces.patch +usb/usb-fix-disconnect-bug-in-cdc-acm.patch +usb/usb-debug-port-converter-does-not-accept-more-than-8-byte-packets.patch +usb/usb-ohci-pnx4008-i2c-cleanups-and-fixes.patch +usb/usb-make-pxa27x_udc-less-invasive-on-up2ocr-pxa-register.patch +usb/usb-force-unbinding-of-drivers-lacking-reset_resume-or-other-methods.patch +usb/usbfs-send-disconnect-signals-when-device-is-unregistered.patch +usb/usbfs-simplify-the-lookup-by-minor-routines.patch +usb/usbfs-fix-race-between-open-and-unregister.patch +usb/usbfs-don-t-store-bad-pointers-in-registration.patch +usb/usb-au1xxx-usb-clean-up-ohci-ehci-bus-glue-sources.patch +usb/usb-au1xxx-usb-suspend-resume-support.patch # wireless usb, still a work in progress usb/bitmap-add-bitmap_copy_le.patch @@ -292,3 +316,4 @@ usb/usb-gotemp.patch #module-usb-serial.patch #modpost + diff --git a/usb.current/sisusbvga-fix-oops-on-disconnect.patch b/usb.current/sisusbvga-fix-oops-on-disconnect.patch new file mode 100644 index 00000000000000..70633160bb1875 --- /dev/null +++ b/usb.current/sisusbvga-fix-oops-on-disconnect.patch @@ -0,0 +1,35 @@ +From will.newton@imgtec.com Fri Jun 27 14:34:47 2008 +From: Will Newton <will.newton@imgtec.com> +Date: Fri, 27 Jun 2008 13:08:08 +0100 +Subject: sisusbvga: Fix oops on disconnect. +To: linux-kernel@vger.kernel.org +Cc: linux-usb@vger.kernel.org, felipe.lima@indt.org.br, gregkh@suse.de, Will Newton <will.newton@gmail.com> +Message-ID: <1214568488-5356-1-git-send-email-will.newton@imgtec.com> + + +From: Will Newton <will.newton@gmail.com> + +Remove dev_info call on disconnect. The sisusb_dev pointer may have been +set to zero by sisusb_delete at this point causing an oops. + +The message does not provide any extra information over the standard USB +subsystem output so removing it does not affect functionality. + +Signed-off-by: Will Newton <will.newton@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/misc/sisusbvga/sisusb.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/usb/misc/sisusbvga/sisusb.c ++++ b/drivers/usb/misc/sisusbvga/sisusb.c +@@ -3264,8 +3264,6 @@ static void sisusb_disconnect(struct usb + + /* decrement our usage count */ + kref_put(&sisusb->kref, sisusb_delete); +- +- dev_info(&sisusb->sisusb_dev->dev, "Disconnected\n"); + } + + static struct usb_device_id sisusb_table [] = { diff --git a/usb.current/usb-new-device-id-for-ftdi_sio-driver.patch b/usb.current/usb-new-device-id-for-ftdi_sio-driver.patch new file mode 100644 index 00000000000000..075546fde1e358 --- /dev/null +++ b/usb.current/usb-new-device-id-for-ftdi_sio-driver.patch @@ -0,0 +1,67 @@ +From hellan@acm.org Fri Jun 27 15:17:23 2008 +From: Jon K Hellan <hellan@acm.org> +Date: Tue, 24 Jun 2008 11:43:13 +0200 +Subject: USB: New device ID for ftdi_sio driver +To: linux-usb@vger.kernel.org +Message-ID: <4860C1B1.4060001@acm.org> + +From: Jon K Hellan <hellan@acm.org> + +Here's a new device ID for the ftdio_sio driver. +The diff is with linus's tree as of this morning. + +The device is the RigExpert Tiny USB Soundcard Transceiver Interface for ham +radio. + +(I didn't actually test this. A fellow ham couldn't get the device to work, and +I suggested binding the device ID using sysfs - see +"http://jk.ufisa.uninett.no/usb/". However, he had had moved on to other things +by then. I guess adding the device ID to the kernel "on spec" won't hurt. +The relevant part of cat /proc/bus/usb/devices shows: + +T: Bus=02 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0 +D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 +P: Vendor=0403 ProdID=ed22 Rev= 5.00 +S: Manufacturer=FTDI +S: Product=MixW RigExpert Tiny +S: SerialNumber=00000000 +C:* #Ifs= 2 Cfg#= 1 Atr=80 MxPwr=100mA +I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) +E: Ad=81(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms +E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms +I:* If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) +E: Ad=83(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms +E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms +) + +From: Jon K Hellan <hellan@acm.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/usb/serial/ftdi_sio.c | 1 + + drivers/usb/serial/ftdi_sio.h | 3 +++ + 2 files changed, 4 insertions(+) + +--- a/drivers/usb/serial/ftdi_sio.c ++++ b/drivers/usb/serial/ftdi_sio.c +@@ -637,6 +637,7 @@ static struct usb_device_id id_table_com + { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, ++ { USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) }, + { }, /* Optional parameter entry */ + { } /* Terminating entry */ + }; +--- a/drivers/usb/serial/ftdi_sio.h ++++ b/drivers/usb/serial/ftdi_sio.h +@@ -828,6 +828,9 @@ + /* Propox devices */ + #define FTDI_PROPOX_JTAGCABLEII_PID 0xD738 + ++/* Rig Expert Ukraine devices */ ++#define FTDI_REU_TINY_PID 0xED22 /* RigExpert Tiny */ ++ + /* Commands */ + #define FTDI_SIO_RESET 0 /* Reset the port */ + #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ diff --git a/usb/usb-additional-power-savings-for-cdc-acm-devices-that-support-remote-wakeup.patch b/usb/usb-additional-power-savings-for-cdc-acm-devices-that-support-remote-wakeup.patch new file mode 100644 index 00000000000000..edf68b8c8413d4 --- /dev/null +++ b/usb/usb-additional-power-savings-for-cdc-acm-devices-that-support-remote-wakeup.patch @@ -0,0 +1,367 @@ +From oliver@neukum.org Fri Jun 27 14:38:18 2008 +From: Oliver Neukum <oliver@neukum.org> +Date: Fri, 20 Jun 2008 11:25:57 +0200 +Subject: USB: additional power savings for cdc-acm devices that support remote wakeup +To: Greg KH <greg@kroah.com>, USB list <linux-usb@vger.kernel.org> +Message-ID: <200806201125.57479.oliver@neukum.org> +Content-Disposition: inline + + +this patch saves power for cdc-acm devices that support remote wakeup +while the device is connected. + +- request needs_remote_wakeup when needed +- delayed write while a device is autoresumed +- the device is marked busy when appropriate + +Signed-off-by: Oliver Neukum <oneukum@suse.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/usb/class/cdc-acm.c | 142 +++++++++++++++++++++++++++++++++++++------- + drivers/usb/class/cdc-acm.h | 5 + + 2 files changed, 126 insertions(+), 21 deletions(-) + +--- a/drivers/usb/class/cdc-acm.c ++++ b/drivers/usb/class/cdc-acm.c +@@ -159,12 +159,34 @@ static void acm_write_done(struct acm *a + spin_lock_irqsave(&acm->write_lock, flags); + acm->write_ready = 1; + wb->use = 0; ++ acm->transmitting--; + spin_unlock_irqrestore(&acm->write_lock, flags); + } + + /* + * Poke write. ++ * ++ * the caller is responsible for locking + */ ++ ++static int acm_start_wb(struct acm *acm, struct acm_wb *wb) ++{ ++ int rc; ++ ++ acm->transmitting++; ++ ++ wb->urb->transfer_buffer = wb->buf; ++ wb->urb->transfer_dma = wb->dmah; ++ wb->urb->transfer_buffer_length = wb->len; ++ wb->urb->dev = acm->dev; ++ ++ if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) { ++ dbg("usb_submit_urb(write bulk) failed: %d", rc); ++ acm_write_done(acm, wb); ++ } ++ return rc; ++} ++ + static int acm_write_start(struct acm *acm, int wbn) + { + unsigned long flags; +@@ -182,26 +204,31 @@ static int acm_write_start(struct acm *a + return 0; /* A white lie */ + } + ++ wb = &acm->wb[wbn]; ++ if(acm_wb_is_avail(acm) <= 1) ++ acm->write_ready = 0; ++ ++ dbg("%s susp_count: %d", __func__, acm->susp_count); ++ if (acm->susp_count) { ++ acm->old_ready = acm->write_ready; ++ acm->delayed_wb = wb; ++ acm->write_ready = 0; ++ schedule_work(&acm->waker); ++ spin_unlock_irqrestore(&acm->write_lock, flags); ++ return 0; /* A white lie */ ++ } ++ usb_mark_last_busy(acm->dev); ++ + if (!acm_wb_is_used(acm, wbn)) { + spin_unlock_irqrestore(&acm->write_lock, flags); + return 0; + } +- wb = &acm->wb[wbn]; + +- if(acm_wb_is_avail(acm) <= 1) +- acm->write_ready = 0; ++ rc = acm_start_wb(acm, wb); + spin_unlock_irqrestore(&acm->write_lock, flags); + +- wb->urb->transfer_buffer = wb->buf; +- wb->urb->transfer_dma = wb->dmah; +- wb->urb->transfer_buffer_length = wb->len; +- wb->urb->dev = acm->dev; +- +- if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) { +- dbg("usb_submit_urb(write bulk) failed: %d", rc); +- acm_write_done(acm, wb); +- } + return rc; ++ + } + /* + * attributes exported through sysfs +@@ -304,6 +331,7 @@ static void acm_ctrl_irq(struct urb *urb + break; + } + exit: ++ usb_mark_last_busy(acm->dev); + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + err ("%s - usb_submit_urb failed with result %d", +@@ -320,8 +348,11 @@ static void acm_read_bulk(struct urb *ur + + dbg("Entering acm_read_bulk with status %d", status); + +- if (!ACM_READY(acm)) ++ if (!ACM_READY(acm)) { ++ dev_dbg(&acm->data->dev, "Aborting, acm not ready"); + return; ++ } ++ usb_mark_last_busy(acm->dev); + + if (status) + dev_dbg(&acm->data->dev, "bulk rx status %d\n", status); +@@ -331,6 +362,7 @@ static void acm_read_bulk(struct urb *ur + + if (likely(status == 0)) { + spin_lock(&acm->read_lock); ++ acm->processing++; + list_add_tail(&rcv->list, &acm->spare_read_urbs); + list_add_tail(&buf->list, &acm->filled_read_bufs); + spin_unlock(&acm->read_lock); +@@ -343,7 +375,8 @@ static void acm_read_bulk(struct urb *ur + /* nevertheless the tasklet must be kicked unconditionally + so the queue cannot dry up */ + } +- tasklet_schedule(&acm->urb_task); ++ if (likely(!acm->susp_count)) ++ tasklet_schedule(&acm->urb_task); + } + + static void acm_rx_tasklet(unsigned long _acm) +@@ -354,16 +387,23 @@ static void acm_rx_tasklet(unsigned long + struct acm_ru *rcv; + unsigned long flags; + unsigned char throttled; ++ + dbg("Entering acm_rx_tasklet"); + + if (!ACM_READY(acm)) ++ { ++ dbg("acm_rx_tasklet: ACM not ready"); + return; ++ } + + spin_lock_irqsave(&acm->throttle_lock, flags); + throttled = acm->throttle; + spin_unlock_irqrestore(&acm->throttle_lock, flags); + if (throttled) ++ { ++ dbg("acm_rx_tasklet: throttled"); + return; ++ } + + next_buffer: + spin_lock_irqsave(&acm->read_lock, flags); +@@ -403,6 +443,7 @@ urbs: + while (!list_empty(&acm->spare_read_bufs)) { + spin_lock_irqsave(&acm->read_lock, flags); + if (list_empty(&acm->spare_read_urbs)) { ++ acm->processing = 0; + spin_unlock_irqrestore(&acm->read_lock, flags); + return; + } +@@ -425,18 +466,23 @@ urbs: + rcv->urb->transfer_dma = buf->dma; + rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + +- dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); +- + /* This shouldn't kill the driver as unsuccessful URBs are returned to the + free-urbs-pool and resubmited ASAP */ +- if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { ++ spin_lock_irqsave(&acm->read_lock, flags); ++ if (acm->susp_count || usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { + list_add(&buf->list, &acm->spare_read_bufs); +- spin_lock_irqsave(&acm->read_lock, flags); + list_add(&rcv->list, &acm->spare_read_urbs); ++ acm->processing = 0; + spin_unlock_irqrestore(&acm->read_lock, flags); + return; ++ } else { ++ spin_unlock_irqrestore(&acm->read_lock, flags); ++ dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); + } + } ++ spin_lock_irqsave(&acm->read_lock, flags); ++ acm->processing = 0; ++ spin_unlock_irqrestore(&acm->read_lock, flags); + } + + /* data interface wrote those outgoing bytes */ +@@ -463,6 +509,27 @@ static void acm_softint(struct work_stru + tty_wakeup(acm->tty); + } + ++static void acm_waker(struct work_struct *waker) ++{ ++ struct acm *acm = container_of(waker, struct acm, waker); ++ long flags; ++ int rv; ++ ++ rv = usb_autopm_get_interface(acm->control); ++ if (rv < 0) { ++ err("Autopm failure in %s", __func__); ++ return; ++ } ++ if (acm->delayed_wb) { ++ acm_start_wb(acm, acm->delayed_wb); ++ acm->delayed_wb = NULL; ++ } ++ spin_lock_irqsave(&acm->write_lock, flags); ++ acm->write_ready = acm->old_ready; ++ spin_unlock_irqrestore(&acm->write_lock, flags); ++ usb_autopm_put_interface(acm->control); ++} ++ + /* + * TTY handlers + */ +@@ -492,6 +559,8 @@ static int acm_tty_open(struct tty_struc + + if (usb_autopm_get_interface(acm->control) < 0) + goto early_bail; ++ else ++ acm->control->needs_remote_wakeup = 1; + + mutex_lock(&acm->mutex); + if (acm->used++) { +@@ -509,6 +578,7 @@ static int acm_tty_open(struct tty_struc + if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && + (acm->ctrl_caps & USB_CDC_CAP_LINE)) + goto full_bailout; ++ usb_autopm_put_interface(acm->control); + + INIT_LIST_HEAD(&acm->spare_read_urbs); + INIT_LIST_HEAD(&acm->spare_read_bufs); +@@ -570,12 +640,14 @@ static void acm_tty_close(struct tty_str + mutex_lock(&open_mutex); + if (!--acm->used) { + if (acm->dev) { ++ usb_autopm_get_interface(acm->control); + acm_set_control(acm, acm->ctrlout = 0); + usb_kill_urb(acm->ctrlurb); + for (i = 0; i < ACM_NW; i++) + usb_kill_urb(acm->wb[i].urb); + for (i = 0; i < nr; i++) + usb_kill_urb(acm->ru[i].urb); ++ acm->control->needs_remote_wakeup = 0; + usb_autopm_put_interface(acm->control); + } else + acm_tty_unregister(acm); +@@ -987,6 +1059,7 @@ skip_normal_probe: + acm->urb_task.func = acm_rx_tasklet; + acm->urb_task.data = (unsigned long) acm; + INIT_WORK(&acm->work, acm_softint); ++ INIT_WORK(&acm->waker, acm_waker); + spin_lock_init(&acm->throttle_lock); + spin_lock_init(&acm->write_lock); + spin_lock_init(&acm->read_lock); +@@ -1116,6 +1189,7 @@ alloc_fail: + static void stop_data_traffic(struct acm *acm) + { + int i; ++ dbg("Entering stop_data_traffic"); + + tasklet_disable(&acm->urb_task); + +@@ -1128,6 +1202,7 @@ static void stop_data_traffic(struct acm + tasklet_enable(&acm->urb_task); + + cancel_work_sync(&acm->work); ++ cancel_work_sync(&acm->waker); + } + + static void acm_disconnect(struct usb_interface *intf) +@@ -1181,8 +1256,27 @@ static void acm_disconnect(struct usb_in + static int acm_suspend(struct usb_interface *intf, pm_message_t message) + { + struct acm *acm = usb_get_intfdata(intf); ++ int cnt; + +- if (acm->susp_count++) ++ if (acm->dev->auto_pm) { ++ int b; ++ ++ spin_lock_irq(&acm->read_lock); ++ spin_lock(&acm->write_lock); ++ b = acm->processing + acm->transmitting; ++ spin_unlock(&acm->write_lock); ++ spin_unlock_irq(&acm->read_lock); ++ if (b) ++ return -EBUSY; ++ } ++ ++ spin_lock_irq(&acm->read_lock); ++ spin_lock(&acm->write_lock); ++ cnt = acm->susp_count++; ++ spin_unlock(&acm->write_lock); ++ spin_unlock_irq(&acm->read_lock); ++ ++ if (cnt) + return 0; + /* + we treat opened interfaces differently, +@@ -1201,15 +1295,21 @@ static int acm_resume(struct usb_interfa + { + struct acm *acm = usb_get_intfdata(intf); + int rv = 0; ++ int cnt; + +- if (--acm->susp_count) ++ spin_lock_irq(&acm->read_lock); ++ acm->susp_count -= 1; ++ cnt = acm->susp_count; ++ spin_unlock_irq(&acm->read_lock); ++ ++ if (cnt) + return 0; + + mutex_lock(&acm->mutex); + if (acm->used) { + rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO); + if (rv < 0) +- goto err_out; ++ goto err_out; + + tasklet_schedule(&acm->urb_task); + } +--- a/drivers/usb/class/cdc-acm.h ++++ b/drivers/usb/class/cdc-acm.h +@@ -107,10 +107,14 @@ struct acm { + struct list_head filled_read_bufs; + int write_used; /* number of non-empty write buffers */ + int write_ready; /* write urb is not running */ ++ int old_ready; ++ int processing; ++ int transmitting; + spinlock_t write_lock; + struct mutex mutex; + struct usb_cdc_line_coding line; /* bits, stop, parity */ + struct work_struct work; /* work queue entry for line discipline waking up */ ++ struct work_struct waker; + struct tasklet_struct urb_task; /* rx processing */ + spinlock_t throttle_lock; /* synchronize throtteling and read callback */ + unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ +@@ -123,6 +127,7 @@ struct acm { + unsigned char clocal; /* termios CLOCAL */ + unsigned int ctrl_caps; /* control capabilities from the class specific header */ + unsigned int susp_count; /* number of suspended interfaces */ ++ struct acm_wb *delayed_wb; /* write queued for a device about to be woken */ + }; + + #define CDC_DATA_INTERFACE_TYPE 0x0a diff --git a/usb/usb-au1xxx-usb-clean-up-ohci-ehci-bus-glue-sources.patch b/usb/usb-au1xxx-usb-clean-up-ohci-ehci-bus-glue-sources.patch new file mode 100644 index 00000000000000..cd4041de667e8a --- /dev/null +++ b/usb/usb-au1xxx-usb-clean-up-ohci-ehci-bus-glue-sources.patch @@ -0,0 +1,742 @@ +From mano@roarinelk.homelinux.net Fri Jun 27 15:16:24 2008 +From: Manuel Lauss <mano@roarinelk.homelinux.net> +Date: Mon, 23 Jun 2008 09:08:29 +0200 +Subject: USB: Au1xxx-usb: clean up ohci/ehci bus glue sources. +To: linux-usb@vger.kernel.org +Message-ID: <20080623070829.GA20861@roarinelk.homelinux.net> +Content-Disposition: inline + + +From: Manuel Lauss <mano@roarinelk.homelinux.net> + +- Fold multiple probe/remove callbacks into one function; +- minor style fixes, no functional changes. + +Tested on Au1200. + +Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net> +Cc: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ehci-au1xxx.c | 276 ++++++++++++++++------------------------- + drivers/usb/host/ohci-au1xxx.c | 268 ++++++++++++++------------------------- + 2 files changed, 210 insertions(+), 334 deletions(-) + +--- a/drivers/usb/host/ehci-au1xxx.c ++++ b/drivers/usb/host/ehci-au1xxx.c +@@ -19,236 +19,179 @@ + #define USB_MCFG_RDCOMB (1<<30) + #define USB_MCFG_SSDEN (1<<23) + #define USB_MCFG_PHYPLLEN (1<<19) ++#define USB_MCFG_UCECLKEN (1<<18) + #define USB_MCFG_EHCCLKEN (1<<17) ++#ifdef CONFIG_DMA_COHERENT + #define USB_MCFG_UCAM (1<<7) ++#else ++#define USB_MCFG_UCAM (0) ++#endif + #define USB_MCFG_EBMEN (1<<3) + #define USB_MCFG_EMEMEN (1<<2) + +-#define USBH_ENABLE_CE (USB_MCFG_PHYPLLEN | USB_MCFG_EHCCLKEN) ++#define USBH_ENABLE_CE (USB_MCFG_PHYPLLEN | USB_MCFG_EHCCLKEN) ++#define USBH_ENABLE_INIT (USB_MCFG_PFEN | USB_MCFG_RDCOMB | \ ++ USBH_ENABLE_CE | USB_MCFG_SSDEN | \ ++ USB_MCFG_UCAM | USB_MCFG_EBMEN | \ ++ USB_MCFG_EMEMEN) + +-#ifdef CONFIG_DMA_COHERENT +-#define USBH_ENABLE_INIT (USBH_ENABLE_CE \ +- | USB_MCFG_PFEN | USB_MCFG_RDCOMB \ +- | USB_MCFG_SSDEN | USB_MCFG_UCAM \ +- | USB_MCFG_EBMEN | USB_MCFG_EMEMEN) +-#else +-#define USBH_ENABLE_INIT (USBH_ENABLE_CE \ +- | USB_MCFG_PFEN | USB_MCFG_RDCOMB \ +- | USB_MCFG_SSDEN \ +- | USB_MCFG_EBMEN | USB_MCFG_EMEMEN) +-#endif + #define USBH_DISABLE (USB_MCFG_EBMEN | USB_MCFG_EMEMEN) + + extern int usb_disabled(void); + +-/*-------------------------------------------------------------------------*/ +- +-static void au1xxx_start_ehc(struct platform_device *dev) ++static void au1xxx_start_ehc(void) + { +- pr_debug(__FILE__ ": starting Au1xxx EHCI USB Controller\n"); +- +- /* write HW defaults again in case Yamon cleared them */ +- if (au_readl(USB_HOST_CONFIG) == 0) { +- au_writel(0x00d02000, USB_HOST_CONFIG); +- au_readl(USB_HOST_CONFIG); +- udelay(1000); +- } +- /* enable host controller */ +- au_writel(USBH_ENABLE_CE | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); +- au_readl(USB_HOST_CONFIG); +- udelay(1000); +- au_writel(USBH_ENABLE_INIT | au_readl(USB_HOST_CONFIG), +- USB_HOST_CONFIG); +- au_readl(USB_HOST_CONFIG); ++ /* enable clock to EHCI block and HS PHY PLL*/ ++ au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_CE, USB_HOST_CONFIG); ++ au_sync(); + udelay(1000); + +- pr_debug(__FILE__ ": Clock to USB host has been enabled\n"); ++ /* enable EHCI mmio */ ++ au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG); ++ au_sync(); ++ udelay(1000); + } + +-static void au1xxx_stop_ehc(struct platform_device *dev) ++static void au1xxx_stop_ehc(void) + { +- pr_debug(__FILE__ ": stopping Au1xxx EHCI USB Controller\n"); ++ unsigned long c; + + /* Disable mem */ +- au_writel(~USBH_DISABLE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); ++ au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_DISABLE, USB_HOST_CONFIG); ++ au_sync(); + udelay(1000); +- /* Disable clock */ +- au_writel(~USB_MCFG_EHCCLKEN & au_readl(USB_HOST_CONFIG), +- USB_HOST_CONFIG); +- au_readl(USB_HOST_CONFIG); ++ ++ /* Disable EHC clock. If the HS PHY is unused disable it too. */ ++ c = au_readl(USB_HOST_CONFIG) & ~USB_MCFG_EHCCLKEN; ++ if (!(c & USB_MCFG_UCECLKEN)) /* UDC disabled? */ ++ c &= ~USB_MCFG_PHYPLLEN; /* yes: disable HS PHY PLL */ ++ au_writel(c, USB_HOST_CONFIG); ++ au_sync(); + } + +-/*-------------------------------------------------------------------------*/ ++static const struct hc_driver ehci_au1xxx_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "Au1xxx EHCI", ++ .hcd_priv_size = sizeof(struct ehci_hcd), ++ ++ /* ++ * generic hardware linkage ++ */ ++ .irq = ehci_irq, ++ .flags = HCD_MEMORY | HCD_USB2, + +-/* configure so an HC device and id are always provided */ +-/* always called with process context; sleeping is OK */ ++ /* ++ * basic lifecycle operations ++ * ++ * FIXME -- ehci_init() doesn't do enough here. ++ * See ehci-ppc-soc for a complete implementation. ++ */ ++ .reset = ehci_init, ++ .start = ehci_run, ++ .stop = ehci_stop, ++ .shutdown = ehci_shutdown, + +-/** +- * usb_ehci_au1xxx_probe - initialize Au1xxx-based HCDs +- * Context: !in_interrupt() +- * +- * Allocates basic resources for this USB host controller, and +- * then invokes the start() method for the HCD associated with it +- * through the hotplug entry's driver_data. +- * +- */ +-int usb_ehci_au1xxx_probe(const struct hc_driver *driver, +- struct usb_hcd **hcd_out, struct platform_device *dev) ++ /* ++ * managing i/o requests and associated device resources ++ */ ++ .urb_enqueue = ehci_urb_enqueue, ++ .urb_dequeue = ehci_urb_dequeue, ++ .endpoint_disable = ehci_endpoint_disable, ++ ++ /* ++ * scheduling support ++ */ ++ .get_frame_number = ehci_get_frame, ++ ++ /* ++ * root hub support ++ */ ++ .hub_status_data = ehci_hub_status_data, ++ .hub_control = ehci_hub_control, ++ .bus_suspend = ehci_bus_suspend, ++ .bus_resume = ehci_bus_resume, ++ .relinquish_port = ehci_relinquish_port, ++ .port_handed_over = ehci_port_handed_over, ++}; ++ ++static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) + { +- int retval; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; ++ int ret; + +-#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT) ++ if (usb_disabled()) ++ return -ENODEV; + ++#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT) + /* Au1200 AB USB does not support coherent memory */ + if (!(read_c0_prid() & 0xff)) { +- pr_info("%s: this is chip revision AB!\n", dev->name); +- pr_info("%s: update your board or re-configure the kernel\n", +- dev->name); ++ printk(KERN_INFO "%s: this is chip revision AB!\n", pdev->name); ++ printk(KERN_INFO "%s: update your board or re-configure" ++ " the kernel\n", pdev->name); + return -ENODEV; + } + #endif + +- au1xxx_start_ehc(dev); +- +- if (dev->resource[1].flags != IORESOURCE_IRQ) { ++ if (pdev->resource[1].flags != IORESOURCE_IRQ) { + pr_debug("resource[1] is not IORESOURCE_IRQ"); +- retval = -ENOMEM; ++ return -ENOMEM; + } +- hcd = usb_create_hcd(driver, &dev->dev, "Au1xxx"); ++ hcd = usb_create_hcd(&ehci_au1xxx_hc_driver, &pdev->dev, "Au1xxx"); + if (!hcd) + return -ENOMEM; +- hcd->rsrc_start = dev->resource[0].start; +- hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; ++ ++ hcd->rsrc_start = pdev->resource[0].start; ++ hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + pr_debug("request_mem_region failed"); +- retval = -EBUSY; ++ ret = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + pr_debug("ioremap failed"); +- retval = -ENOMEM; ++ ret = -ENOMEM; + goto err2; + } + ++ au1xxx_start_ehc(); ++ + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase)); + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = readl(&ehci->caps->hcs_params); + +- /* ehci_hcd_init(hcd_to_ehci(hcd)); */ +- +- retval = +- usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED | IRQF_SHARED); +- if (retval == 0) +- return retval; ++ ret = usb_add_hcd(hcd, pdev->resource[1].start, ++ IRQF_DISABLED | IRQF_SHARED); ++ if (ret == 0) { ++ platform_set_drvdata(pdev, hcd); ++ return ret; ++ } + +- au1xxx_stop_ehc(dev); ++ au1xxx_stop_ehc(); + iounmap(hcd->regs); + err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + err1: + usb_put_hcd(hcd); +- return retval; ++ return ret; + } + +-/* may be called without controller electrically present */ +-/* may be called with controller, bus, and devices active */ +- +-/** +- * usb_ehci_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs +- * @dev: USB Host Controller being removed +- * Context: !in_interrupt() +- * +- * Reverses the effect of usb_ehci_hcd_au1xxx_probe(), first invoking +- * the HCD's stop() method. It is always called from a thread +- * context, normally "rmmod", "apmd", or something similar. +- * +- */ +-void usb_ehci_au1xxx_remove(struct usb_hcd *hcd, struct platform_device *dev) ++static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev) + { ++ struct usb_hcd *hcd = platform_get_drvdata(pdev); ++ + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); +- au1xxx_stop_ehc(dev); +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static const struct hc_driver ehci_au1xxx_hc_driver = { +- .description = hcd_name, +- .product_desc = "Au1xxx EHCI", +- .hcd_priv_size = sizeof(struct ehci_hcd), +- +- /* +- * generic hardware linkage +- */ +- .irq = ehci_irq, +- .flags = HCD_MEMORY | HCD_USB2, +- +- /* +- * basic lifecycle operations +- * +- * FIXME -- ehci_init() doesn't do enough here. +- * See ehci-ppc-soc for a complete implementation. +- */ +- .reset = ehci_init, +- .start = ehci_run, +- .stop = ehci_stop, +- .shutdown = ehci_shutdown, +- +- /* +- * managing i/o requests and associated device resources +- */ +- .urb_enqueue = ehci_urb_enqueue, +- .urb_dequeue = ehci_urb_dequeue, +- .endpoint_disable = ehci_endpoint_disable, +- +- /* +- * scheduling support +- */ +- .get_frame_number = ehci_get_frame, ++ au1xxx_stop_ehc(); ++ platform_set_drvdata(pdev, NULL); + +- /* +- * root hub support +- */ +- .hub_status_data = ehci_hub_status_data, +- .hub_control = ehci_hub_control, +- .bus_suspend = ehci_bus_suspend, +- .bus_resume = ehci_bus_resume, +- .relinquish_port = ehci_relinquish_port, +- .port_handed_over = ehci_port_handed_over, +-}; +- +-/*-------------------------------------------------------------------------*/ +- +-static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) +-{ +- struct usb_hcd *hcd = NULL; +- int ret; +- +- pr_debug("In ehci_hcd_au1xxx_drv_probe\n"); +- +- if (usb_disabled()) +- return -ENODEV; +- +- /* FIXME we only want one one probe() not two */ +- ret = usb_ehci_au1xxx_probe(&ehci_au1xxx_hc_driver, &hcd, pdev); +- return ret; +-} +- +-static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev) +-{ +- struct usb_hcd *hcd = platform_get_drvdata(pdev); +- +- /* FIXME we only want one one remove() not two */ +- usb_ehci_au1xxx_remove(hcd, pdev); + return 0; + } + +@@ -268,14 +211,17 @@ static int ehci_hcd_au1xxx_drv_resume(st + return 0; + } + */ +-MODULE_ALIAS("platform:au1xxx-ehci"); ++ + static struct platform_driver ehci_hcd_au1xxx_driver = { +- .probe = ehci_hcd_au1xxx_drv_probe, +- .remove = ehci_hcd_au1xxx_drv_remove, +- .shutdown = usb_hcd_platform_shutdown, ++ .probe = ehci_hcd_au1xxx_drv_probe, ++ .remove = ehci_hcd_au1xxx_drv_remove, ++ .shutdown = usb_hcd_platform_shutdown, + /*.suspend = ehci_hcd_au1xxx_drv_suspend, */ + /*.resume = ehci_hcd_au1xxx_drv_resume, */ + .driver = { +- .name = "au1xxx-ehci", ++ .name = "au1xxx-ehci", ++ .owner = THIS_MODULE, + } + }; ++ ++MODULE_ALIAS("platform:au1xxx-ehci"); +--- a/drivers/usb/host/ohci-au1xxx.c ++++ b/drivers/usb/host/ohci-au1xxx.c +@@ -34,7 +34,8 @@ + #ifdef __LITTLE_ENDIAN + #define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C) + #elif __BIG_ENDIAN +-#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | USBH_ENABLE_BE) ++#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | \ ++ USBH_ENABLE_BE) + #else + #error not byte order defined + #endif +@@ -46,213 +47,87 @@ + #define USB_MCFG_RDCOMB (1<<30) + #define USB_MCFG_SSDEN (1<<23) + #define USB_MCFG_OHCCLKEN (1<<16) ++#ifdef CONFIG_DMA_COHERENT + #define USB_MCFG_UCAM (1<<7) ++#else ++#define USB_MCFG_UCAM (0) ++#endif + #define USB_MCFG_OBMEN (1<<1) + #define USB_MCFG_OMEMEN (1<<0) + + #define USBH_ENABLE_CE USB_MCFG_OHCCLKEN +-#ifdef CONFIG_DMA_COHERENT +-#define USBH_ENABLE_INIT (USB_MCFG_OHCCLKEN \ +- | USB_MCFG_PFEN | USB_MCFG_RDCOMB \ +- | USB_MCFG_SSDEN | USB_MCFG_UCAM \ +- | USB_MCFG_OBMEN | USB_MCFG_OMEMEN) +-#else +-#define USBH_ENABLE_INIT (USB_MCFG_OHCCLKEN \ +- | USB_MCFG_PFEN | USB_MCFG_RDCOMB \ +- | USB_MCFG_SSDEN \ +- | USB_MCFG_OBMEN | USB_MCFG_OMEMEN) +-#endif ++ ++#define USBH_ENABLE_INIT (USB_MCFG_PFEN | USB_MCFG_RDCOMB | \ ++ USBH_ENABLE_CE | USB_MCFG_SSDEN | \ ++ USB_MCFG_UCAM | \ ++ USB_MCFG_OBMEN | USB_MCFG_OMEMEN) ++ + #define USBH_DISABLE (USB_MCFG_OBMEN | USB_MCFG_OMEMEN) + + #endif /* Au1200 */ + + extern int usb_disabled(void); + +-/*-------------------------------------------------------------------------*/ +- +-static void au1xxx_start_ohc(struct platform_device *dev) ++static void au1xxx_start_ohc(void) + { +- printk(KERN_DEBUG __FILE__ +- ": starting Au1xxx OHCI USB Controller\n"); +- + /* enable host controller */ +- + #ifndef CONFIG_SOC_AU1200 +- + au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG); +- udelay(1000); +- au_writel(USBH_ENABLE_INIT, USB_HOST_CONFIG); ++ au_sync(); + udelay(1000); + +-#else /* Au1200 */ +- +- /* write HW defaults again in case Yamon cleared them */ +- if (au_readl(USB_HOST_CONFIG) == 0) { +- au_writel(0x00d02000, USB_HOST_CONFIG); +- au_readl(USB_HOST_CONFIG); +- udelay(1000); +- } +- au_writel(USBH_ENABLE_CE | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); +- au_readl(USB_HOST_CONFIG); ++ au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG); ++ au_sync(); + udelay(1000); +- au_writel(USBH_ENABLE_INIT | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); +- au_readl(USB_HOST_CONFIG); +- udelay(1000); +- +-#endif /* Au1200 */ + +-#ifndef CONFIG_SOC_AU1200 + /* wait for reset complete (read register twice; see au1500 errata) */ + while (au_readl(USB_HOST_CONFIG), + !(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD)) +-#endif + udelay(1000); + +- printk(KERN_DEBUG __FILE__ +- ": Clock to USB host has been enabled \n"); +-} +- +-static void au1xxx_stop_ohc(struct platform_device *dev) +-{ +- printk(KERN_DEBUG __FILE__ +- ": stopping Au1xxx OHCI USB Controller\n"); +- +-#ifndef CONFIG_SOC_AU1200 +- +- /* Disable clock */ +- au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG); +- + #else /* Au1200 */ +- +- /* Disable mem */ +- au_writel(~USBH_DISABLE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); ++ au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_CE, USB_HOST_CONFIG); ++ au_sync(); + udelay(1000); +- /* Disable clock */ +- au_writel(~USBH_ENABLE_CE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); +- au_readl(USB_HOST_CONFIG); ++ ++ au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG); ++ au_sync(); ++ udelay(2000); + #endif /* Au1200 */ + } + +- +-/*-------------------------------------------------------------------------*/ +- +-/* configure so an HC device and id are always provided */ +-/* always called with process context; sleeping is OK */ +- +- +-/** +- * usb_ohci_au1xxx_probe - initialize Au1xxx-based HCDs +- * Context: !in_interrupt() +- * +- * Allocates basic resources for this USB host controller, and +- * then invokes the start() method for the HCD associated with it +- * through the hotplug entry's driver_data. +- * +- */ +-static int usb_ohci_au1xxx_probe(const struct hc_driver *driver, +- struct platform_device *dev) ++static void au1xxx_stop_ohc(void) + { +- int retval; +- struct usb_hcd *hcd; +- +-#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT) +- /* Au1200 AB USB does not support coherent memory */ +- if (!(read_c0_prid() & 0xff)) { +- pr_info("%s: this is chip revision AB !!\n", +- dev->name); +- pr_info("%s: update your board or re-configure the kernel\n", +- dev->name); +- return -ENODEV; +- } ++#ifdef CONFIG_SOC_AU1200 ++ /* Disable mem */ ++ au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_DISABLE, USB_HOST_CONFIG); ++ au_sync(); ++ udelay(1000); + #endif +- +- if (dev->resource[1].flags != IORESOURCE_IRQ) { +- pr_debug("resource[1] is not IORESOURCE_IRQ\n"); +- return -ENOMEM; +- } +- +- hcd = usb_create_hcd(driver, &dev->dev, "au1xxx"); +- if (!hcd) +- return -ENOMEM; +- hcd->rsrc_start = dev->resource[0].start; +- hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; +- +- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { +- pr_debug("request_mem_region failed\n"); +- retval = -EBUSY; +- goto err1; +- } +- +- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); +- if (!hcd->regs) { +- pr_debug("ioremap failed\n"); +- retval = -ENOMEM; +- goto err2; +- } +- +- au1xxx_start_ohc(dev); +- ohci_hcd_init(hcd_to_ohci(hcd)); +- +- retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED | IRQF_SHARED); +- if (retval == 0) +- return retval; +- +- au1xxx_stop_ohc(dev); +- iounmap(hcd->regs); +- err2: +- release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +- err1: +- usb_put_hcd(hcd); +- return retval; +-} +- +- +-/* may be called without controller electrically present */ +-/* may be called with controller, bus, and devices active */ +- +-/** +- * usb_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs +- * @dev: USB Host Controller being removed +- * Context: !in_interrupt() +- * +- * Reverses the effect of usb_hcd_au1xxx_probe(), first invoking +- * the HCD's stop() method. It is always called from a thread +- * context, normally "rmmod", "apmd", or something similar. +- * +- */ +-static void usb_ohci_au1xxx_remove(struct usb_hcd *hcd, struct platform_device *dev) +-{ +- usb_remove_hcd(hcd); +- au1xxx_stop_ohc(dev); +- iounmap(hcd->regs); +- release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +- usb_put_hcd(hcd); ++ /* Disable clock */ ++ au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG); ++ au_sync(); + } + +-/*-------------------------------------------------------------------------*/ +- +-static int __devinit +-ohci_au1xxx_start (struct usb_hcd *hcd) ++static int __devinit ohci_au1xxx_start(struct usb_hcd *hcd) + { +- struct ohci_hcd *ohci = hcd_to_ohci (hcd); +- int ret; ++ struct ohci_hcd *ohci = hcd_to_ohci(hcd); ++ int ret; + +- ohci_dbg (ohci, "ohci_au1xxx_start, ohci:%p", ohci); ++ ohci_dbg(ohci, "ohci_au1xxx_start, ohci:%p", ohci); + +- if ((ret = ohci_init (ohci)) < 0) ++ if ((ret = ohci_init(ohci)) < 0) + return ret; + +- if ((ret = ohci_run (ohci)) < 0) { ++ if ((ret = ohci_run(ohci)) < 0) { + err ("can't start %s", hcd->self.bus_name); +- ohci_stop (hcd); ++ ohci_stop(hcd); + return ret; + } + + return 0; + } + +-/*-------------------------------------------------------------------------*/ +- + static const struct hc_driver ohci_au1xxx_hc_driver = { + .description = hcd_name, + .product_desc = "Au1xxx OHCI", +@@ -295,18 +170,66 @@ static const struct hc_driver ohci_au1xx + .start_port_reset = ohci_start_port_reset, + }; + +-/*-------------------------------------------------------------------------*/ +- + static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev) + { + int ret; +- +- pr_debug ("In ohci_hcd_au1xxx_drv_probe"); ++ struct usb_hcd *hcd; + + if (usb_disabled()) + return -ENODEV; + +- ret = usb_ohci_au1xxx_probe(&ohci_au1xxx_hc_driver, pdev); ++#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT) ++ /* Au1200 AB USB does not support coherent memory */ ++ if (!(read_c0_prid() & 0xff)) { ++ printk(KERN_INFO "%s: this is chip revision AB !!\n", ++ pdev->name); ++ printk(KERN_INFO "%s: update your board or re-configure " ++ "the kernel\n", pdev->name); ++ return -ENODEV; ++ } ++#endif ++ ++ if (pdev->resource[1].flags != IORESOURCE_IRQ) { ++ pr_debug("resource[1] is not IORESOURCE_IRQ\n"); ++ return -ENOMEM; ++ } ++ ++ hcd = usb_create_hcd(&ohci_au1xxx_hc_driver, &pdev->dev, "au1xxx"); ++ if (!hcd) ++ return -ENOMEM; ++ ++ hcd->rsrc_start = pdev->resource[0].start; ++ hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; ++ ++ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { ++ pr_debug("request_mem_region failed\n"); ++ ret = -EBUSY; ++ goto err1; ++ } ++ ++ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); ++ if (!hcd->regs) { ++ pr_debug("ioremap failed\n"); ++ ret = -ENOMEM; ++ goto err2; ++ } ++ ++ au1xxx_start_ohc(); ++ ohci_hcd_init(hcd_to_ohci(hcd)); ++ ++ ret = usb_add_hcd(hcd, pdev->resource[1].start, ++ IRQF_DISABLED | IRQF_SHARED); ++ if (ret == 0) { ++ platform_set_drvdata(pdev, hcd); ++ return ret; ++ } ++ ++ au1xxx_stop_ohc(); ++ iounmap(hcd->regs); ++err2: ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++err1: ++ usb_put_hcd(hcd); + return ret; + } + +@@ -314,9 +237,16 @@ static int ohci_hcd_au1xxx_drv_remove(st + { + struct usb_hcd *hcd = platform_get_drvdata(pdev); + +- usb_ohci_au1xxx_remove(hcd, pdev); ++ usb_remove_hcd(hcd); ++ au1xxx_stop_ohc(); ++ iounmap(hcd->regs); ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++ usb_put_hcd(hcd); ++ platform_set_drvdata(pdev, NULL); ++ + return 0; + } ++ + /*TBD*/ + /*static int ohci_hcd_au1xxx_drv_suspend(struct platform_device *dev) + { diff --git a/usb/usb-au1xxx-usb-suspend-resume-support.patch b/usb/usb-au1xxx-usb-suspend-resume-support.patch new file mode 100644 index 00000000000000..6a0807d4d0f216 --- /dev/null +++ b/usb/usb-au1xxx-usb-suspend-resume-support.patch @@ -0,0 +1,242 @@ +From mano@roarinelk.homelinux.net Fri Jun 27 15:16:57 2008 +From: Manuel Lauss <mano@roarinelk.homelinux.net> +Date: Mon, 23 Jun 2008 09:09:37 +0200 +Subject: USB: Au1xxx-usb: suspend/resume support. +To: linux-usb@vger.kernel.org +Message-ID: <20080623070937.GB20861@roarinelk.homelinux.net> +Content-Disposition: inline + + +From: Manuel Lauss <mano@roarinelk.homelinux.net> + +Copy the OHCI/EHCI PM callbacks of the PCI implementation since +they work equally well on Au1xxx hardware. + +Tested on Au1200. + +Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net> +Cc: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ehci-au1xxx.c | 115 +++++++++++++++++++++++++++++++++++++---- + drivers/usb/host/ohci-au1xxx.c | 59 +++++++++++++++++---- + 2 files changed, 155 insertions(+), 19 deletions(-) + +--- a/drivers/usb/host/ehci-au1xxx.c ++++ b/drivers/usb/host/ehci-au1xxx.c +@@ -195,29 +195,124 @@ static int ehci_hcd_au1xxx_drv_remove(st + return 0; + } + +- /*TBD*/ +-/*static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) ++#ifdef CONFIG_PM ++static int ehci_hcd_au1xxx_drv_suspend(struct platform_device *pdev, ++ pm_message_t message) + { +- struct platform_device *pdev = to_platform_device(dev); +- struct usb_hcd *hcd = dev_get_drvdata(dev); ++ struct usb_hcd *hcd = platform_get_drvdata(pdev); ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ unsigned long flags; ++ int rc; + + return 0; ++ rc = 0; ++ ++ if (time_before(jiffies, ehci->next_statechange)) ++ msleep(10); ++ ++ /* Root hub was already suspended. Disable irq emission and ++ * mark HW unaccessible, bail out if RH has been resumed. Use ++ * the spinlock to properly synchronize with possible pending ++ * RH suspend or resume activity. ++ * ++ * This is still racy as hcd->state is manipulated outside of ++ * any locks =P But that will be a different fix. ++ */ ++ spin_lock_irqsave(&ehci->lock, flags); ++ if (hcd->state != HC_STATE_SUSPENDED) { ++ rc = -EINVAL; ++ goto bail; ++ } ++ ehci_writel(ehci, 0, &ehci->regs->intr_enable); ++ (void)ehci_readl(ehci, &ehci->regs->intr_enable); ++ ++ /* make sure snapshot being resumed re-enumerates everything */ ++ if (message.event == PM_EVENT_PRETHAW) { ++ ehci_halt(ehci); ++ ehci_reset(ehci); ++ } ++ ++ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); ++ ++ au1xxx_stop_ehc(); ++ ++bail: ++ spin_unlock_irqrestore(&ehci->lock, flags); ++ ++ // could save FLADJ in case of Vaux power loss ++ // ... we'd only use it to handle clock skew ++ ++ return rc; + } +-static int ehci_hcd_au1xxx_drv_resume(struct device *dev) ++ ++ ++static int ehci_hcd_au1xxx_drv_resume(struct platform_device *pdev) + { +- struct platform_device *pdev = to_platform_device(dev); +- struct usb_hcd *hcd = dev_get_drvdata(dev); ++ struct usb_hcd *hcd = platform_get_drvdata(pdev); ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ ++ au1xxx_start_ehc(); ++ ++ // maybe restore FLADJ ++ ++ if (time_before(jiffies, ehci->next_statechange)) ++ msleep(100); ++ ++ /* Mark hardware accessible again as we are out of D3 state by now */ ++ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); ++ ++ /* If CF is still set, we maintained PCI Vaux power. ++ * Just undo the effect of ehci_pci_suspend(). ++ */ ++ if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { ++ int mask = INTR_MASK; ++ ++ if (!hcd->self.root_hub->do_remote_wakeup) ++ mask &= ~STS_PCD; ++ ehci_writel(ehci, mask, &ehci->regs->intr_enable); ++ ehci_readl(ehci, &ehci->regs->intr_enable); ++ return 0; ++ } ++ ++ ehci_dbg(ehci, "lost power, restarting\n"); ++ usb_root_hub_lost_power(hcd->self.root_hub); ++ ++ /* Else reset, to cope with power loss or flush-to-storage ++ * style "resume" having let BIOS kick in during reboot. ++ */ ++ (void) ehci_halt(ehci); ++ (void) ehci_reset(ehci); ++ ++ /* emptying the schedule aborts any urbs */ ++ spin_lock_irq(&ehci->lock); ++ if (ehci->reclaim) ++ end_unlink_async(ehci); ++ ehci_work(ehci); ++ spin_unlock_irq(&ehci->lock); ++ ++ ehci_writel(ehci, ehci->command, &ehci->regs->command); ++ ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); ++ ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ ++ ++ /* here we "know" root ports should always stay powered */ ++ ehci_port_power(ehci, 1); ++ ++ hcd->state = HC_STATE_SUSPENDED; + + return 0; + } +-*/ ++ ++#else ++#define ehci_hcd_au1xxx_drv_suspend NULL ++#define ehci_hcd_au1xxx_drv_resume NULL ++#endif + + static struct platform_driver ehci_hcd_au1xxx_driver = { + .probe = ehci_hcd_au1xxx_drv_probe, + .remove = ehci_hcd_au1xxx_drv_remove, + .shutdown = usb_hcd_platform_shutdown, +- /*.suspend = ehci_hcd_au1xxx_drv_suspend, */ +- /*.resume = ehci_hcd_au1xxx_drv_resume, */ ++ .suspend = ehci_hcd_au1xxx_drv_suspend, ++ .resume = ehci_hcd_au1xxx_drv_resume, + .driver = { + .name = "au1xxx-ehci", + .owner = THIS_MODULE, +--- a/drivers/usb/host/ohci-au1xxx.c ++++ b/drivers/usb/host/ohci-au1xxx.c +@@ -247,27 +247,68 @@ static int ohci_hcd_au1xxx_drv_remove(st + return 0; + } + +- /*TBD*/ +-/*static int ohci_hcd_au1xxx_drv_suspend(struct platform_device *dev) ++#ifdef CONFIG_PM ++static int ohci_hcd_au1xxx_drv_suspend(struct platform_device *pdev, ++ pm_message_t message) + { +- struct usb_hcd *hcd = platform_get_drvdata(dev); ++ struct usb_hcd *hcd = platform_get_drvdata(pdev); ++ struct ohci_hcd *ohci = hcd_to_ohci(hcd); ++ unsigned long flags; ++ int rc; + +- return 0; ++ rc = 0; ++ ++ /* Root hub was already suspended. Disable irq emission and ++ * mark HW unaccessible, bail out if RH has been resumed. Use ++ * the spinlock to properly synchronize with possible pending ++ * RH suspend or resume activity. ++ * ++ * This is still racy as hcd->state is manipulated outside of ++ * any locks =P But that will be a different fix. ++ */ ++ spin_lock_irqsave(&ohci->lock, flags); ++ if (hcd->state != HC_STATE_SUSPENDED) { ++ rc = -EINVAL; ++ goto bail; ++ } ++ ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); ++ (void)ohci_readl(ohci, &ohci->regs->intrdisable); ++ ++ /* make sure snapshot being resumed re-enumerates everything */ ++ if (message.event == PM_EVENT_PRETHAW) ++ ohci_usb_reset(ohci); ++ ++ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); ++ ++ au1xxx_stop_ohc(); ++bail: ++ spin_unlock_irqrestore(&ohci->lock, flags); ++ ++ return rc; + } +-static int ohci_hcd_au1xxx_drv_resume(struct platform_device *dev) ++ ++static int ohci_hcd_au1xxx_drv_resume(struct platform_device *pdev) + { +- struct usb_hcd *hcd = platform_get_drvdata(dev); ++ struct usb_hcd *hcd = platform_get_drvdata(pdev); ++ ++ au1xxx_start_ohc(); ++ ++ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); ++ ohci_finish_controller_resume(hcd); + + return 0; + } +-*/ ++#else ++#define ohci_hcd_au1xxx_drv_suspend NULL ++#define ohci_hcd_au1xxx_drv_resume NULL ++#endif + + static struct platform_driver ohci_hcd_au1xxx_driver = { + .probe = ohci_hcd_au1xxx_drv_probe, + .remove = ohci_hcd_au1xxx_drv_remove, + .shutdown = usb_hcd_platform_shutdown, +- /*.suspend = ohci_hcd_au1xxx_drv_suspend, */ +- /*.resume = ohci_hcd_au1xxx_drv_resume, */ ++ .suspend = ohci_hcd_au1xxx_drv_suspend, ++ .resume = ohci_hcd_au1xxx_drv_resume, + .driver = { + .name = "au1xxx-ohci", + .owner = THIS_MODULE, diff --git a/usb/usb-autosuspend-for-cdc-wdm.patch b/usb/usb-autosuspend-for-cdc-wdm.patch new file mode 100644 index 00000000000000..7f40dd6459b6fc --- /dev/null +++ b/usb/usb-autosuspend-for-cdc-wdm.patch @@ -0,0 +1,215 @@ +From oliver@neukum.org Fri Jun 27 14:38:34 2008 +From: Oliver Neukum <oliver@neukum.org> +Date: Tue, 24 Jun 2008 15:56:10 +0200 +Subject: USB: autosuspend for cdc-wdm +To: Greg KH <greg@kroah.com>, linux-usb@vger.kernel.org +Message-ID: <200806241556.11242.oliver@neukum.org> +Content-Disposition: inline + +this patch implements + +- suspend/resume +- aggressive autosuspend for the cdc-wdm driver +- pre/post_reset + +Signed-off-by: Oliver Neukum <oneukum@suse.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/class/cdc-wdm.c | 107 +++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 101 insertions(+), 6 deletions(-) + +--- a/drivers/usb/class/cdc-wdm.c ++++ b/drivers/usb/class/cdc-wdm.c +@@ -88,6 +88,7 @@ struct wdm_device { + dma_addr_t ihandle; + struct mutex wlock; + struct mutex rlock; ++ struct mutex plock; + wait_queue_head_t wait; + struct work_struct rxwork; + int werr; +@@ -248,6 +249,7 @@ exit: + + static void kill_urbs(struct wdm_device *desc) + { ++ /* the order here is essential */ + usb_kill_urb(desc->command); + usb_kill_urb(desc->validity); + usb_kill_urb(desc->response); +@@ -300,6 +302,9 @@ static ssize_t wdm_write + if (r) + goto outnl; + ++ r = usb_autopm_get_interface(desc->intf); ++ if (r < 0) ++ goto outnp; + r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, + &desc->flags)); + if (r < 0) +@@ -354,6 +359,8 @@ static ssize_t wdm_write + req->wIndex); + } + out: ++ usb_autopm_put_interface(desc->intf); ++outnp: + mutex_unlock(&desc->wlock); + outnl: + return rv < 0 ? rv : count; +@@ -378,6 +385,11 @@ retry: + rv = wait_event_interruptible(desc->wait, + test_bit(WDM_READ, &desc->flags)); + ++ if (test_bit(WDM_DISCONNECTING, &desc->flags)) { ++ rv = -ENODEV; ++ goto err; ++ } ++ usb_mark_last_busy(interface_to_usbdev(desc->intf)); + if (rv < 0) { + rv = -ERESTARTSYS; + goto err; +@@ -485,18 +497,28 @@ static int wdm_open(struct inode *inode, + if (test_bit(WDM_DISCONNECTING, &desc->flags)) + goto out; + +- desc->count++; ++ ; + file->private_data = desc; + +- rv = usb_submit_urb(desc->validity, GFP_KERNEL); +- ++ rv = usb_autopm_get_interface(desc->intf); + if (rv < 0) { +- desc->count--; +- err("Error submitting int urb - %d", rv); ++ err("Error autopm - %d", rv); + goto out; + } +- rv = 0; ++ intf->needs_remote_wakeup = 1; + ++ mutex_lock(&desc->plock); ++ if (!desc->count++) { ++ rv = usb_submit_urb(desc->validity, GFP_KERNEL); ++ if (rv < 0) { ++ desc->count--; ++ err("Error submitting int urb - %d", rv); ++ } ++ } else { ++ rv = 0; ++ } ++ mutex_unlock(&desc->plock); ++ usb_autopm_put_interface(desc->intf); + out: + mutex_unlock(&wdm_mutex); + return rv; +@@ -507,10 +529,15 @@ static int wdm_release(struct inode *ino + struct wdm_device *desc = file->private_data; + + mutex_lock(&wdm_mutex); ++ mutex_lock(&desc->plock); + desc->count--; ++ mutex_unlock(&desc->plock); ++ + if (!desc->count) { + dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); + kill_urbs(desc); ++ if (!test_bit(WDM_DISCONNECTING, &desc->flags)) ++ desc->intf->needs_remote_wakeup = 0; + } + mutex_unlock(&wdm_mutex); + return 0; +@@ -602,6 +629,7 @@ next_desc: + goto out; + mutex_init(&desc->wlock); + mutex_init(&desc->rlock); ++ mutex_init(&desc->plock); + spin_lock_init(&desc->iuspin); + init_waitqueue_head(&desc->wait); + desc->wMaxCommand = maxcom; +@@ -703,6 +731,7 @@ static void wdm_disconnect(struct usb_in + spin_lock_irqsave(&desc->iuspin, flags); + set_bit(WDM_DISCONNECTING, &desc->flags); + set_bit(WDM_READ, &desc->flags); ++ /* to terminate pending flushes */ + clear_bit(WDM_IN_USE, &desc->flags); + spin_unlock_irqrestore(&desc->iuspin, flags); + cancel_work_sync(&desc->rxwork); +@@ -713,11 +742,77 @@ static void wdm_disconnect(struct usb_in + mutex_unlock(&wdm_mutex); + } + ++static int wdm_suspend(struct usb_interface *intf, pm_message_t message) ++{ ++ struct wdm_device *desc = usb_get_intfdata(intf); ++ int rv = 0; ++ ++ dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); ++ ++ mutex_lock(&desc->plock); ++ if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) { ++ rv = -EBUSY; ++ } else { ++ cancel_work_sync(&desc->rxwork); ++ kill_urbs(desc); ++ } ++ mutex_unlock(&desc->plock); ++ ++ return rv; ++} ++ ++static int recover_from_urb_loss(struct wdm_device *desc) ++{ ++ int rv = 0; ++ ++ if (desc->count) { ++ rv = usb_submit_urb(desc->validity, GFP_NOIO); ++ if (rv < 0) ++ err("Error resume submitting int urb - %d", rv); ++ } ++ return rv; ++} ++static int wdm_resume(struct usb_interface *intf) ++{ ++ struct wdm_device *desc = usb_get_intfdata(intf); ++ int rv; ++ ++ dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); ++ mutex_lock(&desc->plock); ++ rv = recover_from_urb_loss(desc); ++ mutex_unlock(&desc->plock); ++ return rv; ++} ++ ++static int wdm_pre_reset(struct usb_interface *intf) ++{ ++ struct wdm_device *desc = usb_get_intfdata(intf); ++ ++ mutex_lock(&desc->plock); ++ return 0; ++} ++ ++static int wdm_post_reset(struct usb_interface *intf) ++{ ++ struct wdm_device *desc = usb_get_intfdata(intf); ++ int rv; ++ ++ rv = recover_from_urb_loss(desc); ++ mutex_unlock(&desc->plock); ++ return 0; ++} ++ + static struct usb_driver wdm_driver = { + .name = "cdc_wdm", + .probe = wdm_probe, + .disconnect = wdm_disconnect, ++ .suspend = wdm_suspend, ++ .resume = wdm_resume, ++ .reset_resume = wdm_resume, ++ .pre_reset = wdm_pre_reset, ++ .post_reset = wdm_post_reset, + .id_table = wdm_ids, ++ .supports_autosuspend = 1, + }; + + /* --- low level module stuff --- */ diff --git a/usb/usb-debug-port-converter-does-not-accept-more-than-8-byte-packets.patch b/usb/usb-debug-port-converter-does-not-accept-more-than-8-byte-packets.patch new file mode 100644 index 00000000000000..d9363e925826f6 --- /dev/null +++ b/usb/usb-debug-port-converter-does-not-accept-more-than-8-byte-packets.patch @@ -0,0 +1,55 @@ +From dared1st@yahoo.com Fri Jun 27 14:42:45 2008 +From: Aleksey Gorelov <dared1st@yahoo.com> +Date: Thu, 19 Jun 2008 15:22:17 -0700 (PDT) +Subject: USB: debug port converter does not accept more than 8 byte packets +To: linux-usb-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org +Cc: greg@kroah.com +Message-ID: <911755.27146.qm@web83307.mail.sp1.yahoo.com> + + +USB debug port only supports 8 byte rx/tx packets. Although spec implies that +"if a packet larger than eight bytes is received from the remote computer, the +device must break the larger packet into eight-byte packets before sending the +data to the Debug Port", the real PLX NET20DC device does not handle it right - +data is corrupted on debug port end if serial interface sends >8 byte urbs. +Patch below fixes the issue by limiting tx urb to 8 byte. + +Signed off by: Aleks Gorelov <dared1st@yahoo.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/usb_debug.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/drivers/usb/serial/usb_debug.c ++++ b/drivers/usb/serial/usb_debug.c +@@ -15,6 +15,8 @@ + #include <linux/usb.h> + #include <linux/usb/serial.h> + ++#define USB_DEBUG_MAX_PACKET_SIZE 8 ++ + static struct usb_device_id id_table [] = { + { USB_DEVICE(0x0525, 0x127a) }, + { }, +@@ -29,6 +31,12 @@ static struct usb_driver debug_driver = + .no_dynamic_id = 1, + }; + ++int usb_debug_open(struct usb_serial_port *port, struct file *filp) ++{ ++ port->bulk_out_size = USB_DEBUG_MAX_PACKET_SIZE; ++ return usb_serial_generic_open(port, filp); ++} ++ + static struct usb_serial_driver debug_device = { + .driver = { + .owner = THIS_MODULE, +@@ -36,6 +44,7 @@ static struct usb_serial_driver debug_de + }, + .id_table = id_table, + .num_ports = 1, ++ .open = usb_debug_open, + }; + + static int __init debug_init(void) diff --git a/usb/usb-fix-disconnect-bug-in-cdc-acm.patch b/usb/usb-fix-disconnect-bug-in-cdc-acm.patch new file mode 100644 index 00000000000000..20cb2aea26c74c --- /dev/null +++ b/usb/usb-fix-disconnect-bug-in-cdc-acm.patch @@ -0,0 +1,88 @@ +From oliver@neukum.org Fri Jun 27 14:39:17 2008 +From: Oliver Neukum <oliver@neukum.org> +Date: Wed, 25 Jun 2008 14:17:16 +0200 +Subject: USB: fix disconnect bug in cdc-acm +To: Greg KH <greg@kroah.com>, linux-usb@vger.kernel.org +Message-ID: <200806251417.17070.oliver@neukum.org> +Content-Disposition: inline + + +cdc-acm must give up secondary interfaces if the primary is disconnected +and vice versa. This wasn't done correctly. + +Signed-off-by: Oliver Neukum <oneukum@suse.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/usb/class/cdc-acm.c | 28 +++++++++++++++------------- + 1 file changed, 15 insertions(+), 13 deletions(-) + +--- a/drivers/usb/class/cdc-acm.c ++++ b/drivers/usb/class/cdc-acm.c +@@ -838,7 +838,7 @@ static void acm_tty_set_termios(struct t + * USB probe and disconnect routines. + */ + +-/* Little helper: write buffers free */ ++/* Little helpers: write/read buffers free */ + static void acm_write_buffers_free(struct acm *acm) + { + int i; +@@ -849,6 +849,15 @@ static void acm_write_buffers_free(struc + } + } + ++static void acm_read_buffers_free(struct acm *acm) ++{ ++ struct usb_device *usb_dev = interface_to_usbdev(acm->control); ++ int i, n = acm->rx_buflimit; ++ ++ for (i = 0; i < n; i++) ++ usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); ++} ++ + /* Little helper: write buffers allocate */ + static int acm_write_buffers_alloc(struct acm *acm) + { +@@ -1171,8 +1180,7 @@ alloc_fail8: + for (i = 0; i < ACM_NW; i++) + usb_free_urb(acm->wb[i].urb); + alloc_fail7: +- for (i = 0; i < num_rx_buf; i++) +- usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); ++ acm_read_buffers_free(acm); + for (i = 0; i < num_rx_buf; i++) + usb_free_urb(acm->ru[i].urb); + usb_free_urb(acm->ctrlurb); +@@ -1209,15 +1217,9 @@ static void acm_disconnect(struct usb_in + { + struct acm *acm = usb_get_intfdata(intf); + struct usb_device *usb_dev = interface_to_usbdev(intf); +- int i; +- +- if (!acm || !acm->dev) { +- dbg("disconnect on nonexisting interface"); +- return; +- } + + mutex_lock(&open_mutex); +- if (!usb_get_intfdata(intf)) { ++ if (!acm || !acm->dev) { + mutex_unlock(&open_mutex); + return; + } +@@ -1236,10 +1238,10 @@ static void acm_disconnect(struct usb_in + + acm_write_buffers_free(acm); + usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); +- for (i = 0; i < acm->rx_buflimit; i++) +- usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); ++ acm_read_buffers_free(acm); + +- usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf); ++ usb_driver_release_interface(&acm_driver, intf == acm->control ? ++ acm->data : acm->control); + + if (!acm->used) { + acm_tty_unregister(acm); diff --git a/usb/usb-fix-uninitialized-variable-warning-in-keyspan_pda.patch b/usb/usb-fix-uninitialized-variable-warning-in-keyspan_pda.patch new file mode 100644 index 00000000000000..c738d0cb9cd9b2 --- /dev/null +++ b/usb/usb-fix-uninitialized-variable-warning-in-keyspan_pda.patch @@ -0,0 +1,29 @@ +From bhalevy@panasas.com Fri Jun 27 14:35:37 2008 +From: Benny Halevy <bhalevy@panasas.com> +Date: Fri, 27 Jun 2008 12:22:32 +0300 +Subject: usb: fix uninitialized variable warning in keyspan_pda +To: Alan Cox <alan@lxorguk.ukuu.org.uk> +Cc: Greg KH <greg@kroah.com>, linux-kernel@vger.kernel.org +Message-ID: <4864B158.6080301@panasas.com> + +From: Benny Halevy <bhalevy@panasas.com> + +This fixes the compiler warning. + +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/keyspan_pda.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/serial/keyspan_pda.c ++++ b/drivers/usb/serial/keyspan_pda.c +@@ -420,7 +420,7 @@ static int keyspan_pda_get_modem_info(st + 3, /* get pins */ + USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_IN, + 0, 0, &data, 1, 2000); +- if (rc > 0) ++ if (rc >= 0) + *value = data; + return rc; + } diff --git a/usb/usb-fix-usb-serial-pm-counter-decrement-for-disconnected-interfaces.patch b/usb/usb-fix-usb-serial-pm-counter-decrement-for-disconnected-interfaces.patch new file mode 100644 index 00000000000000..fa409c21807a4d --- /dev/null +++ b/usb/usb-fix-usb-serial-pm-counter-decrement-for-disconnected-interfaces.patch @@ -0,0 +1,38 @@ +From oliver@neukum.org Fri Jun 27 14:38:53 2008 +From: Oliver Neukum <oliver@neukum.org> +Date: Wed, 25 Jun 2008 13:32:49 +0200 +Subject: USB: fix usb serial pm counter decrement for disconnected interfaces +To: Greg KH <greg@kroah.com>, linux-usb@vger.kernel.org +Message-ID: <200806251332.49797.oliver@neukum.org> +Content-Disposition: inline + + +usb serial decrements the pm counter even if an interface has been +disconnected. If it was a logical disconnect the interface may belong +already to another driver. This patch introduces a check for disconnected +interfaces. + +Signed-off-by: Oliver Neukum <oneukum@suse.de> +Cc: Stable <stable@kernel.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + +--- + drivers/usb/serial/usb-serial.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/usb/serial/usb-serial.c ++++ b/drivers/usb/serial/usb-serial.c +@@ -283,7 +283,10 @@ static void serial_close(struct tty_stru + } + + if (port->open_count == 0) { +- usb_autopm_put_interface(port->serial->interface); ++ mutex_lock(&port->serial->disc_mutex); ++ if (!port->serial->disconnected) ++ usb_autopm_put_interface(port->serial->interface); ++ mutex_unlock(&port->serial->disc_mutex); + module_put(port->serial->type->driver.owner); + } + diff --git a/usb/usb-force-unbinding-of-drivers-lacking-reset_resume-or-other-methods.patch b/usb/usb-force-unbinding-of-drivers-lacking-reset_resume-or-other-methods.patch new file mode 100644 index 00000000000000..da6aff2866ff38 --- /dev/null +++ b/usb/usb-force-unbinding-of-drivers-lacking-reset_resume-or-other-methods.patch @@ -0,0 +1,319 @@ +From stern@rowland.harvard.edu Fri Jun 27 15:14:03 2008 +From: Alan Stern <stern@rowland.harvard.edu> +Date: Mon, 23 Jun 2008 16:00:40 -0400 (EDT) +Subject: USB: Force unbinding of drivers lacking reset_resume or other methods +To: Greg KH <greg@kroah.com> +Cc: USB list <linux-usb@vger.kernel.org> +Message-ID: <Pine.LNX.4.44L0.0806231558370.30973-100000@iolanthe.rowland.org> + + +This patch (as1024) takes care of a FIXME issue: Drivers that don't +have the necessary suspend, resume, reset_resume, pre_reset, or +post_reset methods will be unbound and their interface reprobed when +one of the unsupported events occurs. + +This is made slightly more difficult by the fact that bind operations +won't work during a system sleep transition. So instead the code has +to defer the operation until the transition ends. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/core/driver.c | 131 ++++++++++++++++++++++++++++++++++++++++------ + drivers/usb/core/hub.c | 27 +++++++-- + drivers/usb/core/usb.h | 2 + include/linux/usb.h | 1 + 4 files changed, 140 insertions(+), 21 deletions(-) + +--- a/drivers/usb/core/driver.c ++++ b/drivers/usb/core/driver.c +@@ -201,6 +201,7 @@ static int usb_probe_interface(struct de + + intf = to_usb_interface(dev); + udev = interface_to_usbdev(intf); ++ intf->needs_binding = 0; + + if (udev->authorized == 0) { + dev_err(&intf->dev, "Device is not authorized for usage\n"); +@@ -311,6 +312,7 @@ int usb_driver_claim_interface(struct us + + dev->driver = &driver->drvwrap.driver; + usb_set_intfdata(iface, priv); ++ iface->needs_binding = 0; + + usb_pm_lock(udev); + iface->condition = USB_INTERFACE_BOUND; +@@ -772,6 +774,104 @@ void usb_deregister(struct usb_driver *d + } + EXPORT_SYMBOL_GPL(usb_deregister); + ++ ++/* Forced unbinding of a USB interface driver, either because ++ * it doesn't support pre_reset/post_reset/reset_resume or ++ * because it doesn't support suspend/resume. ++ * ++ * The caller must hold @intf's device's lock, but not its pm_mutex ++ * and not @intf->dev.sem. ++ */ ++void usb_forced_unbind_intf(struct usb_interface *intf) ++{ ++ struct usb_driver *driver = to_usb_driver(intf->dev.driver); ++ ++ dev_dbg(&intf->dev, "forced unbind\n"); ++ usb_driver_release_interface(driver, intf); ++ ++ /* Mark the interface for later rebinding */ ++ intf->needs_binding = 1; ++} ++ ++/* Delayed forced unbinding of a USB interface driver and scan ++ * for rebinding. ++ * ++ * The caller must hold @intf's device's lock, but not its pm_mutex ++ * and not @intf->dev.sem. ++ * ++ * FIXME: The caller must block system sleep transitions. ++ */ ++void usb_rebind_intf(struct usb_interface *intf) ++{ ++ int rc; ++ ++ /* Delayed unbind of an existing driver */ ++ if (intf->dev.driver) { ++ struct usb_driver *driver = ++ to_usb_driver(intf->dev.driver); ++ ++ dev_dbg(&intf->dev, "forced unbind\n"); ++ usb_driver_release_interface(driver, intf); ++ } ++ ++ /* Try to rebind the interface */ ++ intf->needs_binding = 0; ++ rc = device_attach(&intf->dev); ++ if (rc < 0) ++ dev_warn(&intf->dev, "rebind failed: %d\n", rc); ++} ++ ++#define DO_UNBIND 0 ++#define DO_REBIND 1 ++ ++/* Unbind drivers for @udev's interfaces that don't support suspend/resume, ++ * or rebind interfaces that have been unbound, according to @action. ++ * ++ * The caller must hold @udev's device lock. ++ * FIXME: For rebinds, the caller must block system sleep transitions. ++ */ ++static void do_unbind_rebind(struct usb_device *udev, int action) ++{ ++ struct usb_host_config *config; ++ int i; ++ struct usb_interface *intf; ++ struct usb_driver *drv; ++ ++ config = udev->actconfig; ++ if (config) { ++ for (i = 0; i < config->desc.bNumInterfaces; ++i) { ++ intf = config->interface[i]; ++ switch (action) { ++ case DO_UNBIND: ++ if (intf->dev.driver) { ++ drv = to_usb_driver(intf->dev.driver); ++ if (!drv->suspend || !drv->resume) ++ usb_forced_unbind_intf(intf); ++ } ++ break; ++ case DO_REBIND: ++ if (intf->needs_binding) { ++ ++ /* FIXME: The next line is needed because we are going to probe ++ * the interface, but as far as the PM core is concerned the ++ * interface is still suspended. The problem wouldn't exist ++ * if we could rebind the interface during the interface's own ++ * resume() call, but at the time the usb_device isn't locked! ++ * ++ * The real solution will be to carry this out during the device's ++ * complete() callback. Until that is implemented, we have to ++ * use this hack. ++ */ ++ intf->dev.power.sleeping = 0; ++ ++ usb_rebind_intf(intf); ++ } ++ break; ++ } ++ } ++ } ++} ++ + #ifdef CONFIG_PM + + /* Caller has locked udev's pm_mutex */ +@@ -841,7 +941,7 @@ static int usb_suspend_interface(struct + goto done; + driver = to_usb_driver(intf->dev.driver); + +- if (driver->suspend && driver->resume) { ++ if (driver->suspend) { + status = driver->suspend(intf, msg); + if (status == 0) + mark_quiesced(intf); +@@ -849,12 +949,10 @@ static int usb_suspend_interface(struct + dev_err(&intf->dev, "%s error %d\n", + "suspend", status); + } else { +- /* +- * FIXME else if there's no suspend method, disconnect... +- * Not possible if auto_pm is set... +- */ +- dev_warn(&intf->dev, "no suspend for driver %s?\n", +- driver->name); ++ /* Later we will unbind the driver and reprobe */ ++ intf->needs_binding = 1; ++ dev_warn(&intf->dev, "no %s for driver %s?\n", ++ "suspend", driver->name); + mark_quiesced(intf); + } + +@@ -878,10 +976,12 @@ static int usb_resume_interface(struct u + goto done; + + /* Can't resume it if it doesn't have a driver. */ +- if (intf->condition == USB_INTERFACE_UNBOUND) { +- status = -ENOTCONN; ++ if (intf->condition == USB_INTERFACE_UNBOUND) ++ goto done; ++ ++ /* Don't resume if the interface is marked for rebinding */ ++ if (intf->needs_binding) + goto done; +- } + driver = to_usb_driver(intf->dev.driver); + + if (reset_resume) { +@@ -891,7 +991,7 @@ static int usb_resume_interface(struct u + dev_err(&intf->dev, "%s error %d\n", + "reset_resume", status); + } else { +- /* status = -EOPNOTSUPP; */ ++ intf->needs_binding = 1; + dev_warn(&intf->dev, "no %s for driver %s?\n", + "reset_resume", driver->name); + } +@@ -902,7 +1002,7 @@ static int usb_resume_interface(struct u + dev_err(&intf->dev, "%s error %d\n", + "resume", status); + } else { +- /* status = -EOPNOTSUPP; */ ++ intf->needs_binding = 1; + dev_warn(&intf->dev, "no %s for driver %s?\n", + "resume", driver->name); + } +@@ -910,11 +1010,10 @@ static int usb_resume_interface(struct u + + done: + dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status); +- if (status == 0) ++ if (status == 0 && intf->condition == USB_INTERFACE_BOUND) + mark_active(intf); + +- /* FIXME: Unbind the driver and reprobe if the resume failed +- * (not possible if auto_pm is set) */ ++ /* Later we will unbind the driver and/or reprobe, if necessary */ + return status; + } + +@@ -1470,6 +1569,7 @@ int usb_external_suspend_device(struct u + { + int status; + ++ do_unbind_rebind(udev, DO_UNBIND); + usb_pm_lock(udev); + udev->auto_pm = 0; + status = usb_suspend_both(udev, msg); +@@ -1497,6 +1597,7 @@ int usb_external_resume_device(struct us + status = usb_resume_both(udev); + udev->last_busy = jiffies; + usb_pm_unlock(udev); ++ do_unbind_rebind(udev, DO_REBIND); + + /* Now that the device is awake, we can start trying to autosuspend + * it again. */ +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -3357,6 +3357,11 @@ re_enumerate: + * this from a driver probe() routine after downloading new firmware. + * For calls that might not occur during probe(), drivers should lock + * the device using usb_lock_device_for_reset(). ++ * ++ * If an interface is currently being probed or disconnected, we assume ++ * its driver knows how to handle resets. For all other interfaces, ++ * if the driver doesn't have pre_reset and post_reset methods then ++ * we attempt to unbind it and rebind afterward. + */ + int usb_reset_device(struct usb_device *udev) + { +@@ -3378,12 +3383,17 @@ int usb_reset_device(struct usb_device * + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + struct usb_interface *cintf = config->interface[i]; + struct usb_driver *drv; ++ int unbind = 0; + + if (cintf->dev.driver) { + drv = to_usb_driver(cintf->dev.driver); +- if (drv->pre_reset) +- (drv->pre_reset)(cintf); +- /* FIXME: Unbind if pre_reset returns an error or isn't defined */ ++ if (drv->pre_reset && drv->post_reset) ++ unbind = (drv->pre_reset)(cintf); ++ else if (cintf->condition == ++ USB_INTERFACE_BOUND) ++ unbind = 1; ++ if (unbind) ++ usb_forced_unbind_intf(cintf); + } + } + } +@@ -3394,13 +3404,18 @@ int usb_reset_device(struct usb_device * + for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) { + struct usb_interface *cintf = config->interface[i]; + struct usb_driver *drv; ++ int rebind = cintf->needs_binding; + +- if (cintf->dev.driver) { ++ if (!rebind && cintf->dev.driver) { + drv = to_usb_driver(cintf->dev.driver); + if (drv->post_reset) +- (drv->post_reset)(cintf); +- /* FIXME: Unbind if post_reset returns an error or isn't defined */ ++ rebind = (drv->post_reset)(cintf); ++ else if (cintf->condition == ++ USB_INTERFACE_BOUND) ++ rebind = 1; + } ++ if (rebind) ++ usb_rebind_intf(cintf); + } + } + +--- a/drivers/usb/core/usb.h ++++ b/drivers/usb/core/usb.h +@@ -29,6 +29,8 @@ extern int usb_choose_configuration(stru + extern void usb_kick_khubd(struct usb_device *dev); + extern int usb_match_device(struct usb_device *dev, + const struct usb_device_id *id); ++extern void usb_forced_unbind_intf(struct usb_interface *intf); ++extern void usb_rebind_intf(struct usb_interface *intf); + + extern int usb_hub_init(void); + extern void usb_hub_cleanup(void); +--- a/include/linux/usb.h ++++ b/include/linux/usb.h +@@ -160,6 +160,7 @@ struct usb_interface { + unsigned is_active:1; /* the interface is not suspended */ + unsigned sysfs_files_created:1; /* the sysfs attributes exist */ + unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ ++ unsigned needs_binding:1; /* needs delayed unbind/rebind */ + + struct device dev; /* interface specific device info */ + struct device *usb_dev; diff --git a/usb/usb-keyspan-remove-duplicate-device-entries.patch b/usb/usb-keyspan-remove-duplicate-device-entries.patch new file mode 100644 index 00000000000000..fe57697bb15ecc --- /dev/null +++ b/usb/usb-keyspan-remove-duplicate-device-entries.patch @@ -0,0 +1,47 @@ +From ben.collins@canonical.com Fri Jun 27 14:37:22 2008 +From: Ben Collins <ben.collins@canonical.com> +Date: Thu, 26 Jun 2008 20:08:16 -0400 +Subject: USB: keyspan: Remove duplicate device entries +To: gregkh <gregkh@suse.de> +Message-ID: <1214525296.7150.120.camel@cunning> + + +The 28xb, as documented in comments, has the same ID's as the 28x. +Remove the duplicated ID's from the device tables, and expand the +comment to document this. + +Signed-off-by: Ben Collins <ben.collins@canonical.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/keyspan.h | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/drivers/usb/serial/keyspan.h ++++ b/drivers/usb/serial/keyspan.h +@@ -222,7 +222,8 @@ struct ezusb_hex_record { + + /* Product IDs post-renumeration. Note that the 28x and 28xb + have the same id's post-renumeration but behave identically +- so it's not an issue. */ ++ so it's not an issue. As such, the 28xb is not listed in any ++ of the device tables. */ + #define keyspan_usa18x_product_id 0x0112 + #define keyspan_usa19_product_id 0x0107 + #define keyspan_usa19qi_product_id 0x010c +@@ -566,7 +567,6 @@ static struct usb_device_id keyspan_ids_ + { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) }, + { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) }, + { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) }, +- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_product_id) }, + { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) }, + { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id)}, + { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)}, +@@ -616,7 +616,6 @@ static struct usb_device_id keyspan_2por + { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) }, + { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) }, + { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) }, +- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_product_id) }, + { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) }, + { } /* Terminating entry */ + }; diff --git a/usb/usb-make-pxa27x_udc-less-invasive-on-up2ocr-pxa-register.patch b/usb/usb-make-pxa27x_udc-less-invasive-on-up2ocr-pxa-register.patch new file mode 100644 index 00000000000000..38f1407c819ddd --- /dev/null +++ b/usb/usb-make-pxa27x_udc-less-invasive-on-up2ocr-pxa-register.patch @@ -0,0 +1,44 @@ +From david-b@pacbell.net Fri Jun 27 15:12:21 2008 +From: Robert Jarzmik <rjarzmik@free.fr> +Date: Tue, 10 Jun 2008 15:01:15 -0700 +Subject: USB: Make pxa27x_udc less invasive on UP2OCR pxa register. +To: Robert Jarzmik <rjarzmik@free.fr> +Cc: linux-usb@vger.kernel.org +Message-ID: <200806101501.15282.david-b@pacbell.net> +Content-Disposition: inline + +From: Robert Jarzmik <rjarzmik@free.fr> + +UP2OCR registers sets up usb client function, as well as +other board specific setups (resistors pullups and +pulldowns). Let the driver setup the "non-OTG client" part, +and let all other bits as they were set by board code. + +Signed-off-by: Robert Jarzmik <rjarzmik@free.fr> +Acked-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/pxa27x_udc.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +--- a/drivers/usb/gadget/pxa27x_udc.c ++++ b/drivers/usb/gadget/pxa27x_udc.c +@@ -1573,9 +1573,16 @@ static __init void udc_init_data(struct + */ + static void udc_enable(struct pxa_udc *udc) + { ++ u32 up2ocr; ++ + udc_writel(udc, UDCICR0, 0); + udc_writel(udc, UDCICR1, 0); +- udc_writel(udc, UP2OCR, UP2OCR_HXOE); ++ /* ++ * UP2OCR : set HXOE=1, HXS=0, SEOS=0 => non-OTG client ++ */ ++ up2ocr = udc_readl(udc, UP2OCR); ++ up2ocr = (up2ocr & ~(UP2OCR_HXS - 1)) | UP2OCR_HXOE; ++ udc_writel(udc, UP2OCR, up2ocr); + udc_clear_mask_UDCCR(udc, UDCCR_UDE); + + clk_enable(udc->clk); diff --git a/usb/usb-ohci-pnx4008-i2c-cleanups-and-fixes.patch b/usb/usb-ohci-pnx4008-i2c-cleanups-and-fixes.patch new file mode 100644 index 00000000000000..e70eefc0a99a51 --- /dev/null +++ b/usb/usb-ohci-pnx4008-i2c-cleanups-and-fixes.patch @@ -0,0 +1,101 @@ +From khali@linux-fr.org Fri Jun 27 15:11:56 2008 +From: Jean Delvare <khali@linux-fr.org> +Date: Wed, 18 Jun 2008 14:46:27 +0200 +Subject: USB: ohci-pnx4008: I2C cleanups and fixes +To: Vitaly Wool <vitalywool@gmail.com> +Cc: David Brownell <david-b@pacbell.net>, linux-usb@vger.kernel.org +Message-ID: <20080618144627.7cdfd5d3@hyperion.delvare> + + +Various cleanups and fixes to the i2c code in ohci-pnx4008: +* Delete empty isp1301_command. The i2c driver command implementation + is optional, so there's no point in providing an empty + implementation. +* Give a name to isp1301_driver. I'm surprised that i2c-core accepted + to register this driver at all. I've chosen "isp1301_pnx" as the + name, because it's not a generic ISP1301 driver (much like the + isp1301_omap driver.) We might want to make the name even more + specific (but "isp1301_ohci_pnx4008" doesn't fit.) +* The ISP1301 is definitely not a hardware monitoring device. +* Fix a memory leak on failure in isp1301_attach. If + i2c_attach_client fails, the client is not registered so + isp1301_detach is never called and the i2c_client memory is lost. +* Use strlcpy instead of strcpy. + +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Cc: Vitaly Wool <vitalywool@gmail.com> +Cc: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ohci-pnx4008.c | 26 ++++++++++++-------------- + 1 file changed, 12 insertions(+), 14 deletions(-) + +--- a/drivers/usb/host/ohci-pnx4008.c ++++ b/drivers/usb/host/ohci-pnx4008.c +@@ -109,8 +109,6 @@ static struct clk *usb_clk; + + static int isp1301_probe(struct i2c_adapter *adap); + static int isp1301_detach(struct i2c_client *client); +-static int isp1301_command(struct i2c_client *client, unsigned int cmd, +- void *arg); + + static const unsigned short normal_i2c[] = + { ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END }; +@@ -123,30 +121,37 @@ static struct i2c_client_address_data ad + }; + + struct i2c_driver isp1301_driver = { +- .class = I2C_CLASS_HWMON, ++ .driver = { ++ .name = "isp1301_pnx", ++ }, + .attach_adapter = isp1301_probe, + .detach_client = isp1301_detach, +- .command = isp1301_command + }; + + static int isp1301_attach(struct i2c_adapter *adap, int addr, int kind) + { + struct i2c_client *c; ++ int err; + + c = kzalloc(sizeof(*c), GFP_KERNEL); +- + if (!c) + return -ENOMEM; + +- strcpy(c->name, "isp1301"); ++ strlcpy(c->name, "isp1301_pnx", I2C_NAME_SIZE); + c->flags = 0; + c->addr = addr; + c->adapter = adap; + c->driver = &isp1301_driver; + ++ err = i2c_attach_client(c); ++ if (err) { ++ kfree(c); ++ return err; ++ } ++ + isp1301_i2c_client = c; + +- return i2c_attach_client(c); ++ return 0; + } + + static int isp1301_probe(struct i2c_adapter *adap) +@@ -161,13 +166,6 @@ static int isp1301_detach(struct i2c_cli + return 0; + } + +-/* No commands defined */ +-static int isp1301_command(struct i2c_client *client, unsigned int cmd, +- void *arg) +-{ +- return 0; +-} +- + static void i2c_write(u8 buf, u8 subaddr) + { + char tmpbuf[2]; diff --git a/usb/usb-r8a66597-hcd-fix-iinterval-for-full-low-speed-device.patch b/usb/usb-r8a66597-hcd-fix-iinterval-for-full-low-speed-device.patch new file mode 100644 index 00000000000000..21d87f8b42ad5f --- /dev/null +++ b/usb/usb-r8a66597-hcd-fix-iinterval-for-full-low-speed-device.patch @@ -0,0 +1,65 @@ +From shimoda.yoshihiro@renesas.com Fri Jun 27 14:35:17 2008 +From: Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> +Date: Fri, 27 Jun 2008 19:09:58 +0900 +Subject: usb: r8a66597-hcd: fix iinterval for Full/Low speed device +To: gregkh@suse.de +Cc: linux-usb@vger.kernel.org +Message-ID: <4864BC76.7040004@renesas.com> + + +fix interrupt transfer interval for Full/Low speed device. + +Signed-off-by: Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/r8a66597-hcd.c | 31 +++++++++++++++++++++++++++---- + 1 file changed, 27 insertions(+), 4 deletions(-) + +--- a/drivers/usb/host/r8a66597-hcd.c ++++ b/drivers/usb/host/r8a66597-hcd.c +@@ -863,6 +863,32 @@ static void disable_r8a66597_pipe_all(st + dev->dma_map = 0; + } + ++static u16 get_interval(struct urb *urb, __u8 interval) ++{ ++ u16 time = 1; ++ int i; ++ ++ if (urb->dev->speed == USB_SPEED_HIGH) { ++ if (interval > IITV) ++ time = IITV; ++ else ++ time = interval ? interval - 1 : 0; ++ } else { ++ if (interval > 128) { ++ time = IITV; ++ } else { ++ /* calculate the nearest value for PIPEPERI */ ++ for (i = 0; i < 7; i++) { ++ if ((1 << i) < interval && ++ (1 << (i + 1) > interval)) ++ time = 1 << i; ++ } ++ } ++ } ++ ++ return time; ++} ++ + static unsigned long get_timer_interval(struct urb *urb, __u8 interval) + { + __u8 i; +@@ -901,10 +927,7 @@ static void init_pipe_info(struct r8a665 + info.interval = 0; + info.timer_interval = 0; + } else { +- if (ep->bInterval > IITV) +- info.interval = IITV; +- else +- info.interval = ep->bInterval ? ep->bInterval - 1 : 0; ++ info.interval = get_interval(urb, ep->bInterval); + info.timer_interval = get_timer_interval(urb, ep->bInterval); + } + if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) diff --git a/usb/usb-r8a66597-hcd-fix-interrupt-trigger.patch b/usb/usb-r8a66597-hcd-fix-interrupt-trigger.patch new file mode 100644 index 00000000000000..bf4409bac2813b --- /dev/null +++ b/usb/usb-r8a66597-hcd-fix-interrupt-trigger.patch @@ -0,0 +1,41 @@ +From shimoda.yoshihiro@renesas.com Fri Jun 27 14:35:05 2008 +From: Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> +Date: Fri, 27 Jun 2008 19:09:55 +0900 +Subject: usb: r8a66597-hcd: fix interrupt trigger +To: gregkh@suse.de +Cc: linux-usb@vger.kernel.org +Message-ID: <4864BC73.4050607@renesas.com> + + +fix the problem that did not set IRQF_TRIGGER_ flag. + +Signed-off-by: Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/r8a66597-hcd.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/usb/host/r8a66597-hcd.c ++++ b/drivers/usb/host/r8a66597-hcd.c +@@ -2244,6 +2244,7 @@ static int __init r8a66597_probe(struct + struct r8a66597 *r8a66597; + int ret = 0; + int i; ++ unsigned long irq_trigger; + + if (pdev->dev.dma_mask) { + ret = -EINVAL; +@@ -2302,7 +2303,11 @@ static int __init r8a66597_probe(struct + INIT_LIST_HEAD(&r8a66597->child_device); + + hcd->rsrc_start = res->start; +- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); ++ if (irq_sense == INTL) ++ irq_trigger = IRQF_TRIGGER_LOW; ++ else ++ irq_trigger = IRQF_TRIGGER_FALLING; ++ ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger); + if (ret != 0) { + err("Failed to add hcd"); + goto clean_up; diff --git a/usb/usbfs-don-t-store-bad-pointers-in-registration.patch b/usb/usbfs-don-t-store-bad-pointers-in-registration.patch new file mode 100644 index 00000000000000..560709639fa273 --- /dev/null +++ b/usb/usbfs-don-t-store-bad-pointers-in-registration.patch @@ -0,0 +1,57 @@ +From stern@rowland.harvard.edu Fri Jun 27 15:15:45 2008 +From: Alan Stern <stern@rowland.harvard.edu> +Date: Tue, 24 Jun 2008 14:47:29 -0400 (EDT) +Subject: usbfs: don't store bad pointers in registration +To: Greg KH <greg@kroah.com> +Cc: USB list <linux-usb@vger.kernel.org> +Message-ID: <Pine.LNX.4.44L0.0806241444500.2166-100000@iolanthe.rowland.org> + + +This patch (as1107) fixes a small bug in the usbfs registration and +unregistration code. It avoids leaving an error value stored in the +device's usb_classdev field and it avoids trying to unregister a NULL +pointer. (It also fixes a rather extreme overuse of whitespace.) + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/core/devio.c | 21 +++++++++------------ + 1 file changed, 9 insertions(+), 12 deletions(-) + +--- a/drivers/usb/core/devio.c ++++ b/drivers/usb/core/devio.c +@@ -1724,24 +1724,21 @@ static struct class *usb_classdev_class; + + static int usb_classdev_add(struct usb_device *dev) + { +- int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1); +- +- dev->usb_classdev = device_create_drvdata(usb_classdev_class, +- &dev->dev, +- MKDEV(USB_DEVICE_MAJOR, +- minor), NULL, +- "usbdev%d.%d", +- dev->bus->busnum, +- dev->devnum); +- if (IS_ERR(dev->usb_classdev)) +- return PTR_ERR(dev->usb_classdev); ++ struct device *cldev; + ++ cldev = device_create_drvdata(usb_classdev_class, ++ &dev->dev, dev->dev.devt, NULL, ++ "usbdev%d.%d", dev->bus->busnum, dev->devnum); ++ if (IS_ERR(cldev)) ++ return PTR_ERR(cldev); ++ dev->usb_classdev = cldev; + return 0; + } + + static void usb_classdev_remove(struct usb_device *dev) + { +- device_unregister(dev->usb_classdev); ++ if (dev->usb_classdev) ++ device_unregister(dev->usb_classdev); + usb_fs_classdev_common_remove(dev); + } + diff --git a/usb/usbfs-fix-race-between-open-and-unregister.patch b/usb/usbfs-fix-race-between-open-and-unregister.patch new file mode 100644 index 00000000000000..585ad3b7d4662b --- /dev/null +++ b/usb/usbfs-fix-race-between-open-and-unregister.patch @@ -0,0 +1,70 @@ +From stern@rowland.harvard.edu Fri Jun 27 15:15:27 2008 +From: Alan Stern <stern@rowland.harvard.edu> +Date: Tue, 24 Jun 2008 14:47:19 -0400 (EDT) +Subject: usbfs: fix race between open and unregister +To: Greg KH <greg@kroah.com> +Cc: USB list <linux-usb@vger.kernel.org> +Message-ID: <Pine.LNX.4.44L0.0806241443260.2166-100000@iolanthe.rowland.org> + + +This patch (as1106) fixes a race between opening and unregistering +device files in usbfs. The current code drops its reference to the +device and then reacquires it, ignoring the possibility that the +device structure might have been removed in the meantime. It also +doesn't check whether the device is already in the NOTATTACHED state +when the file is opened. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/core/devio.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +--- a/drivers/usb/core/devio.c ++++ b/drivers/usb/core/devio.c +@@ -562,7 +562,6 @@ static struct usb_device *usbdev_lookup_ + dev = bus_find_device(&usb_bus_type, NULL, (void *) devt, match_devt); + if (!dev) + return NULL; +- put_device(dev); + return container_of(dev, struct usb_device, dev); + } + +@@ -590,16 +589,21 @@ static int usbdev_open(struct inode *ino + dev = usbdev_lookup_by_devt(inode->i_rdev); + #ifdef CONFIG_USB_DEVICEFS + /* procfs file */ +- if (!dev) ++ if (!dev) { + dev = inode->i_private; ++ if (dev && dev->usbfs_dentry && ++ dev->usbfs_dentry->d_inode == inode) ++ usb_get_dev(dev); ++ else ++ dev = NULL; ++ } + #endif +- if (!dev) ++ if (!dev || dev->state == USB_STATE_NOTATTACHED) + goto out; + ret = usb_autoresume_device(dev); + if (ret) + goto out; + +- usb_get_dev(dev); + ret = 0; + ps->dev = dev; + ps->file = file; +@@ -619,8 +623,10 @@ static int usbdev_open(struct inode *ino + list_add_tail(&ps->list, &dev->filelist); + file->private_data = ps; + out: +- if (ret) ++ if (ret) { + kfree(ps); ++ usb_put_dev(dev); ++ } + mutex_unlock(&usbfs_mutex); + return ret; + } diff --git a/usb/usbfs-send-disconnect-signals-when-device-is-unregistered.patch b/usb/usbfs-send-disconnect-signals-when-device-is-unregistered.patch new file mode 100644 index 00000000000000..8fcfb7d15eddd8 --- /dev/null +++ b/usb/usbfs-send-disconnect-signals-when-device-is-unregistered.patch @@ -0,0 +1,161 @@ +From stern@rowland.harvard.edu Fri Jun 27 15:14:42 2008 +From: Alan Stern <stern@rowland.harvard.edu> +Date: Tue, 24 Jun 2008 14:47:04 -0400 (EDT) +Subject: usbfs: send disconnect signals when device is unregistered +To: Greg KH <greg@kroah.com> +Cc: USB list <linux-usb@vger.kernel.org> +Message-ID: <Pine.LNX.4.44L0.0806241441310.2166-100000@iolanthe.rowland.org> + + +USB device files are accessible in two ways: as files in usbfs and as +character device nodes. The two paths are supposed to behave +identically, but they don't. When the underlying USB device is +unplugged, disconnect signals are sent to processes with open usbfs +files (if they requested these signals) but not to processes with open +device node files. + +This patch (as1104) fixes the bug by moving the disconnect-signalling +code into a common subroutine which is called from both paths. +Putting this subroutine in devio.c removes the only out-of-file +reference to struct dev_state, and so the structure's declaration can +be moved from usb.h into devio.c. + +Finally, the new subroutine performs one extra action: It kills all +the outstanding async URBs. (I'd kill the outstanding synchronous +URBs too, if there was any way to do it.) In the past this hasn't +mattered much, because devices were unregistered from usbfs only +when they were disconnected. But now the unregistration can also +occur whenever devices are unbound from the usb_generic driver. At +any rate, killing URBs when a device is unregistered from usbfs seems +like a good thing to do. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/core/devio.c | 39 +++++++++++++++++++++++++++++++++++++++ + drivers/usb/core/inode.c | 16 +--------------- + drivers/usb/core/usb.h | 17 +---------------- + 3 files changed, 41 insertions(+), 31 deletions(-) + +--- a/drivers/usb/core/devio.c ++++ b/drivers/usb/core/devio.c +@@ -59,6 +59,22 @@ + /* Mutual exclusion for removal, open, and release */ + DEFINE_MUTEX(usbfs_mutex); + ++struct dev_state { ++ struct list_head list; /* state list */ ++ struct usb_device *dev; ++ struct file *file; ++ spinlock_t lock; /* protects the async urb lists */ ++ struct list_head async_pending; ++ struct list_head async_completed; ++ wait_queue_head_t wait; /* wake up if a request completed */ ++ unsigned int discsignr; ++ struct pid *disc_pid; ++ uid_t disc_uid, disc_euid; ++ void __user *disccontext; ++ unsigned long ifclaimed; ++ u32 secid; ++}; ++ + struct async { + struct list_head asynclist; + struct dev_state *ps; +@@ -1678,6 +1694,28 @@ const struct file_operations usbdev_file + .release = usbdev_release, + }; + ++void usb_fs_classdev_common_remove(struct usb_device *udev) ++{ ++ struct dev_state *ps; ++ struct siginfo sinfo; ++ ++ while (!list_empty(&udev->filelist)) { ++ ps = list_entry(udev->filelist.next, struct dev_state, list); ++ destroy_all_async(ps); ++ wake_up_all(&ps->wait); ++ list_del_init(&ps->list); ++ if (ps->discsignr) { ++ sinfo.si_signo = ps->discsignr; ++ sinfo.si_errno = EPIPE; ++ sinfo.si_code = SI_ASYNCIO; ++ sinfo.si_addr = ps->disccontext; ++ kill_pid_info_as_uid(ps->discsignr, &sinfo, ++ ps->disc_pid, ps->disc_uid, ++ ps->disc_euid, ps->secid); ++ } ++ } ++} ++ + #ifdef CONFIG_USB_DEVICE_CLASS + static struct class *usb_classdev_class; + +@@ -1701,6 +1739,7 @@ static int usb_classdev_add(struct usb_d + static void usb_classdev_remove(struct usb_device *dev) + { + device_unregister(dev->usb_classdev); ++ usb_fs_classdev_common_remove(dev); + } + + static int usb_classdev_notify(struct notifier_block *self, +--- a/drivers/usb/core/inode.c ++++ b/drivers/usb/core/inode.c +@@ -712,25 +712,11 @@ static void usbfs_add_device(struct usb_ + + static void usbfs_remove_device(struct usb_device *dev) + { +- struct dev_state *ds; +- struct siginfo sinfo; +- + if (dev->usbfs_dentry) { + fs_remove_file (dev->usbfs_dentry); + dev->usbfs_dentry = NULL; + } +- while (!list_empty(&dev->filelist)) { +- ds = list_entry(dev->filelist.next, struct dev_state, list); +- wake_up_all(&ds->wait); +- list_del_init(&ds->list); +- if (ds->discsignr) { +- sinfo.si_signo = ds->discsignr; +- sinfo.si_errno = EPIPE; +- sinfo.si_code = SI_ASYNCIO; +- sinfo.si_addr = ds->disccontext; +- kill_pid_info_as_uid(ds->discsignr, &sinfo, ds->disc_pid, ds->disc_uid, ds->disc_euid, ds->secid); +- } +- } ++ usb_fs_classdev_common_remove(dev); + } + + static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev) +--- a/drivers/usb/core/usb.h ++++ b/drivers/usb/core/usb.h +@@ -142,26 +142,11 @@ extern struct usb_driver usbfs_driver; + extern const struct file_operations usbfs_devices_fops; + extern const struct file_operations usbdev_file_operations; + extern void usbfs_conn_disc_event(void); ++extern void usb_fs_classdev_common_remove(struct usb_device *udev); + + extern int usb_devio_init(void); + extern void usb_devio_cleanup(void); + +-struct dev_state { +- struct list_head list; /* state list */ +- struct usb_device *dev; +- struct file *file; +- spinlock_t lock; /* protects the async urb lists */ +- struct list_head async_pending; +- struct list_head async_completed; +- wait_queue_head_t wait; /* wake up if a request completed */ +- unsigned int discsignr; +- struct pid *disc_pid; +- uid_t disc_uid, disc_euid; +- void __user *disccontext; +- unsigned long ifclaimed; +- u32 secid; +-}; +- + /* internal notify stuff */ + extern void usb_notify_add_device(struct usb_device *udev); + extern void usb_notify_remove_device(struct usb_device *udev); diff --git a/usb/usbfs-simplify-the-lookup-by-minor-routines.patch b/usb/usbfs-simplify-the-lookup-by-minor-routines.patch new file mode 100644 index 00000000000000..822752eb4bcbeb --- /dev/null +++ b/usb/usbfs-simplify-the-lookup-by-minor-routines.patch @@ -0,0 +1,60 @@ +From stern@rowland.harvard.edu Fri Jun 27 15:15:09 2008 +From: Alan Stern <stern@rowland.harvard.edu> +Date: Tue, 24 Jun 2008 14:47:12 -0400 (EDT) +Subject: usbfs: simplify the lookup-by-minor routines +To: Greg KH <greg@kroah.com> +Cc: USB list <linux-usb@vger.kernel.org> +Message-ID: <Pine.LNX.4.44L0.0806241442410.2166-100000@iolanthe.rowland.org> + + +This patch (as1105) simplifies the lookup-by-minor-number code in +usbfs. Instead of passing the minor number to the callback, which +must then reconstruct the entire dev_t value, the patch passes the +dev_t value directly. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/core/devio.c | 15 ++++++--------- + 1 file changed, 6 insertions(+), 9 deletions(-) + +--- a/drivers/usb/core/devio.c ++++ b/drivers/usb/core/devio.c +@@ -550,20 +550,16 @@ static int check_ctrlrecip(struct dev_st + return ret; + } + +-static int __match_minor(struct device *dev, void *data) ++static int match_devt(struct device *dev, void *data) + { +- int minor = *((int *)data); +- +- if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) +- return 1; +- return 0; ++ return (dev->devt == (dev_t) data); + } + +-static struct usb_device *usbdev_lookup_by_minor(int minor) ++static struct usb_device *usbdev_lookup_by_devt(dev_t devt) + { + struct device *dev; + +- dev = bus_find_device(&usb_bus_type, NULL, &minor, __match_minor); ++ dev = bus_find_device(&usb_bus_type, NULL, (void *) devt, match_devt); + if (!dev) + return NULL; + put_device(dev); +@@ -588,9 +584,10 @@ static int usbdev_open(struct inode *ino + goto out; + + ret = -ENOENT; ++ + /* usbdev device-node */ + if (imajor(inode) == USB_DEVICE_MAJOR) +- dev = usbdev_lookup_by_minor(iminor(inode)); ++ dev = usbdev_lookup_by_devt(inode->i_rdev); + #ifdef CONFIG_USB_DEVICEFS + /* procfs file */ + if (!dev) |
