bk://kernel.bkbits.net/gregkh/linux/usb-2.6 greg@kroah.com|ChangeSet|20040512170623|54964 greg # This is a BitKeeper generated diff -Nru style patch. # # ChangeSet # 2004/05/12 20:32:44-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/core/inode.c # 2004/05/12 20:32:41-07:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2004/05/12 20:32:41-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/05/12 10:06:23-07:00 greg@kroah.com # Merge kroah.com:/home/greg/linux/BK/bleed-2.6 # into kroah.com:/home/greg/linux/BK/usb-2.6 # # drivers/usb/core/inode.c # 2004/05/12 10:06:18-07:00 greg@kroah.com +0 -0 # Auto merged # # MAINTAINERS # 2004/05/12 10:06:18-07:00 greg@kroah.com +0 -0 # Auto merged # # ChangeSet # 2004/05/12 01:26:11-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/input/wacom.c # 2004/05/12 01:26:08-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/input/hid-core.c # 2004/05/12 01:26:08-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/05/12 01:25:17-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # include/linux/pci_ids.h # 2004/05/12 01:25:14-07:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/parisc/configs/c3000_defconfig # 2004/05/12 01:25:14-07:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2004/05/12 01:25:14-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/image/mdc800.c # 2004/05/11 16:37:35-07:00 oliver@neukum.org +9 -3 # USB: fixes of assumptions about waitqueues # # ChangeSet # 2004/05/11 16:19:59-07:00 colin@colino.net # [PATCH] USB: cosmetic fixes for cdc-acm # # drivers/usb/class/cdc-acm.c # 2004/05/10 10:04:34-07:00 colin@colino.net +142 -133 # USB: cosmetic fixes for cdc-acm # # ChangeSet # 2004/05/11 16:03:07-07:00 david-b@pacbell.net # [PATCH] USB: OHCI root hub suspend/resume/wakeup # # This patch goes on top of the previous two, and the hcd-0506 patch: # # - Moves root hub suspend/resume code out of PCI-specific bus glue # into generic hub code. That way it's easy to re-use it even # for non-PCI implementations like SA1111, OMAP, and LH7A404. # (Plus, given CONFIG_USB_SUSPEND, it can be invoked with sysfs.) # # - Root hub suspend is a lot more careful, as is root hub resume. # Pending transactions are now shut down more consistently; and # more registers are re-initialized on resume. # # - The PCI bus glue is now left with truly generic PCI stuff, plus # some PMAC-specific stuff (which doesn't include irq disabling # any more, hcd-0506 moves that up a level in the stack). # # - Remote wakeup support is basically working for the root hub. # (given CONFIG_USB_SUSPEND to suspend devices and enable it). # # - Idle HCs will now automatically suspend themselves, and resume # as necessary. This saves a certain amount of power on most # systems, and matches what UHCI has been doing for a while. # # The large size of this patch is mostly because of moving that # root hub suspend/resume code out of the PCI-specific glue. # # drivers/usb/host/ohci.h # 2004/05/09 10:29:43-07:00 david-b@pacbell.net +4 -1 # USB: OHCI root hub suspend/resume/wakeup # # drivers/usb/host/ohci-pci.c # 2004/05/09 10:33:18-07:00 david-b@pacbell.net +26 -142 # USB: OHCI root hub suspend/resume/wakeup # # drivers/usb/host/ohci-mem.c # 2004/05/09 10:31:20-07:00 david-b@pacbell.net +1 -0 # USB: OHCI root hub suspend/resume/wakeup # # drivers/usb/host/ohci-hub.c # 2004/05/09 10:35:38-07:00 david-b@pacbell.net +290 -3 # USB: OHCI root hub suspend/resume/wakeup # # drivers/usb/host/ohci-hcd.c # 2004/05/09 10:37:07-07:00 david-b@pacbell.net +12 -1 # USB: OHCI root hub suspend/resume/wakeup # # ChangeSet # 2004/05/11 15:57:51-07:00 david-b@pacbell.net # [PATCH] USB: khubd turns port power back on after reset # # This goes with the OHCI anti-deadlock patch, and is what # ensures that when a root hub loses power during suspend, # khubd can turn port power back on so devices can enumerate. # # drivers/usb/core/hub.c # 2004/05/09 10:26:14-07:00 david-b@pacbell.net +8 -1 # USB: khubd turns port power back on after reset # # ChangeSet # 2004/05/11 15:57:21-07:00 david-b@pacbell.net # [PATCH] USB: OHCI cleanups # # This splits out a few obvious fixes, to help shrink a PM patch: # # - when the HC is quiescing, don't schedule any more EDs # or re-activate any after unlink completion. # # - when the HC is suspended, don't access registers through # sysfs either. # # - simplify locking and call for donelist processing # # drivers/usb/host/ohci-q.c # 2004/05/11 13:17:33-07:00 david-b@pacbell.net +8 -9 # USB: OHCI cleanups # # drivers/usb/host/ohci-pci.c # 2004/05/11 06:28:56-07:00 david-b@pacbell.net +5 -3 # USB: OHCI cleanups # # drivers/usb/host/ohci-hcd.c # 2004/05/11 13:17:48-07:00 david-b@pacbell.net +4 -2 # USB: OHCI cleanups # # drivers/usb/host/ohci-dbg.c # 2004/05/11 13:17:33-07:00 david-b@pacbell.net +12 -2 # USB: OHCI cleanups # # ChangeSet # 2004/05/11 15:56:50-07:00 david-b@pacbell.net # [PATCH] USB: OHCI resume/reset stops deadlocking in PM code # # System-wide PM resume now happily deadlocks if one of the # resuming devices tries to remove devices which vanished # during the suspend(*). IMO that's unreasonable both # because devices can/do vanish, and because 2.4 didn't # deadlock in those cases; but no patch to fix that has been # merged. The result is that ever since merging the "new" PM # code, some OHCI-based systems deadlock on resume. # # So this patch handles the "lost power during resume" case # differently: it doesn't disconnect the root hub (or its # children) directly. Instead, it does part of that work # immediately, and defers the rest to khubd: # # - add a "pending" list for live urbs, and use it after reset # to abort pending URBs (and reclaim "live" EDs/TDs) # - immediately mark all devices NOTATTACHED, so any operations # on the devices before khubd handles the disconnects, including # resume() callbacks, will fail # - kick root hub so it can do the cleanup # # It also handles "fminterval" init/reinit a bit better, mostly # to work better in some remote wakeup scenarios addressed in # later patches: # # - save any initial value the boot firmware provided # - use it during initialization (and eventually, remote wakeup) # # Other changes: # # - use better jiffies calculation for scheduled delays # - the allocator does more of the one-time initialization # - initialize hcd.can_wakeup according to boot firmware # - move some inlines to the header # - minor cleanups # # # (*) http://marc.theaimsgroup.com/?l=linux-kernel&m=106606272103414&w=2 # reported against 2.6.0-test7. # # drivers/usb/host/ohci.h # 2004/05/11 03:17:51-07:00 david-b@pacbell.net +31 -2 # USB: OHCI resume/reset stops deadlocking in PM code # # drivers/usb/host/ohci-q.c # 2004/05/11 03:19:12-07:00 david-b@pacbell.net +3 -1 # USB: OHCI resume/reset stops deadlocking in PM code # # drivers/usb/host/ohci-mem.c # 2004/05/11 03:17:58-07:00 david-b@pacbell.net +2 -0 # USB: OHCI resume/reset stops deadlocking in PM code # # drivers/usb/host/ohci-hcd.c # 2004/05/11 04:09:19-07:00 david-b@pacbell.net +89 -30 # USB: OHCI resume/reset stops deadlocking in PM code # # ChangeSet # 2004/05/11 15:45:25-07:00 oliver@neukum.org # [PATCH] USB: fixes of assumptions about waitqueues # # quoting Linus: # # -- # > so there is no need to recheck the bit in do/while loop, because # > there is no false wakeups now. # # You should never assume this. You should assume that there are _always_ # false wakeups. # # Why? Because Linux has always allowed people to leave wait-queues active, # without being "atomic". For example, the tty read/write layer used to # (still does?) add itself on the wait-queue _once_, and then leave itself # on the wait-queue while in a loop it does copies from/to user space. # -- # # Unfortunately, this means us. Here's the first fix. Comments? # # - make sure timeouts are observed even if somebody left us on a queue # # ChangeSet # 2004/05/11 15:41:37-07:00 david-b@pacbell.net # [PATCH] USB: more functional HCD PCI PM glue # # This patch makes the usbcore PCI suspend/resume logic behave # much better. In particular: # # - Even HCs without PCI PM support will normally be able # to support global suspend, saving power ... and will # need to resume later. Let them try to suspend; lots # of not-that-old USB controllers don't have PM caps. # # - Saner order for the boilerplate PCI stuff. It also # explicitly disables the IRQ and DMA, which aren't # available in D1/D2/D3 states anyway. # # - Uses pci_enable_wake() when the root hub supports # remote wakeup. Didn't fully work in one test setup; # that controller's PME# was evidently ignored. (Not # enabled unless CONFIG_USB_SUSPEND.) # # It worked for me with brief tests with the current 2.6.6-rc # uhci-hcd with one old UHCI; more extensive ones with various # OHCIs (using patches which I'll post soonish); and not at all # with EHCI (where PM hasn't ever worked). # # Those of you who've been having PM problems might find # this helpful as-is, though I think that unless you're # using UHCI you'll also need an HCD patch. # # - Dave # # drivers/usb/core/hcd-pci.c # 2004/05/06 12:41:30-07:00 david-b@pacbell.net +47 -22 # USB: more functional HCD PCI PM glue # # ChangeSet # 2004/05/11 15:34:08-07:00 stern@rowland.harvard.edu # [PATCH] USB: Accept devices with funky interface/altsetting numbers # # Now that all the USB drivers have been audited, we can safely accept # devices that have noncompliant numbering for their interfaces or # altsettings. This patch skips bad or duplicate descriptors, allows gaps # in the numbering, accepts more or fewer interfaces than bNumInterfaces, # and logs warnings describing all these things. Also, the debugging log # messages have been improved by David Brownell. # # This should please a sizeable group of users. # # drivers/usb/core/config.c # 2004/05/11 06:59:21-07:00 stern@rowland.harvard.edu +173 -120 # USB: Accept devices with funky interface/altsetting numbers # # ChangeSet # 2004/05/11 15:33:38-07:00 david-b@pacbell.net # [PATCH] USB: EHCI power management updates # # This patch updates EHCI suspend/resume so that its essential # components work on a few different implementations: # # - make root hub suspend/resume work # - make remote wakeup work (given CONFIG_USB_SUSPEND patch) # - separate root hub suspend/resume from PCI suspend/resume # - say if controller supports remote wakeup (on this system) # - sysfs register dump unavailable if controller is suspended # # Plus a handful of minor cleanups. Please merge, along with the # "hcd-0506.patch" I sent last week. # # Tested by modifying sysfs power/state files, since ACPI doesn't # work on this system (so I can't test system suspend/resume): # # - For root hub(*) ... suspend/resume works, also remote wakeup # - PCI controller ... suspend/resume works, remote wakeup # signals PME# (according to "lspci -vv"), but that's ignored # on my test sytem # # Regardless of whether USB was active, "echo 1 > /proc/acpi/sleep" # produced a system that wouldn't resume, and the same result # came from "echo standby > /sys/power/state". So that's about # as far as I can take this testing for now. # # - Dave # # (*) Doing this relies on the CONFIG_USB_SUSPEND patch. Otherwise # no USB devices respond to sysfs power/state updates. The # PCI suspend/resume is a superset of this. # # drivers/usb/host/ehci.h # 2004/05/07 12:49:08-07:00 david-b@pacbell.net +15 -4 # USB: EHCI power management updates # # drivers/usb/host/ehci-hub.c # 2004/05/10 12:40:20-07:00 david-b@pacbell.net +177 -5 # USB: EHCI power management updates # # drivers/usb/host/ehci-hcd.c # 2004/05/10 12:38:12-07:00 david-b@pacbell.net +68 -68 # USB: EHCI power management updates # # drivers/usb/host/ehci-dbg.c # 2004/05/07 12:48:33-07:00 david-b@pacbell.net +27 -3 # USB: EHCI power management updates # # ChangeSet # 2004/05/11 15:33:09-07:00 stern@rowland.harvard.edu # [PATCH] USB Gadget: Fix file-storage gadget Request Sense length # # On Fri, 7 May 2004, kernel@metro.cx wrote: # > Hi All, # > # > I don't know where else to report this, but I found a very very very # > minor bug in the usb gadgets drivers, specifically the file_storage.c # > mass storage driver. # > # > In the function do_request_sense(..) it says: # > # > buf[7] = 18 - 7; // Additional sense length # > # > Whereas (according to page 38 of the USB mass storage class, UFI command spec, # > http://www.usb.org/developers/devclass_docs#approved) this clearly neads # > to be equal to 10, not 11. # > # > I checked with the 2.6.5 source, it is still there. Hope someone will find this usefull, although most USB hosts seem to ignore length bits alltogether anyway.... # > # > Koen Martens # # You are quite right; thank you for pointing this out. Greg, please apply # the patch below. # # drivers/usb/gadget/file_storage.c # 2004/05/07 05:21:45-07:00 stern@rowland.harvard.edu +1 -1 # USB Gadget: Fix file-storage gadget Request Sense length # # ChangeSet # 2004/05/11 15:32:38-07:00 lcapitulino@prefeitura.sp.gov.br # [PATCH] USB: fix media/dsbr100.c unused variable. # # drivers/usb/media/dsbr100.c: In function `usb_dsbr100_probe': # drivers/usb/media/dsbr100.c:239: warning: unused variable `videodev' # # drivers/usb/media/dsbr100.c # 2004/05/07 01:53:51-07:00 lcapitulino@prefeitura.sp.gov.br +0 -1 # USB: fix media/dsbr100.c unused variable. # # ChangeSet # 2004/05/11 14:31:48-07:00 hannal@us.ibm.com # [PATCH] USB: Add class support to drivers/usb/misc/tiglusb.c # # drivers/usb/misc/tiglusb.c # 2004/05/05 15:40:27-07:00 hannal@us.ibm.com +49 -15 # USB: Add class support to drivers/usb/misc/tiglusb.c # # ChangeSet # 2004/05/11 11:34:29-07:00 greg@kroah.com # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/storage/shuttle_usbat.h # 2004/05/11 04:32:50-07:00 greg@kroah.com +0 -20 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/storage/shuttle_usbat.c # 2004/05/11 04:32:50-07:00 greg@kroah.com +70 -72 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/storage/jumpshot.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +1 -1 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/storage/isd200.c # 2004/05/11 04:32:50-07:00 greg@kroah.com +13 -13 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/storage/datafab.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +1 -1 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/serial/kobil_sct.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +1 -1 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/serial/ir-usb.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +1 -1 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/serial/ipaq.c # 2004/05/11 04:32:50-07:00 greg@kroah.com +1 -1 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/serial/io_ti.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +5 -5 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/net/usbnet.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +5 -5 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/net/rtl8150.c # 2004/05/11 04:32:50-07:00 greg@kroah.com +4 -4 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/net/pegasus.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +2 -2 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/net/kaweth.c # 2004/05/11 04:32:50-07:00 greg@kroah.com +10 -8 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/misc/emi62_fw_s.h # 2004/05/11 04:32:50-07:00 greg@kroah.com +4 -3 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/misc/emi62_fw_m.h # 2004/05/11 04:32:49-07:00 greg@kroah.com +4 -3 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/misc/emi62.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +1 -1 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/misc/emi26_fw.h # 2004/05/11 04:32:49-07:00 greg@kroah.com +3 -3 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/misc/emi26.c # 2004/05/11 04:32:50-07:00 greg@kroah.com +1 -1 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/misc/cytherm.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +3 -6 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/media/w9968cf.h # 2004/05/11 04:32:49-07:00 greg@kroah.com +2 -2 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/media/ultracam.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +1 -1 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/input/wacom.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +2 -2 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/input/kbtab.c # 2004/05/11 04:32:50-07:00 greg@kroah.com +1 -1 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/input/hid-core.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +5 -3 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/input/aiptek.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +2 -2 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # drivers/usb/class/bluetty.c # 2004/05/11 04:32:49-07:00 greg@kroah.com +2 -2 # USB: make functions static in usb drivers that should be # # Thanks to Tridge's findstatic.pl script for helping find these. # # ChangeSet # 2004/05/05 14:52:58-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/core/message.c # 2004/05/05 14:52:55-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/05/05 14:52:11-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # CREDITS # 2004/05/05 14:52:08-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/05/05 14:32:49-07:00 herbert@gondor.apana.org.au # [PATCH] USB Storage: Sony Clie # # I've received the following report which indicates that the Sony Clie needs # the US_FL_FIX_INQUIRY flag set. # # http://bugs.debian.org/243650 # # drivers/usb/storage/unusual_devs.h # 2004/04/16 17:47:39-07:00 herbert@gondor.apana.org.au +7 -0 # USB Storage: Sony Clie # # ChangeSet # 2004/05/05 14:10:42-07:00 stern@rowland.harvard.edu # [PATCH] PATCH: (as268) Import device-reset changes from gadget-2.6 tree # # This patch imports the changes that David Brownell has made to the # device-reset functions in his gadget-2.6 tree. Once these ongoing # troubling questions about locking are settled, I'll add support for the # "descriptors changed" case. # # drivers/usb/core/hub.c # 2004/05/03 08:02:43-07:00 stern@rowland.harvard.edu +99 -97 # PATCH: (as268) Import device-reset changes from gadget-2.6 tree # # ChangeSet # 2004/05/05 13:44:44-07:00 al.fracchetti@tin.it # [PATCH] USB Storage: Kyocera Finecsm 3L -unusual_devs.h # # drivers/usb/storage/unusual_devs.h # 2004/03/27 10:48:01-08:00 al.fracchetti@tin.it +6 -0 # USB Storage: Kyocera Finecsm 3L -unusual_devs.h # # ChangeSet # 2004/05/05 13:36:39-07:00 stern@rowland.harvard.edu # [PATCH] USB: Small change to CPiA USB driver # # Only one aspect of it is notable: The CPiA USB # driver calls usb_driver_release_interface() during its disconnect() # routine. That doesn't appear to be necessary, since it didn't call # usb_driver_claim_interface() beforehand and since the interface will be # released automatically when disconnect() returns. # # drivers/media/video/cpia_usb.c # 2004/04/27 06:54:33-07:00 stern@rowland.harvard.edu +1 -3 # USB: Small change to CPiA USB driver # # ChangeSet # 2004/05/05 13:36:11-07:00 stern@rowland.harvard.edu # [PATCH] USB Storage: unusual_devs.h update # # On 4 May 2004, Rajesh Kumble Nayak wrote: # # > The Above patch work fine for Sony Hc-85 # > I shall post the dmesg entry soon. # > # > With many thanks # > Rajesh # # Greg and Pete, here's the patch. It's possible that this entry could be # combined with the previous one, but until we know definitely they should # be kept separate. # # drivers/usb/storage/unusual_devs.h # 2004/04/29 07:42:22-07:00 stern@rowland.harvard.edu +7 -0 # USB Storage: unusual_devs.h update # # ChangeSet # 2004/05/05 13:35:43-07:00 stern@rowland.harvard.edu # [PATCH] USB: Reduce kernel stack usage # # This patch allocates a temporary array from the heap instead of from the # kernel's stack in usb_set_configuration(). It also updates a few # comments. Please apply. # # drivers/usb/core/message.c # 2004/05/03 03:26:40-07:00 stern@rowland.harvard.edu +23 -12 # USB: Reduce kernel stack usage # # ChangeSet # 2004/05/05 13:35:14-07:00 daniel.ritz@gmx.ch # [PATCH] USB: add support for eGalax Touchscreen USB # # this is the second version of the patch to add support for eGalax Touchkit USB # touchscreen. changes since last patch: # - fixed the bug in open, found by oliver neukum # - renamed driver from touchkit.c to touchkitusb.c (since the thing also exists # as RS232, PS/2 and I2C) # - some minor coding style updates # # drivers/usb/input/touchkitusb.c # 2004/05/02 07:32:32-07:00 daniel.ritz@gmx.ch +310 -0 # USB: add support for eGalax Touchscreen USB # # drivers/usb/input/Makefile # 2004/04/25 09:18:58-07:00 daniel.ritz@gmx.ch +1 -0 # USB: add support for eGalax Touchscreen USB # # drivers/usb/input/Kconfig # 2004/05/01 10:40:50-07:00 daniel.ritz@gmx.ch +13 -0 # USB: add support for eGalax Touchscreen USB # # drivers/usb/input/touchkitusb.c # 2004/05/02 07:32:32-07:00 daniel.ritz@gmx.ch +0 -0 # BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/input/touchkitusb.c # # ChangeSet # 2004/05/05 13:34:46-07:00 david-b@pacbell.net # [PATCH] USB: usbnet handles Billionton Systems USB2AR # # This adds another ax8817x device to "usbnet". # # drivers/usb/net/usbnet.c # 2004/04/26 14:32:52-07:00 david-b@pacbell.net +4 -0 # USB: usbnet handles Billionton Systems USB2AR # # ChangeSet # 2004/05/05 13:34:16-07:00 stefan.eletzhofer@eletztrick.de # [PATCH] USB Gadget: fix g_serial debug module parm # # g_serial.ko can't be load as module because "debug" is only # defined if G_SERIAL_DEBUG is defined, but "debug" is referenced # in MODULE_PARM(). # # drivers/usb/gadget/serial.c # 2004/05/04 17:00:00-07:00 stefan.eletzhofer@eletztrick.de +5 -1 # USB Gadget: fix g_serial debug module parm # # ChangeSet # 2004/05/05 13:33:47-07:00 stefan.eletzhofer@eletztrick.de # [PATCH] USB Gadget: fix pxa define in gadget_chips.h # # below is a trivial patch which fixes the PXA gadget define # in drivers/linux/usb/gadget/gadget_chips.h # # Everywhere CONFIG_USB_GADGET_PXA2XX is used, except in that file, which # bites obviously ... # # # Fix define for PXA UDC. # # drivers/usb/gadget/gadget_chips.h # 2004/05/04 17:00:00-07:00 stefan.eletzhofer@eletztrick.de +1 -1 # USB Gadget: fix pxa define in gadget_chips.h # # ChangeSet # 2004/05/05 12:13:23-07:00 greg@kroah.com # USB: add support for Zire 31 devices. # # Info was from Adriaan de Groot # # drivers/usb/serial/visor.h # 2004/05/05 05:12:42-07:00 greg@kroah.com +1 -0 # USB: add support for Zire 31 devices. # # Info was from Adriaan de Groot # # drivers/usb/serial/visor.c # 2004/05/05 05:12:42-07:00 greg@kroah.com +3 -0 # USB: add support for Zire 31 devices. # # Info was from Adriaan de Groot # # ChangeSet # 2004/05/04 14:13:11-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb.h # 2004/05/04 14:13:08-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/05/03 14:18:33-07:00 tejohnson@yahoo.com # [PATCH] USB: update for mtouchusb # # The attached patch for the 3M Touch Systems Capacitive controller. (again) # # Quick list of changes: # # * decrease mtouch->open counter in the event of a urb submission # failure # # The changes are due to comments Oliver Neukum's comments on the # touchkit.c driver. Good catch! Sorry I missed it. # # http://marc.theaimsgroup.com/?l=linux-usb-devel&m=108343028201159&w=2 # # drivers/usb/input/mtouchusb.c # 2004/05/02 09:37:18-07:00 tejohnson@yahoo.com +3 -1 # USB: update for mtouchusb # # ChangeSet # 2004/05/03 14:17:38-07:00 stern@rowland.harvard.edu # [PATCH] USB: Altsetting update for USB IrDA driver # # This patch updates the USB IrDA driver to take into account that the # kernel may no longer store altsetting entries in numerical order. # The driver only needed one change; this was a simple matter of using the # entry corresponding to the altsetting that was just installed. # # drivers/net/irda/irda-usb.c # 2004/05/03 03:37:59-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting update for USB IrDA driver # # ChangeSet # 2004/05/03 14:16:51-07:00 c.lucas@ifrance.com # [PATCH] USB: esthetic and trivial patch. # # include/linux/usb.h # 2004/05/02 23:42:51-07:00 c.lucas@ifrance.com +1 -1 # USB: esthetic and trivial patch. # # ChangeSet # 2004/05/01 16:24:00-07:00 msdemlei@cl.uni-heidelberg.de # [PATCH] USB: DSBR-100 tiny patch # # On Fri, Feb 06, 2004 at 10:17:32AM -0800, Greg KH wrote: # > On Fri, Feb 06, 2004 at 05:06:01PM +0100, Markus Demleitner wrote: # > > Since I finally switched over to 2.6 I noticed that my dsbr100 driver # > > produces a warning to the effect that I should provide a release # > > callback. After a quick google on the issue I came to the conclusion # > # > No, you will have to fix up your driver to work properly, sorry. It's # > due to the changes to the v4l layer to handle removable devices much # > better (and to tie it into the driver model.) # # I didn't get around to doing real work on this until now, but finally # in the attachment there's my stab at bringing dsbr100 up to kernel 2.6. # I'm not really comfortable with the release callback issues (I've yet # to find some HOWTO-like documentation on this...) on the v4l side, # so I'd be grateful if you could have a look at it. I've basically # tried to copy what stv680 does, which may or may not have been a # good idea (in particular see the comment above the disconnect # function). # # I've used the opportunity for some code beautyfing, which of course # makes the patch a bit of a mess. I hope you won't mind too much # -- as you can see, it would have been pretty messy anyway. # # drivers/usb/media/dsbr100.c # 2004/04/21 12:32:35-07:00 msdemlei@cl.uni-heidelberg.de +133 -94 # USB: DSBR-100 tiny patch # # ChangeSet # 2004/05/01 16:18:03-07:00 david-b@pacbell.net # [PATCH] USB: dummy_hcd, root port wakeup/suspend # # Here's what's in my tree to make dummy_hcd do suspend and # wakeup correctly ... that is, making its emulated root hub # and gadget work more like real ones. # # It's easier to do this for fake hardware than the real stuff. # But real drivers tend to need very similar changes ... :) # # - Dave # # p.s. This does not depend on the suspend/resume patch. # And it doesn't do "global" suspend (of root hub). # # drivers/usb/gadget/dummy_hcd.c # 2004/04/29 11:16:12-07:00 david-b@pacbell.net +133 -33 # USB: dummy_hcd, root port wakeup/suspend # # ChangeSet # 2004/05/01 16:02:37-07:00 baldrick@free.fr # [PATCH] USB: fix WARN_ON in usbfs # # On Tuesday 27 April 2004 10:58, Oliver Neukum wrote: # > Am Dienstag, 27. April 2004 00:14 schrieb Greg KH: # > > On Mon, Apr 26, 2004 at 04:05:17PM +0200, Duncan Sands wrote: # > > > diff -Nru a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c # > > > --- a/drivers/usb/core/devio.c Mon Apr 26 13:48:28 2004 # > > > +++ b/drivers/usb/core/devio.c Mon Apr 26 13:48:28 2004 # > > > @@ -350,8 +350,8 @@ # > > > * all pending I/O requests; 2.6 does that. # > > > */ # > > > # > > > - if (ifnum < 8*sizeof(ps->ifclaimed)) # > > > - clear_bit(ifnum, &ps->ifclaimed); # > > > + BUG_ON(ifnum >= 8*sizeof(ps->ifclaimed)); # > > # > > I've changed that to a WARN_ON(). Yeah, writing over memory is bad, but # > > oopsing is worse. Let's be a bit nicer than that. # > # > You aren't nice that way. An oops has localised consequences. Scribbling # > over memory can cause anything. # # Hi Greg, if won't accept a BUG_ON, how about the following? # # drivers/usb/core/devio.c # 2004/04/30 03:01:37-07:00 baldrick@free.fr +5 -2 # USB: fix WARN_ON in usbfs # # ChangeSet # 2004/05/01 16:02:00-07:00 baldrick@free.fr # [PATCH] USB: usbfs: change extern inline to static inline # # And change __inline__ to inline and get rid of an unused function # while at it. # # drivers/usb/core/devio.c # 2004/04/30 16:36:25-07:00 baldrick@free.fr +5 -30 # USB: usbfs: change extern inline to static inline # # ChangeSet # 2004/05/01 15:23:25-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # MAINTAINERS # 2004/05/01 15:23:22-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/30 22:19:36-07:00 stuber@loria.fr # [PATCH] USB: LEGO USB Tower driver v0.95 # # here is the latest version 0.95 of the LEGO USB Tower driver against 2.6.6-rc3 # which corrects a lot of problems in the version currently in the kernel, # most notably sleeping in interrupt context and improper locking. # Please apply. # # It has been thoroughly tested with UHCI, OHCI and EHCI host controllers # using Lejos and NQC. Firmware and program download, and with proper # modifications all communication protocols supported by Lejos work, # as well as firmware and program download and datalog upload in NQC. # # Notes to application maintainers/protocol designers: # # - Small modifications are needed in communication protocols because # the tower tends to discard the first byte of transmissions. # So for example LNP needs to send an extra byte like 0xff before # the packet, and F7 handlers needs to cope with a lost 0x55. # # - I suggest /dev/usb/legousbtower0 etc. as the standard device names. # This puts it in the same place as the other USB devices and makes # clear which driver is responsible for these devices. # # drivers/usb/misc/legousbtower.c # 2004/04/30 12:44:31-07:00 stuber@loria.fr +489 -276 # USB: LEGO USB Tower driver v0.95 # # MAINTAINERS # 2004/04/30 08:30:25-07:00 stuber@loria.fr +7 -0 # USB: LEGO USB Tower driver v0.95 # # ChangeSet # 2004/04/30 22:19:00-07:00 david-b@pacbell.net # [PATCH] USB: reject urb submissions to suspended devices # # This patch rejects URB submissions to suspended devices, so # that they don't get hardware-specific fault reports. Instead, # they get the same code (-EHOSTUNREACH) for all HCDs. # # It also fixes a minor problem with colliding declarations of # the symbol USB_STATE_SUSPENDED. # # drivers/usb/core/urb.c # 2004/04/28 07:00:02-07:00 david-b@pacbell.net +2 -0 # USB: reject urb submissions to suspended devices # # drivers/usb/core/hcd.h # 2004/04/28 07:01:03-07:00 david-b@pacbell.net +1 -1 # USB: reject urb submissions to suspended devices # # drivers/usb/core/hcd-pci.c # 2004/04/28 06:57:24-07:00 david-b@pacbell.net +3 -3 # USB: reject urb submissions to suspended devices # # ChangeSet # 2004/04/30 22:17:59-07:00 david-b@pacbell.net # [PATCH] USB Gadget: gadget zero and USB suspend/resume # # This patch lets gadget zero be more useful in testing usb suspend # and resume. It prints messages on suspend() and resume(), and # supports an "autoresume=N" mode to wake the host after N seconds. # # drivers/usb/gadget/zero.c # 2004/04/27 13:26:18-07:00 david-b@pacbell.net +63 -2 # USB Gadget: gadget zero and USB suspend/resume # # ChangeSet # 2004/04/30 22:16:57-07:00 linux-usb@nerds-incorporated.org # [PATCH] USB: Alcatel TD10 Serial to USB converter cable support # # The Alcatel TD10 USB to Serial converter cable (for use with a Alcatel # OT 535 or 735(i) mobile phone) seems to be a repackaged Alcatel # version of the Prolific 2303 adapter. # # And as such, simply adding its product/vendor id (0x11f7/0x02df) to # drivers/usb/serial/pl2303.c seems to be enough to make it work. # # drivers/usb/serial/pl2303.h # 2004/04/29 09:35:06-07:00 linux-usb@nerds-incorporated.org +3 -0 # USB: Alcatel TD10 Serial to USB converter cable support # # drivers/usb/serial/pl2303.c # 2004/04/29 09:35:28-07:00 linux-usb@nerds-incorporated.org +1 -0 # USB: Alcatel TD10 Serial to USB converter cable support # # ChangeSet # 2004/04/30 22:15:51-07:00 stern@rowland.harvard.edu # [PATCH] USB: USB altsetting updates for IDSN Hisax driver # # The USB core is changing the way interfaces and altsettings are stored. # They are no longer required to be in numerical order, and as a result, # simply indexing the interface and altsetting arrays won't work as # expected. # # This patch for the st5481 takes these changes into account. A simpler # approach would be to store a pointer to the struct usb_host_interface # rather than look it up repeatedly, but I'm not very familiar with this # driver and didn't want to attempt such an alteration. # # drivers/isdn/hisax/st5481_usb.c # 2004/04/26 07:43:53-07:00 stern@rowland.harvard.edu +7 -3 # USB: USB altsetting updates for IDSN Hisax driver # # drivers/isdn/hisax/st5481_d.c # 2004/04/26 07:43:53-07:00 stern@rowland.harvard.edu +7 -2 # USB: USB altsetting updates for IDSN Hisax driver # # drivers/isdn/hisax/st5481_b.c # 2004/04/26 07:43:53-07:00 stern@rowland.harvard.edu +7 -2 # USB: USB altsetting updates for IDSN Hisax driver # # ChangeSet # 2004/04/30 22:06:45-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # drivers/usb/core/inode.c # 2004/04/30 22:06:42-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/29 16:00:47-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # include/linux/pci_ids.h # 2004/04/29 16:00:44-07:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2004/04/29 16:00:44-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/29 10:47:50-07:00 greg@kroah.com # USB: fix build error in hci_usb driver due to urb reference count change. # # This really needs to get fixed the proper way, by making the urb allocation # dynamic in the driver, instead of the hack it is currently doing... # # drivers/bluetooth/hci_usb.c # 2004/04/29 03:47:26-07:00 greg@kroah.com +1 -1 # USB: fix build error in hci_usb driver due to urb reference count change. # # This really needs to get fixed the proper way, by making the urb allocation # dynamic in the driver, instead of the hack it is currently doing... # # ChangeSet # 2004/04/29 10:40:57-07:00 greg@kroah.com # USB: remove the wait_for_urb function from bfusb driver as it's no longer needed. # # drivers/bluetooth/bfusb.c # 2004/04/29 03:40:30-07:00 greg@kroah.com +0 -9 # USB: remove the wait_for_urb function from bfusb driver as it's no longer needed. # # ChangeSet # 2004/04/28 21:43:38-07:00 sean@mess.org # [PATCH] USB: fix PhidgetServo driver # # Somehow I managed to send the wrong version. Here is a patch which fixes # that. (Remove a dev_info() which wasn't supposed to be there, and make sure # that everything is still consistent in the unlikely event that kmalloc() # fails). Just minor cleanups. # # drivers/usb/misc/phidgetservo.c # 2004/04/28 17:35:09-07:00 sean@mess.org +14 -17 # USB: fix PhidgetServo driver # # ChangeSet # 2004/04/28 13:38:52-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/input/hid-core.c # 2004/04/28 13:38:49-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/28 13:25:14-07:00 david-b@pacbell.net # [PATCH] USB: fix sparc64 2.6.6-rc2-mm2 build busted: usb/core/hub.c hubstatus # # > 2) An undefined 'hubstatus' variable in drivers/usb/core/hub.c: # > # > CC drivers/usb/core/hub.o # > drivers/usb/core/hub.c: In function `hub_port_connect_change': # > drivers/usb/core/hub.c:1343: error: `hubstatus' undeclared (first use in this function) # > drivers/usb/core/hub.c:1343: error: (Each undeclared identifier is reported only once # > drivers/usb/core/hub.c:1343: error: for each function it appears in.) # > make[3]: *** [drivers/usb/core/hub.o] Error 1 # > # > As a total shot in the dark, the following fixes the build (I've no clue # > if it is the right fix): # # Yes, it's the right fix. Greg, please merge the attached patch, # which will be needed on any big-endian system. # # drivers/usb/core/hub.c # 2004/04/27 01:19:29-07:00 david-b@pacbell.net +1 -1 # USB: fix sparc64 2.6.6-rc2-mm2 build busted: usb/core/hub.c hubstatus # # ChangeSet # 2004/04/28 11:52:21-07:00 stern@rowland.harvard.edu # [PATCH] USB: Lock devices during tree traversal # # On Tue, 27 Apr 2004, Greg KH wrote: # # > So, what's next in this patch series? :) # # Funny you should ask... # # While writing those patches I noted a problem, that the USB device tree # can change while a process reading /proc/bus/usb/devices is traversing it, # leading to an oops when a pointer to a no-longer-existing child device is # dereferenced. The ensuing discussion led to the conclusion that the # devices' ->serialize locks should be acquired, top-down, while going # through the tree. # # That means changing the code that populates the devices file and changing # the code that adds and removes USB device structures. This patch takes # care of the first part. I'm delaying the second part because that section # of usbcore is still under change -- David Brownell's revisions have not # yet been fully integrated. # # A similar change should be made to usb_find_device() and match_device() in # usb.c. You may want to add that yourself. # # drivers/usb/core/devices.c # 2004/04/28 06:48:48-07:00 stern@rowland.harvard.edu +10 -2 # USB: Lock devices during tree traversal # # ChangeSet # 2004/04/28 11:51:52-07:00 sean@mess.org # [PATCH] USB: add new USB PhidgetServo driver # # Here is a driver for the usb servo controllers from Phidgets # , using sysfs. # # Note that the devices claim to be hid devices, so I've added them to the # hid_blacklist (HID_QUIRK_IGNORE). A servo controller isn't really an hid # device (or is it?). # # drivers/usb/misc/Makefile # 2004/04/21 09:02:00-07:00 sean@mess.org +1 -0 # USB: add new USB PhidgetServo driver # # drivers/usb/misc/Kconfig # 2004/04/21 09:02:00-07:00 sean@mess.org +12 -0 # USB: add new USB PhidgetServo driver # # drivers/usb/input/hid-core.c # 2004/04/22 02:54:25-07:00 sean@mess.org +12 -0 # USB: add new USB PhidgetServo driver # # drivers/usb/Makefile # 2004/04/21 09:02:00-07:00 sean@mess.org +1 -0 # USB: add new USB PhidgetServo driver # # drivers/usb/misc/phidgetservo.c # 2004/04/27 04:03:07-07:00 sean@mess.org +330 -0 # USB: add new USB PhidgetServo driver # # drivers/usb/misc/phidgetservo.c # 2004/04/27 04:03:07-07:00 sean@mess.org +0 -0 # BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/misc/phidgetservo.c # # ChangeSet # 2004/04/27 19:39:13-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb.h # 2004/04/27 19:39:10-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/04/27 19:39:10-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/message.c # 2004/04/27 19:39:10-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/27 17:33:52-07:00 greg@kroah.com # USB: fix compiler warnings in devices.c file. # # drivers/usb/core/devices.c # 2004/04/27 10:33:12-07:00 greg@kroah.com +2 -2 # USB: fix compiler warnings in devices.c file. # # ChangeSet # 2004/04/27 16:02:32-07:00 stern@rowland.harvard.edu # [PATCH] USB: Allocate interface structures dynamically # # This is a revised version of an earlier patch; I feel a lot better about # this one. Basically it does the same thing as before: allocate # interfaces dynamically to avoid the problems with reusing them. # # The difference is that this patch adds a struct kref to the array of # usb_interface_cache's, so the array can persist if needed after the # device has been disconnected. Each interface takes a reference to it # (along with the configuration itself), so as long as the interfaces # remain pinned in memory the altsettings will also remain. # # Here is a slight revision of patch as246b. This one allocates all the new # interfaces before changing any other state; otherwise it's the same. # # include/linux/usb.h # 2004/04/15 07:43:58-07:00 stern@rowland.harvard.edu +38 -3 # USB: Allocate interface structures dynamically # # drivers/usb/core/usb.c # 2004/04/15 05:19:20-07:00 stern@rowland.harvard.edu +1 -1 # USB: Allocate interface structures dynamically # # drivers/usb/core/message.c # 2004/04/15 05:28:25-07:00 stern@rowland.harvard.edu +46 -13 # USB: Allocate interface structures dynamically # # drivers/usb/core/devices.c # 2004/04/15 05:21:07-07:00 stern@rowland.harvard.edu +19 -12 # USB: Allocate interface structures dynamically # # drivers/usb/core/config.c # 2004/04/15 07:45:41-07:00 stern@rowland.harvard.edu +31 -44 # USB: Allocate interface structures dynamically # # ChangeSet # 2004/04/27 15:22:05-07:00 greg@kroah.com # USB: fix incorrect usb-serial conversion for cur_altsetting from previous patch. # # drivers/usb/serial/usb-serial.c # 2004/04/27 08:21:23-07:00 greg@kroah.com +1 -1 # USB: fix incorrect usb-serial conversion for cur_altsetting from previous patch. # # ChangeSet # 2004/04/27 14:45:49-07:00 greg@kroah.com # USB: make ehci driver use a kref instead of an atomic_t # # drivers/usb/host/ehci.h # 2004/04/27 14:45:32-07:00 greg@kroah.com +2 -1 # USB: make ehci driver use a kref instead of an atomic_t # # drivers/usb/host/ehci-sched.c # 2004/04/27 14:45:32-07:00 greg@kroah.com +3 -3 # USB: make ehci driver use a kref instead of an atomic_t # # drivers/usb/host/ehci-q.c # 2004/04/27 14:45:32-07:00 greg@kroah.com +5 -5 # USB: make ehci driver use a kref instead of an atomic_t # # drivers/usb/host/ehci-mem.c # 2004/04/27 14:45:32-07:00 greg@kroah.com +23 -16 # USB: make ehci driver use a kref instead of an atomic_t # # drivers/usb/host/ehci-hcd.c # 2004/04/27 14:45:32-07:00 greg@kroah.com +1 -1 # USB: make ehci driver use a kref instead of an atomic_t # # ChangeSet # 2004/04/27 14:21:13-07:00 greg@kroah.com # USB: removed unused atomic_t in keyspan driver structure. # # drivers/usb/serial/keyspan.c # 2004/04/27 07:20:12-07:00 greg@kroah.com +0 -3 # USB: removed unused atomic_t in keyspan driver structure. # # ChangeSet # 2004/04/26 18:31:48-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb.h # 2004/04/26 18:31:45-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/04/26 18:31:45-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/26 17:07:28-07:00 greg@kroah.com # USB: switch struct urb to use a kref instead of it's own atomic_t # # include/linux/usb.h # 2004/04/26 10:06:52-07:00 greg@kroah.com +2 -1 # USB: switch struct urb to use a kref instead of it's own atomic_t # # drivers/usb/core/urb.c # 2004/04/26 10:06:52-07:00 greg@kroah.com +13 -8 # USB: switch struct urb to use a kref instead of it's own atomic_t # # ChangeSet # 2004/04/26 16:11:11-07:00 mdharm-usb@one-eyed-alien.net # [PATCH] USB: usb-storage driver changes for 2.6.x [4/4] # # This is a trivial patch to remove some duplicate includes. sched.h and # errno.h are already included in this file about a dozen lines or so above # this point. # # drivers/usb/storage/usb.c # 2004/04/24 19:25:23-07:00 mdharm-usb@one-eyed-alien.net +0 -2 # USB: usb-storage driver changes for 2.6.x [4/4] # # ChangeSet # 2004/04/26 16:10:49-07:00 mdharm-usb@one-eyed-alien.net # [PATCH] USB: usb-storage driver changes for 2.6.x [3/4] # # This patch adds some clear-halt calls if a GetMaxLUN fails. Apparently, # some devices (like certain early-rev Zip100s) stall their bulk pipes if # they receive a GetMaxLUN. # # drivers/usb/storage/transport.c # 2004/04/24 19:25:32-07:00 mdharm-usb@one-eyed-alien.net +11 -0 # USB: usb-storage driver changes for 2.6.x [3/4] # # ChangeSet # 2004/04/26 16:10:25-07:00 mdharm-usb@one-eyed-alien.net # [PATCH] USB: usb-storage driver changes for 2.6.x [2/4] # # This is patch as248b from Alan Stern, modified by myself: This adds a flag # which allows us to supress the "unneeded unusual_devs.h entry" message. # This is useful for times when idiotic device manufacturers break the rules # and release two different devices with the same VID, PID, and revision # number. # # drivers/usb/storage/usb.h # 2004/04/24 19:25:41-07:00 mdharm-usb@one-eyed-alien.net +1 -0 # USB: usb-storage driver changes for 2.6.x [2/4] # # drivers/usb/storage/usb.c # 2004/04/24 19:25:41-07:00 mdharm-usb@one-eyed-alien.net +1 -1 # USB: usb-storage driver changes for 2.6.x [2/4] # # drivers/usb/storage/unusual_devs.h # 2004/04/24 19:25:41-07:00 mdharm-usb@one-eyed-alien.net +1 -1 # USB: usb-storage driver changes for 2.6.x [2/4] # # ChangeSet # 2004/04/26 16:10:02-07:00 mdharm-usb@one-eyed-alien.net # [PATCH] USB: usb-storage driver changes for 2.6.x [1/4] # # Patch as239b from Alan Stern: This patch improves the interaction between # a SCSI reset, an internally generated reset, and an abort. This improves # our error-recovery in cases where the device is hung (or almost hung) while # we're trying to auto-reset. # # drivers/usb/storage/usb.h # 2004/04/24 19:25:50-07:00 mdharm-usb@one-eyed-alien.net +3 -2 # USB: usb-storage driver changes for 2.6.x [1/4] # # drivers/usb/storage/transport.c # 2004/04/24 19:25:50-07:00 mdharm-usb@one-eyed-alien.net +26 -21 # USB: usb-storage driver changes for 2.6.x [1/4] # # drivers/usb/storage/scsiglue.c # 2004/04/24 19:25:50-07:00 mdharm-usb@one-eyed-alien.net +11 -8 # USB: usb-storage driver changes for 2.6.x [1/4] # # ChangeSet # 2004/04/26 16:09:34-07:00 stern@rowland.harvard.edu # [PATCH] USB: Altsetting updates for usb/serial # # The updates needed for proper altsetting handling among the USB serial # drivers turned out to be a lot easier than I expected, thanks to the # organization of the drivers. Only a handful of changes were needed. # # drivers/usb/serial/usb-serial.c # 2004/04/26 07:38:00-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting updates for usb/serial # # drivers/usb/serial/safe_serial.c # 2004/04/26 07:38:00-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting updates for usb/serial # # drivers/usb/serial/kobil_sct.c # 2004/04/26 07:38:00-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting updates for usb/serial # # drivers/usb/serial/io_ti.c # 2004/04/26 07:38:00-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting updates for usb/serial # # ChangeSet # 2004/04/26 16:09:06-07:00 baldrick@free.fr # [PATCH] USB: be assertive in usbfs # # Be assertive. # # drivers/usb/core/devio.c # 2004/04/26 06:48:28-07:00 baldrick@free.fr +2 -2 # USB: be assertive in usbfs # # ChangeSet # 2004/04/26 16:08:38-07:00 bellucda@tiscali.it # [PATCH] USB: audits in usb_init() # # there were some missing audits in usb_init() # # drivers/usb/core/usb.c # 2004/04/26 14:55:56-07:00 bellucda@tiscali.it +26 -6 # USB: audits in usb_init() # # ChangeSet # 2004/04/26 16:08:12-07:00 tejohnson@yahoo.com # [PATCH] USB: mtouchusb update for 2.6.6-rc2 # # The attached patch for the 3M Touch Systems Capacitive controller. # # Quick list of changes: # # * Changed reset from standard USB dev reset to vendor reset # * Changed data sent to host from compensated to raw coordinates # * Eliminated vendor/product module params # * Performed multiple successfull tests with an EXII-5010UC # # The changes are primarily due to comments from Vojtech Pavlik, as well # as making the newer EXII-50XXUC controllers work. # # Thanks to 3M Touch Systems for sending me some new controllers to test with! # # An updated HOWTO is also available at: # # # http://groomlakelabs.com/grandamp/code/microtouch/Linux-Input-USB-Touchscreen-HowTo.txt # # drivers/usb/input/mtouchusb.c # 2004/04/25 12:16:01-07:00 tejohnson@yahoo.com +35 -72 # USB: mtouchusb update for 2.6.6-rc2 # # Documentation/usb/mtouchusb.txt # 2004/04/25 12:16:49-07:00 tejohnson@yahoo.com +42 -51 # USB: mtouchusb update for 2.6.6-rc2 # # ChangeSet # 2004/04/25 23:06:05-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # include/linux/pci_ids.h # 2004/04/25 23:06:02-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/24 23:51:05-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/core/usb.c # 2004/04/24 23:51:02-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/23 16:41:30-07:00 stern@rowland.harvard.edu # [PATCH] USB: Altsetting update for USB net drivers # # The only driver under usb/net that needed any altsetting changes was # usbnet. I'm not looking forward to going through all the source files # under usb/serial. :-( # # drivers/usb/net/usbnet.c # 2004/04/22 09:56:28-07:00 stern@rowland.harvard.edu +4 -4 # USB: Altsetting update for USB net drivers # # ChangeSet # 2004/04/23 16:41:04-07:00 stern@rowland.harvard.edu # [PATCH] USB: Altsetting update for USB misc drivers # # This is the altsetting update for the drivers under usb/misc. As you can, # not much was needed at all. # # drivers/usb/misc/uss720.c # 2004/04/22 09:08:37-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting update for USB misc drivers # # drivers/usb/misc/legousbtower.c # 2004/04/22 09:01:26-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting update for USB misc drivers # # ChangeSet # 2004/04/23 16:40:35-07:00 stern@rowland.harvard.edu # [PATCH] USB: Altsetting updates for USB media drivers # # This patch implements the new altsetting regime for the drivers under # usb/media. Not much needed to be changed. I'm unable to test any of the # changes, but at least they compile all right (except that I didn't even # try to compile the pwc driver since it's marked BROKEN). # # The stv680 and w9968cf drivers still include an assumption that they are # bound to interface number 0. Since that the drivers are fairly tightly # linked to a specific kind of device I didn't try to change those # assumptions, but maybe they should be changed. # # drivers/usb/media/vicam.c # 2004/04/22 04:41:49-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting updates for USB media drivers # # drivers/usb/media/ultracam.c # 2004/04/21 09:26:45-07:00 stern@rowland.harvard.edu +4 -3 # USB: Altsetting updates for USB media drivers # # drivers/usb/media/se401.c # 2004/04/21 09:21:19-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting updates for USB media drivers # # drivers/usb/media/pwc-if.c # 2004/04/21 09:18:47-07:00 stern@rowland.harvard.edu +5 -2 # USB: Altsetting updates for USB media drivers # # drivers/usb/media/ov511.c # 2004/04/22 04:40:56-07:00 stern@rowland.harvard.edu +11 -3 # USB: Altsetting updates for USB media drivers # # drivers/usb/media/konicawc.c # 2004/04/21 09:41:57-07:00 stern@rowland.harvard.edu +12 -5 # USB: Altsetting updates for USB media drivers # # drivers/usb/media/ibmcam.c # 2004/04/21 09:32:00-07:00 stern@rowland.harvard.edu +5 -4 # USB: Altsetting updates for USB media drivers # # ChangeSet # 2004/04/23 16:40:06-07:00 stern@rowland.harvard.edu # [PATCH] USB: Cosmetic improvements for the UHCI driver # # This patch makes a few minor improvements to the appearance of the UHCI # driver. Please apply. # # drivers/usb/host/uhci-hcd.h # 2004/04/19 04:05:56-07:00 stern@rowland.harvard.edu +7 -7 # USB: Cosmetic improvements for the UHCI driver # # drivers/usb/host/uhci-hcd.c # 2004/04/14 03:19:54-07:00 stern@rowland.harvard.edu +10 -10 # USB: Cosmetic improvements for the UHCI driver # # ChangeSet # 2004/04/23 16:39:38-07:00 stern@rowland.harvard.edu # [PATCH] USB: Ignore URB_NO_INTERRUPT flag in UHCI # # Following a suggestion of David Brownell's I have decided to remove # support for the URB_NO_INTERRUPT flag in the UHCI driver. The overall # effect of the flag is to reduce the number of interrupts, thereby # improving throughput somewhat while increasing the duration of the # remaining IRQ handlers quite a lot (i.e., increasing interrupt variance). # So I think we're better off without it. Mind you, this is all in the # absence of any firm measurements. # # A common case where this will come up is during usb-storage bulk # transfers. Such transfers are generally divided into scatter-gather # components each corresponding to a single URB and transferring one memory # page (4 KB). While generating an interrupt for each one is a little # faster than ideal -- about every 3 ms -- it's better than waiting until 64 # KB has been transferred and there are 1024 individual TDs to clean up # during the IRQ. # # drivers/usb/host/uhci-hcd.c # 2004/04/16 04:02:37-07:00 stern@rowland.harvard.edu +7 -3 # USB: Ignore URB_NO_INTERRUPT flag in UHCI # # ChangeSet # 2004/04/23 16:20:26-07:00 baldrick@free.fr # [PATCH] USB usbfs: drop pointless racy check # # The check of interface->dev.driver requires a lock to be taken # to protect against driver binding changes. But in fact I think it # is better just to drop the test. The result is that the caller is # required to claim an interface before changing the altsetting, # which is consistent with the other routines that operate on # interfaces. # # devio.c | 6 ++---- # 1 files changed, 2 insertions(+), 4 deletions(-) # # drivers/usb/core/devio.c # 2004/04/14 05:18:37-07:00 baldrick@free.fr +2 -4 # USB usbfs: drop pointless racy check # # ChangeSet # 2004/04/23 16:20:07-07:00 baldrick@free.fr # [PATCH] USB usbfs: missing lock in proc_getdriver # # Hi Oliver, # # > I expect it to rarely matter, but it might matter now and then. It's # > just a question of hygiene. If you are using a temporary buffer I'd # > like to see it used to full advantage. So either drop the lock or do # > a direct copy. I'd prefer the first option your patch implemented. # # I agree. Greg, please consider applying the updated patch: # # # # Protect against driver binding changes while reading the driver name. # # drivers/usb/core/devio.c # 2004/04/14 07:03:12-07:00 baldrick@free.fr +6 -4 # USB usbfs: missing lock in proc_getdriver # # ChangeSet # 2004/04/23 16:19:43-07:00 baldrick@free.fr # [PATCH] USB usbfs: destroy submitted urbs only on the disconnected interface # # The remaining three patches contain miscellaneous fixes to usbfs. # This one fixes up the disconnect callback to only shoot down urbs # on the disconnected interface, and not on all interfaces. It also adds # a sanity check (this check is pointless because the interface could # never have been claimed in the first place if it failed, but I feel better # having it there). # # devio.c | 6 ++++-- # 1 files changed, 4 insertions(+), 2 deletions(-) # # drivers/usb/core/devio.c # 2004/04/14 05:18:20-07:00 baldrick@free.fr +4 -2 # USB usbfs: destroy submitted urbs only on the disconnected interface # # ChangeSet # 2004/04/23 16:19:14-07:00 baldrick@free.fr # [PATCH] USB usbfs: fix up releaseintf # # The semaphore is now taken in the callers. # # devio.c | 2 -- # 1 files changed, 2 deletions(-) # # drivers/usb/core/devio.c # 2004/04/14 05:18:08-07:00 baldrick@free.fr +0 -2 # USB usbfs: fix up releaseintf # # ChangeSet # 2004/04/23 16:18:50-07:00 baldrick@free.fr # [PATCH] USB usbfs: fix up proc_ioctl # # The semaphore is now taken in the caller. # # devio.c | 2 -- # 1 files changed, 2 deletions(-) # # drivers/usb/core/devio.c # 2004/04/14 05:17:56-07:00 baldrick@free.fr +0 -2 # USB usbfs: fix up proc_ioctl # # ChangeSet # 2004/04/23 16:18:24-07:00 baldrick@free.fr # [PATCH] USB usbfs: fix up proc_setconfig # # The semaphore is now taken in the caller. # # devio.c | 2 -- # 1 files changed, 2 deletions(-) # # drivers/usb/core/devio.c # 2004/04/14 05:17:46-07:00 baldrick@free.fr +0 -2 # USB usbfs: fix up proc_setconfig # # ChangeSet # 2004/04/23 16:17:59-07:00 baldrick@free.fr # [PATCH] USB usbfs: remove obsolete comment from proc_resetdevice # # devio.c | 3 --- # 1 files changed, 3 deletions(-) # # drivers/usb/core/devio.c # 2004/04/14 05:17:37-07:00 baldrick@free.fr +0 -3 # USB usbfs: remove obsolete comment from proc_resetdevice # # ChangeSet # 2004/04/23 16:17:35-07:00 baldrick@free.fr # [PATCH] USB usbfs: replace the per-file semaphore with the per-device semaphore # # devio.c | 43 +++++++++++++++++++++++-------------------- # usbdevice_fs.h | 1 - # 2 files changed, 23 insertions(+), 21 deletions(-) # # include/linux/usbdevice_fs.h # 2004/04/14 05:34:00-07:00 baldrick@free.fr +0 -1 # USB usbfs: replace the per-file semaphore with the per-device semaphore # # drivers/usb/core/devio.c # 2004/04/14 05:17:29-07:00 baldrick@free.fr +23 -20 # USB usbfs: replace the per-file semaphore with the per-device semaphore # # ChangeSet # 2004/04/23 16:17:09-07:00 baldrick@free.fr # [PATCH] USB usbfs: take a reference to the usb device # # Hi Greg, this is the first of a series of patches that replace the # per-file semaphore ps->devsem with the per-device semaphore # ps->dev->serialize. The role of devsem was to protect against # device disconnection. This can be done equally well using # ps->dev->serialize. On the other hand, ps->dev->serialize # protects against configuration and other changes, and has # already been introduced into usbfs in several places. Using # just one semaphore simplifies the code and removes some # remaining race conditions. It should also fix the oopses some # people have been seeing. In this first patch, a reference is # taken to the usb device as long as the usbfs file is open. That # way we can use ps->dev->serialize for as long as ps exists. # # devio.c | 27 ++++++++++++++++----------- # inode.c | 3 --- # 2 files changed, 16 insertions(+), 14 deletions(-) # # drivers/usb/core/inode.c # 2004/04/14 05:15:29-07:00 baldrick@free.fr +0 -3 # USB usbfs: take a reference to the usb device # # drivers/usb/core/devio.c # 2004/04/14 05:15:29-07:00 baldrick@free.fr +16 -11 # USB usbfs: take a reference to the usb device # # ChangeSet # 2004/04/23 15:45:24-07:00 david-b@pacbell.net # [PATCH] USB: khubd fixes # # This goes on top of the other enumeration patch I just sent, # to handle some dubious and/or broken hub configurations better. # # # Make khubd handle some cases better: # # - Track power budget for bus-powered hubs. This version only warns # when the budgets are exceeded. Eventually, the budgets should help # prevent such errors. # # - Rejects illegal USB setup: two consecutive bus powered hubs # would exceed the voltage drop budget, causing much flakiness. # # - For hosts with high speed hubs, warn when devices are hooked up # to full speed hubs if they'd be faster on a high speed one. # # - For hubs that don't do power switching, don't try to use it # # - For hubs that aren't self-powered, don't report local power status # # drivers/usb/core/hub.h # 2004/04/19 08:29:02-07:00 david-b@pacbell.net +2 -0 # USB: khubd fixes # # drivers/usb/core/hub.c # 2004/04/20 20:44:45-07:00 david-b@pacbell.net +143 -15 # USB: khubd fixes # # ChangeSet # 2004/04/23 15:44:57-07:00 david-b@pacbell.net # [PATCH] USB: re-factor enumeration logic # # This is an update to some patches from the December/January # timeframe, which will help sort out some of the mess for # drivers that need to use the reset logic. It's one of the # last significant patches in my gadget-2.6 tree that haven't # yet been merged into the main kernel tree. # # # More refactoring of the enumeration code paths: # # * The first half of usb_new_device() becomes the second half of a new # hub_port_init() routine (resets, sets address, gets descriptor) # # * The middle chunk of hub_port_connect_change() becomes the first half # of that new hub_port_init() routine. # # * Khubd uses that new routine in hub_port_connect_change(). # # * Now usb_new_device() cleans up better after faults, and has # a more useful locking policy (caller owns dev->serialize). # # * Has related minor cleanups including commenting some of # the curious request sequences coming from khubd. # # Refactoring means a lot of the current usb_reset_device() logic won't # need to stay an imperfect clone of the enumeration code ... soon, it # can just call hub_port_init(). # # Even without touching usb_reset_device(), this eliminates a deadlock. # Previously, address0_sem was used both during probe and during reset, # so probe routines can't implement DFU firmware download (involves a # reset; DFU also uncovers other problems) or safely recover from probe # faults by resetting (usb-storage can try that). Now that lock is no # longer held during probe(); so those deadlocks are gone. (And some # drivers, like at76c503, can start to remove ugly workarounds.) # # drivers/usb/core/usb.c # 2004/04/21 03:46:29-07:00 david-b@pacbell.net +13 -79 # USB: re-factor enumeration logic # # drivers/usb/core/hub.c # 2004/04/21 03:46:29-07:00 david-b@pacbell.net +215 -72 # USB: re-factor enumeration logic # # drivers/usb/core/hcd.c # 2004/04/21 03:46:29-07:00 david-b@pacbell.net +12 -0 # USB: re-factor enumeration logic # # ChangeSet # 2004/04/23 15:44:32-07:00 david-b@pacbell.net # [PATCH] USB: usbtest, smp unlink modes # # Handle some SMP-visible unlink states better. # # drivers/usb/misc/usbtest.c # 2004/04/14 20:23:11-07:00 david-b@pacbell.net +3 -3 # USB: usbtest, smp unlink modes # # ChangeSet # 2004/04/23 14:50:19-07:00 greg@kroah.com # [PATCH] USB: fix devio compiler warnings created by previous patch. # # drivers/usb/core/devio.c # 2004/04/23 07:33:30-07:00 greg@kroah.com +2 -2 # USB: fix devio compiler warnings created by previous patch. # # ChangeSet # 2004/04/23 14:49:56-07:00 stern@rowland.harvard.edu # [PATCH] USB: Eliminate dead code from the UHCI driver # # I'm not sure what this piece of code is doing in the UHCI driver. It # looks like someone envisioned queuing several URBs for the same endpoint # simultaneously. Anyway, the driver can't do that and this code can never # run. # # drivers/usb/host/uhci-hcd.c # 2004/04/16 04:02:37-07:00 stern@rowland.harvard.edu +1 -11 # USB: Eliminate dead code from the UHCI driver # # ChangeSet # 2004/04/23 14:49:33-07:00 stern@rowland.harvard.edu # [PATCH] USB: Implement endpoint_disable() for UHCI # # This patch implements the endpoint_disable method for the UHCI driver, as # you requested a while back. It guarantees that during unbinding events # (disconnect, configuration change, rmmod) the UHCI driver will have # finished using every URB for the interface being unbound. It doesn't # quite guarantee that the completion handlers will have finished running, # but it would take a pretty unlikely race to violate that assumption. (I # think it's the same with the OHCI and EHCI drivers.) # # Despite the patch numbering this one applies _after_ as249, which is a # more important bugfix. # # drivers/usb/host/uhci-hcd.h # 2004/04/19 04:05:56-07:00 stern@rowland.harvard.edu +2 -0 # USB: Implement endpoint_disable() for UHCI # # drivers/usb/host/uhci-hcd.c # 2004/04/14 03:19:54-07:00 stern@rowland.harvard.edu +49 -0 # USB: Implement endpoint_disable() for UHCI # # ChangeSet # 2004/04/23 14:49:10-07:00 stern@rowland.harvard.edu # [PATCH] USB: unusual_devs.h update # # On Tue, 20 Apr 2004, Damian Ivereigh wrote: # # > Here is the output of dmesg when plugging in an IBM USB MemKey # > # > usb-storage: This device (0a16,8888,0100 S 06 P 50) has unneeded SubClass and Protocol entries in unusual_devs.h # > Please send a copy of this message to # # Thank you for sending this in. Greg and Pete, here's the patch. # # drivers/usb/storage/unusual_devs.h # 2004/04/19 05:11:29-07:00 stern@rowland.harvard.edu +1 -1 # USB: unusual_devs.h update # # ChangeSet # 2004/04/23 14:48:47-07:00 stern@rowland.harvard.edu # [PATCH] USB: Remove unusual_devs entries for Minolta DiMAGE 7, 7Hi # # It looks safe to conclude that the unusual_devs.h entries for the Minolta # DiMAGE 7x cameras aren't needed. (Michael has tested the 7Hi and it's # definitely unnecessary.) The two other DiMAGE entries probably aren't # needed either, but we don't have any evidence of that so I'm leaving them. # # drivers/usb/storage/unusual_devs.h # 2004/04/16 04:37:06-07:00 stern@rowland.harvard.edu +0 -16 # USB: Remove unusual_devs entries for Minolta DiMAGE 7, 7Hi # # ChangeSet # 2004/04/23 14:48:28-07:00 david-b@pacbell.net # [PATCH] USB: root hubs can report remote wakeup feature # # The patch lets HCDs report the root hub remote wakeup feature to usbcore # through config descriptors, and lets usbcore say whether or not remote # wakeup (of host from sleep, by devices) should be enabled. # # Both OHCI and UHCI HCDs have some remote wakeup support already; I'm not # too sure how well it works. Given (separate) patches, their root hubs # can start to act more like other hubs in this area too. That'll make # it easier to start using USB suspend mode. # # drivers/usb/core/hcd.h # 2004/04/13 11:48:39-07:00 david-b@pacbell.net +10 -1 # USB: root hubs can report remote wakeup feature # # drivers/usb/core/hcd.c # 2004/04/13 11:33:31-07:00 david-b@pacbell.net +28 -11 # USB: root hubs can report remote wakeup feature # # ChangeSet # 2004/04/23 14:48:02-07:00 david-b@pacbell.net # [PATCH] USB: fix usbfs iso interval problem # # In 2.6, ISO transfers on USB require a value for urb->interval ... which # usbfs didn't provide (until this patch), or let user mode drivers specify. # # This patch initializes the urb->interval from the endpoint's descriptor, # so ISO transfers should now work from userspace. It also fixes a related # problem for interrupt transfers. # # drivers/usb/core/devio.c # 2004/04/14 13:36:53-07:00 david-b@pacbell.net +7 -1 # USB: fix usbfs iso interval problem # # ChangeSet # 2004/04/23 12:58:38-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # CREDITS # 2004/04/23 12:58:35-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/19 19:45:10-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # include/linux/pci_ids.h # 2004/04/19 19:45:07-07:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2004/04/19 19:45:07-07:00 akpm@bix.(none) +0 -0 # Auto merged # # CREDITS # 2004/04/19 19:45:06-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/07 20:17:13-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/core/message.c # 2004/04/07 20:17:11-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/02 11:35:28-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/pci_ids.h # 2004/04/02 11:35:25-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/01 15:16:14-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # CREDITS # 2004/04/01 15:16:11-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/31 19:24:39-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb.h # 2004/03/31 19:24:37-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/03/31 19:24:37-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/message.c # 2004/03/31 19:24:37-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc64/defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc64/configs/pSeries_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc/defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc/configs/pmac_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc/configs/common_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/parisc/configs/c3000_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ia64/defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ia64/configs/zx1_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ia64/configs/generic_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/arm/configs/neponset_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/30 20:18:36-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # arch/ppc64/defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc64/configs/pSeries_defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc/defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc/configs/pmac_defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc/configs/common_defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/parisc/configs/c3000_defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ia64/defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ia64/configs/zx1_defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ia64/configs/generic_defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/arm/configs/neponset_defconfig # 2004/03/30 20:18:32-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/30 12:09:32-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb.h # 2004/03/30 12:09:29-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/03/30 12:09:29-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/message.c # 2004/03/30 12:09:29-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/29 18:05:43-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # MAINTAINERS # 2004/03/29 18:05:41-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/29 13:51:58-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # CREDITS # 2004/03/29 13:51:56-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/28 12:29:41-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # drivers/usb/core/message.c # 2004/03/28 12:29:38-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/27 02:28:18-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/input/wacom.c # 2004/03/27 02:28:16-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/26 12:24:49-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb_gadget.h # 2004/03/26 12:24:46-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/03/26 12:24:46-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/20 13:26:55-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # CREDITS # 2004/03/20 13:26:53-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/16 21:53:42-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # drivers/usb/input/wacom.c # 2004/03/16 21:53:39-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/input/hid-core.c # 2004/03/16 21:53:39-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/16 12:59:58-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb_gadget.h # 2004/03/16 12:59:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # include/linux/usb.h # 2004/03/16 12:59:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/03/16 12:59:46-08:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2004/03/16 12:59:46-08:00 akpm@bix.(none) +0 -0 # Auto merged # # CREDITS # 2004/03/16 12:59:46-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/16 12:58:57-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # drivers/usb/input/wacom.c # 2004/03/16 12:58:47-08:00 akpm@bix.(none) +0 -4 # Auto merged # # drivers/usb/input/hid-core.c # 2004/03/16 12:58:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # CREDITS # 2004/03/16 12:58:46-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/14 11:03:00-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # include/linux/usb_gadget.h # 2004/03/14 11:02:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # include/linux/usb.h # 2004/03/14 11:02:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/03/14 11:02:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2004/03/14 11:02:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/12 10:57:17-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # MAINTAINERS # 2004/03/12 10:57:02-08:00 akpm@bix.(none) +0 -0 # Auto merged # # CREDITS # 2004/03/12 10:57:01-08:00 akpm@bix.(none) +0 -0 # Auto merged # diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Wed May 12 20:33:39 2004 +++ b/MAINTAINERS Wed May 12 20:33:39 2004 @@ -1229,6 +1229,13 @@ L: linux-scsi@vger.kernel.org S: Maintained +LEGO USB Tower driver +P: Juergen Stuber +M: starblue@users.sourceforge.net +L: legousb-devel@lists.sourceforge.net +W: http://legousb.sourceforge.net/ +S: Maintained + LINUX FOR IBM pSERIES (RS/6000) P: Paul Mackerras M: paulus@au.ibm.com diff -Nru a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c --- a/drivers/bluetooth/bfusb.c Wed May 12 20:33:39 2004 +++ b/drivers/bluetooth/bfusb.c Wed May 12 20:33:39 2004 @@ -98,14 +98,6 @@ static void bfusb_tx_complete(struct urb *urb, struct pt_regs *regs); static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs); -static inline void bfusb_wait_for_urb(struct urb *urb) -{ - while (atomic_read(&urb->count) > 1) { - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout((5 * HZ + 999) / 1000); - } -} - static struct urb *bfusb_get_completed(struct bfusb *bfusb) { struct sk_buff *skb; @@ -132,7 +124,6 @@ while ((skb = skb_dequeue(&bfusb->pending_q))) { urb = ((struct bfusb_scb *) skb->cb)->urb; usb_unlink_urb(urb); - bfusb_wait_for_urb(urb); skb_queue_tail(&bfusb->completed_q, skb); } diff -Nru a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c --- a/drivers/bluetooth/hci_usb.c Wed May 12 20:33:40 2004 +++ b/drivers/bluetooth/hci_usb.c Wed May 12 20:33:40 2004 @@ -342,7 +342,7 @@ static inline void hci_usb_wait_for_urb(struct urb *urb) { - while (atomic_read(&urb->count) > 1) { + while (atomic_read(&urb->kref.refcount) > 1) { current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((5 * HZ + 999) / 1000); } diff -Nru a/drivers/isdn/hisax/st5481_b.c b/drivers/isdn/hisax/st5481_b.c --- a/drivers/isdn/hisax/st5481_b.c Wed May 12 20:33:39 2004 +++ b/drivers/isdn/hisax/st5481_b.c Wed May 12 20:33:39 2004 @@ -257,13 +257,18 @@ static int st5481_setup_b_out(struct st5481_bcs *bcs) { struct usb_device *dev = bcs->adapter->usb_dev; - struct usb_host_interface *altsetting; + struct usb_interface *intf; + struct usb_host_interface *altsetting = NULL; struct usb_host_endpoint *endpoint; struct st5481_b_out *b_out = &bcs->b_out; DBG(4,""); - altsetting = &(dev->config->interface[0]->altsetting[3]); + intf = usb_ifnum_to_if(dev, 0); + if (intf) + altsetting = usb_altnum_to_altsetting(intf, 3); + if (!altsetting) + return -ENXIO; // Allocate URBs and buffers for the B channel out endpoint = &altsetting->endpoint[EP_B1_OUT - 1 + bcs->channel * 2]; diff -Nru a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c --- a/drivers/isdn/hisax/st5481_d.c Wed May 12 20:33:39 2004 +++ b/drivers/isdn/hisax/st5481_d.c Wed May 12 20:33:39 2004 @@ -652,13 +652,18 @@ static int st5481_setup_d_out(struct st5481_adapter *adapter) { struct usb_device *dev = adapter->usb_dev; - struct usb_host_interface *altsetting; + struct usb_interface *intf; + struct usb_host_interface *altsetting = NULL; struct usb_host_endpoint *endpoint; struct st5481_d_out *d_out = &adapter->d_out; DBG(2,""); - altsetting = &(dev->config->interface[0]->altsetting[3]); + intf = usb_ifnum_to_if(dev, 0); + if (intf) + altsetting = usb_altnum_to_altsetting(intf, 3); + if (!altsetting) + return -ENXIO; // Allocate URBs and buffers for the D channel out endpoint = &altsetting->endpoint[EP_D_OUT-1]; diff -Nru a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c --- a/drivers/isdn/hisax/st5481_usb.c Wed May 12 20:33:39 2004 +++ b/drivers/isdn/hisax/st5481_usb.c Wed May 12 20:33:39 2004 @@ -244,7 +244,8 @@ struct usb_device *dev = adapter->usb_dev; struct st5481_ctrl *ctrl = &adapter->ctrl; struct st5481_intr *intr = &adapter->intr; - struct usb_host_interface *altsetting; + struct usb_interface *intf; + struct usb_host_interface *altsetting = NULL; struct usb_host_endpoint *endpoint; int status; struct urb *urb; @@ -257,8 +258,11 @@ return status; } - - altsetting = &(dev->config->interface[0]->altsetting[3]); + intf = usb_ifnum_to_if(dev, 0); + if (intf) + altsetting = usb_altnum_to_altsetting(intf, 3); + if (!altsetting) + return -ENXIO; // Check if the config is sane if ( altsetting->desc.bNumEndpoints != 7 ) { diff -Nru a/drivers/media/video/cpia_usb.c b/drivers/media/video/cpia_usb.c --- a/drivers/media/video/cpia_usb.c Wed May 12 20:33:39 2004 +++ b/drivers/media/video/cpia_usb.c Wed May 12 20:33:39 2004 @@ -499,7 +499,7 @@ if (udev->descriptor.bNumConfigurations != 1) return -ENODEV; - interface = &intf->altsetting[0]; + interface = intf->cur_altsetting; printk(KERN_INFO "USB CPiA camera found\n"); @@ -620,8 +620,6 @@ wake_up_interruptible(&ucpia->wq_stream); udev = interface_to_usbdev(intf); - usb_driver_release_interface(&cpia_driver, - udev->actconfig->interface[0]); ucpia->curbuff = ucpia->workbuff = NULL; diff -Nru a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c --- a/drivers/net/irda/irda-usb.c Wed May 12 20:33:39 2004 +++ b/drivers/net/irda/irda-usb.c Wed May 12 20:33:39 2004 @@ -1421,7 +1421,7 @@ } /* Find our endpoints */ - interface = &intf->altsetting[0]; + interface = intf->cur_altsetting; if(!irda_usb_parse_endpoints(self, interface->endpoint, interface->desc.bNumEndpoints)) { ERROR("%s(), Bogus endpoints...\n", __FUNCTION__); diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Wed May 12 20:33:40 2004 +++ b/drivers/usb/Makefile Wed May 12 20:33:40 2004 @@ -66,3 +66,4 @@ obj-$(CONFIG_USB_TEST) += misc/ obj-$(CONFIG_USB_TIGL) += misc/ obj-$(CONFIG_USB_USS720) += misc/ +obj-$(CONFIG_USB_PHIDGETSERVO) += misc/ diff -Nru a/drivers/usb/class/bluetty.c b/drivers/usb/class/bluetty.c --- a/drivers/usb/class/bluetty.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/class/bluetty.c Wed May 12 20:33:39 2004 @@ -1234,7 +1234,7 @@ .chars_in_buffer = bluetooth_chars_in_buffer, }; -int usb_bluetooth_init(void) +static int usb_bluetooth_init(void) { int i; int result; @@ -1283,7 +1283,7 @@ } -void usb_bluetooth_exit(void) +static void usb_bluetooth_exit(void) { usb_deregister(&usb_bluetooth_driver); tty_unregister_driver(bluetooth_tty_driver); diff -Nru a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c --- a/drivers/usb/class/cdc-acm.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/class/cdc-acm.c Wed May 12 20:33:39 2004 @@ -567,6 +567,8 @@ * USB probe and disconnect routines. */ +#define CHECK_XFERTYPE(descr, xfer_type) (((descr)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == xfer_type) + static int acm_probe (struct usb_interface *intf, const struct usb_device_id *id) { @@ -583,143 +585,150 @@ dev = interface_to_usbdev (intf); - cfacm = dev->actconfig; - - /* We know we're probe()d with the control interface. */ - ifcom = intf->cur_altsetting; - - /* ACM doesn't guarantee the data interface is - * adjacent to the control interface, or that if one - * is there it's not for call management ... so find - * it - */ - for (j = 0; j < cfacm->desc.bNumInterfaces; j++) { - ifdata = cfacm->interface[j]->cur_altsetting; - data = cfacm->interface[j]; - - if (ifdata->desc.bInterfaceClass == 10 && - ifdata->desc.bNumEndpoints == 2) { - epctrl = &ifcom->endpoint[0].desc; - epread = &ifdata->endpoint[0].desc; - epwrite = &ifdata->endpoint[1].desc; - - if ((epctrl->bEndpointAddress & 0x80) != 0x80 || - (epctrl->bmAttributes & 3) != 3 || - (epread->bmAttributes & 3) != 2 || - (epwrite->bmAttributes & 3) != 2 || - ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80) - goto next_interface; - - if ((epread->bEndpointAddress & 0x80) != 0x80) { - epread = &ifdata->endpoint[1].desc; - epwrite = &ifdata->endpoint[0].desc; - } - dbg("found data interface at %d\n", j); - break; - } else { -next_interface: - ifdata = NULL; - data = NULL; - } - } - - /* there's been a problem */ - if (!ifdata) { - dbg("interface not found (%p)\n", ifdata); - return -ENODEV; - - } - - for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); - if (acm_table[minor]) { - err("no more free acm devices"); - return -ENODEV; - } + cfacm = dev->actconfig; - if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) { - err("out of memory"); - return -ENOMEM; - } - memset(acm, 0, sizeof(struct acm)); - - ctrlsize = epctrl->wMaxPacketSize; - readsize = epread->wMaxPacketSize; - acm->writesize = epwrite->wMaxPacketSize; - acm->control = intf; - acm->data = data; - acm->minor = minor; - acm->dev = dev; - - acm->bh.func = acm_rx_tasklet; - acm->bh.data = (unsigned long) acm; - INIT_WORK(&acm->work, acm_softint, acm); - - if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) { - err("out of memory"); - kfree(acm); - return -ENOMEM; - } + /* We know we're probe()d with the control interface. */ + ifcom = intf->cur_altsetting; - acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); - if (!acm->ctrlurb) { - err("out of memory"); - kfree(acm); - kfree(buf); - return -ENOMEM; - } - acm->readurb = usb_alloc_urb(0, GFP_KERNEL); - if (!acm->readurb) { - err("out of memory"); - usb_free_urb(acm->ctrlurb); - kfree(acm); - kfree(buf); - return -ENOMEM; - } - acm->writeurb = usb_alloc_urb(0, GFP_KERNEL); - if (!acm->writeurb) { - err("out of memory"); - usb_free_urb(acm->readurb); - usb_free_urb(acm->ctrlurb); - kfree(acm); - kfree(buf); - return -ENOMEM; - } - - usb_fill_int_urb(acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress), - buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); + /* ACM doesn't guarantee the data interface is + * adjacent to the control interface, or that if one + * is there it's not for call management ... so find + * it + */ + for (j = 0; j < cfacm->desc.bNumInterfaces; j++) { + ifdata = cfacm->interface[j]->cur_altsetting; + data = cfacm->interface[j]; + + if (ifdata->desc.bInterfaceClass == USB_CLASS_CDC_DATA + && ifdata->desc.bNumEndpoints == 2) { + + epctrl = &ifcom->endpoint[0].desc; + epread = &ifdata->endpoint[0].desc; + epwrite = &ifdata->endpoint[1].desc; + + if ((epctrl->bEndpointAddress & USB_DIR_IN) != USB_DIR_IN + || !CHECK_XFERTYPE(epctrl, USB_ENDPOINT_XFER_INT) + || !CHECK_XFERTYPE(epread, USB_ENDPOINT_XFER_BULK) + || !CHECK_XFERTYPE(epwrite, USB_ENDPOINT_XFER_BULK) + || ((epread->bEndpointAddress & USB_DIR_IN) + ^ (epwrite->bEndpointAddress & USB_DIR_IN)) != USB_DIR_IN) { + /* not suitable */ + goto next_interface; + } + + if ((epread->bEndpointAddress & USB_DIR_IN) != USB_DIR_IN) { + /* descriptors are swapped */ + epread = &ifdata->endpoint[1].desc; + epwrite = &ifdata->endpoint[0].desc; + } + dev_dbg(&intf->dev, "found data interface at %d\n", j); + break; + } else { +next_interface: + ifdata = NULL; + data = NULL; + } + } + + /* there's been a problem */ + if (!ifdata) { + dev_dbg(&intf->dev, "data interface not found\n"); + return -ENODEV; + + } + + for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); + if (acm_table[minor]) { + err("no more free acm devices"); + return -ENODEV; + } + + if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) { + dev_dbg(&intf->dev, "out of memory (acm kmalloc)\n"); + return -ENOMEM; + } + + memset(acm, 0, sizeof(struct acm)); - usb_fill_bulk_urb(acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), - buf += ctrlsize, readsize, acm_read_bulk, acm); - acm->readurb->transfer_flags |= URB_NO_FSBR; - - usb_fill_bulk_urb(acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), - buf += readsize, acm->writesize, acm_write_bulk, acm); - acm->writeurb->transfer_flags |= URB_NO_FSBR; - - dev_info(&intf->dev, "ttyACM%d: USB ACM device", minor); - - acm_set_control(acm, acm->ctrlout); - - acm->line.speed = cpu_to_le32(9600); - acm->line.databits = 8; - acm_set_line(acm, &acm->line); - - if ( (j = usb_driver_claim_interface(&acm_driver, data, acm)) != 0) { - err("claim failed"); - usb_free_urb(acm->ctrlurb); - usb_free_urb(acm->readurb); - usb_free_urb(acm->writeurb); - kfree(acm); - kfree(buf); - return j; - } - - tty_register_device(acm_tty_driver, minor, &intf->dev); - - acm_table[minor] = acm; - usb_set_intfdata (intf, acm); - return 0; + ctrlsize = epctrl->wMaxPacketSize; + readsize = epread->wMaxPacketSize; + acm->writesize = epwrite->wMaxPacketSize; + acm->control = intf; + acm->data = data; + acm->minor = minor; + acm->dev = dev; + + acm->bh.func = acm_rx_tasklet; + acm->bh.data = (unsigned long) acm; + INIT_WORK(&acm->work, acm_softint, acm); + + if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) { + dev_dbg(&intf->dev, "out of memory (buf kmalloc)\n"); + kfree(acm); + return -ENOMEM; + } + + acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); + if (!acm->ctrlurb) { + dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); + kfree(acm); + kfree(buf); + return -ENOMEM; + } + acm->readurb = usb_alloc_urb(0, GFP_KERNEL); + if (!acm->readurb) { + dev_dbg(&intf->dev, "out of memory (readurb kmalloc)\n"); + usb_free_urb(acm->ctrlurb); + kfree(acm); + kfree(buf); + return -ENOMEM; + } + acm->writeurb = usb_alloc_urb(0, GFP_KERNEL); + if (!acm->writeurb) { + dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)\n"); + usb_free_urb(acm->readurb); + usb_free_urb(acm->ctrlurb); + kfree(acm); + kfree(buf); + return -ENOMEM; + } + + usb_fill_int_urb(acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress), + buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); + + usb_fill_bulk_urb(acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), + buf += ctrlsize, readsize, acm_read_bulk, acm); + acm->readurb->transfer_flags |= URB_NO_FSBR; + + usb_fill_bulk_urb(acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), + buf += readsize, acm->writesize, acm_write_bulk, acm); + acm->writeurb->transfer_flags |= URB_NO_FSBR; + + if ( (j = usb_driver_claim_interface(&acm_driver, data, acm)) != 0) { + err("claim failed"); + usb_free_urb(acm->ctrlurb); + usb_free_urb(acm->readurb); + usb_free_urb(acm->writeurb); + kfree(acm); + kfree(buf); + return j; + } + + tty_register_device(acm_tty_driver, minor, &intf->dev); + + dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); + + acm_set_control(acm, acm->ctrlout); + + acm->line.speed = cpu_to_le32(9600); + acm->line.databits = 8; + acm_set_line(acm, &acm->line); + + acm_table[minor] = acm; + usb_set_intfdata (intf, acm); + return 0; } +#undef CHECK_XFERTYPE static void acm_disconnect(struct usb_interface *intf) { diff -Nru a/drivers/usb/core/config.c b/drivers/usb/core/config.c --- a/drivers/usb/core/config.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/core/config.c Wed May 12 20:33:39 2004 @@ -18,6 +18,11 @@ #define USB_MAXCONFIG 8 /* Arbitrary limit */ +static inline const char *plural(int n) +{ + return (n == 1 ? "" : "s"); +} + static int find_next_descriptor(unsigned char *buffer, int size, int dt1, int dt2, int *num_skipped) { @@ -26,7 +31,7 @@ unsigned char *buffer0 = buffer; /* Find the next descriptor of type dt1 or dt2 */ - while (size >= sizeof(struct usb_descriptor_header)) { + while (size > 0) { h = (struct usb_descriptor_header *) buffer; if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2) break; @@ -43,46 +48,46 @@ } static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, - int asnum, struct usb_host_endpoint *endpoint, + int asnum, struct usb_host_interface *ifp, int num_ep, unsigned char *buffer, int size) { unsigned char *buffer0 = buffer; - struct usb_descriptor_header *header; + struct usb_endpoint_descriptor *d; + struct usb_host_endpoint *endpoint; int n, i; - header = (struct usb_descriptor_header *)buffer; - if (header->bDescriptorType != USB_DT_ENDPOINT) { - dev_err(ddev, "config %d interface %d altsetting %d has an " - "unexpected descriptor of type 0x%X, " - "expecting endpoint type 0x%X\n", - cfgno, inum, asnum, - header->bDescriptorType, USB_DT_ENDPOINT); - return -EINVAL; - } + d = (struct usb_endpoint_descriptor *) buffer; + buffer += d->bLength; + size -= d->bLength; - if (header->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE) - memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_AUDIO_SIZE); - else if (header->bLength >= USB_DT_ENDPOINT_SIZE) - memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_SIZE); + if (d->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE) + n = USB_DT_ENDPOINT_AUDIO_SIZE; + else if (d->bLength >= USB_DT_ENDPOINT_SIZE) + n = USB_DT_ENDPOINT_SIZE; else { - dev_err(ddev, "config %d interface %d altsetting %d has an " - "invalid endpoint descriptor of length %d\n", - cfgno, inum, asnum, header->bLength); - return -EINVAL; + dev_warn(ddev, "config %d interface %d altsetting %d has an " + "invalid endpoint descriptor of length %d, skipping\n", + cfgno, inum, asnum, d->bLength); + goto skip_to_next_endpoint_or_interface_descriptor; } - i = endpoint->desc.bEndpointAddress & ~USB_ENDPOINT_DIR_MASK; + i = d->bEndpointAddress & ~USB_ENDPOINT_DIR_MASK; if (i >= 16 || i == 0) { - dev_err(ddev, "config %d interface %d altsetting %d has an " - "invalid endpoint with address 0x%X\n", - cfgno, inum, asnum, endpoint->desc.bEndpointAddress); - return -EINVAL; + dev_warn(ddev, "config %d interface %d altsetting %d has an " + "invalid endpoint with address 0x%X, skipping\n", + cfgno, inum, asnum, d->bEndpointAddress); + goto skip_to_next_endpoint_or_interface_descriptor; } - le16_to_cpus(&endpoint->desc.wMaxPacketSize); + /* Only store as many endpoints as we have room for */ + if (ifp->desc.bNumEndpoints >= num_ep) + goto skip_to_next_endpoint_or_interface_descriptor; + + endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints]; + ++ifp->desc.bNumEndpoints; - buffer += header->bLength; - size -= header->bLength; + memcpy(&endpoint->desc, d, n); + le16_to_cpus(&endpoint->desc.wMaxPacketSize); /* Skip over any Class Specific or Vendor Specific descriptors; * find the next endpoint or interface descriptor */ @@ -91,67 +96,72 @@ USB_DT_INTERFACE, &n); endpoint->extralen = i; if (n > 0) - dev_dbg(ddev, "skipped %d class/vendor specific endpoint " - "descriptors\n", n); + dev_dbg(ddev, "skipped %d descriptor%s after %s\n", + n, plural(n), "endpoint"); + return buffer - buffer0 + i; + +skip_to_next_endpoint_or_interface_descriptor: + i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, + USB_DT_INTERFACE, NULL); return buffer - buffer0 + i; } -static void usb_free_intf(struct usb_interface *intf) +static void usb_release_interface_cache(struct kref *ref) { + struct usb_interface_cache *intfc = ref_to_usb_interface_cache(ref); int j; - if (intf->altsetting) { - for (j = 0; j < intf->num_altsetting; j++) { - struct usb_host_interface *alt = &intf->altsetting[j]; - - kfree(alt->endpoint); - } - kfree(intf->altsetting); - } - kfree(intf); + for (j = 0; j < intfc->num_altsetting; j++) + kfree(intfc->altsetting[j].endpoint); + kfree(intfc); } static int usb_parse_interface(struct device *ddev, int cfgno, - struct usb_host_config *config, unsigned char *buffer, int size) + struct usb_host_config *config, unsigned char *buffer, int size, + u8 inums[], u8 nalts[]) { unsigned char *buffer0 = buffer; struct usb_interface_descriptor *d; int inum, asnum; - struct usb_interface *interface; + struct usb_interface_cache *intfc; struct usb_host_interface *alt; int i, n; int len, retval; + int num_ep, num_ep_orig; d = (struct usb_interface_descriptor *) buffer; buffer += d->bLength; size -= d->bLength; - if (d->bDescriptorType != USB_DT_INTERFACE) { - dev_err(ddev, "config %d has an unexpected descriptor of type " - "0x%X, expecting interface type 0x%X\n", - cfgno, d->bDescriptorType, USB_DT_INTERFACE); - return -EINVAL; - } + if (d->bLength < USB_DT_INTERFACE_SIZE) + goto skip_to_next_interface_descriptor; + /* Which interface entry is this? */ + intfc = NULL; inum = d->bInterfaceNumber; - if (inum >= config->desc.bNumInterfaces) + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + if (inums[i] == inum) { + intfc = config->intf_cache[i]; + break; + } + } + if (!intfc || intfc->num_altsetting >= nalts[i]) goto skip_to_next_interface_descriptor; - interface = config->interface[inum]; + /* Check for duplicate altsetting entries */ asnum = d->bAlternateSetting; - if (asnum >= interface->num_altsetting) { - dev_err(ddev, "config %d interface %d has an invalid " - "alternate setting number: %d but max is %d\n", - cfgno, inum, asnum, interface->num_altsetting - 1); - return -EINVAL; + for ((i = 0, alt = &intfc->altsetting[0]); + i < intfc->num_altsetting; + (++i, ++alt)) { + if (alt->desc.bAlternateSetting == asnum) { + dev_warn(ddev, "Duplicate descriptor for config %d " + "interface %d altsetting %d, skipping\n", + cfgno, inum, asnum); + goto skip_to_next_interface_descriptor; + } } - alt = &interface->altsetting[asnum]; - if (alt->desc.bLength) { - dev_err(ddev, "Duplicate descriptor for config %d " - "interface %d altsetting %d\n", cfgno, inum, asnum); - return -EINVAL; - } + ++intfc->num_altsetting; memcpy(&alt->desc, d, USB_DT_INTERFACE_SIZE); /* Skip over any Class Specific or Vendor Specific descriptors; @@ -161,41 +171,48 @@ USB_DT_INTERFACE, &n); alt->extralen = i; if (n > 0) - dev_dbg(ddev, "skipped %d class/vendor specific " - "interface descriptors\n", n); + dev_dbg(ddev, "skipped %d descriptor%s after %s\n", + n, plural(n), "interface"); buffer += i; size -= i; - if (alt->desc.bNumEndpoints > USB_MAXENDPOINTS) { - dev_err(ddev, "too many endpoints for config %d interface %d " - "altsetting %d: %d, maximum allowed: %d\n", - cfgno, inum, asnum, alt->desc.bNumEndpoints, - USB_MAXENDPOINTS); - return -EINVAL; + /* Allocate space for the right(?) number of endpoints */ + num_ep = num_ep_orig = alt->desc.bNumEndpoints; + alt->desc.bNumEndpoints = 0; // Use as a counter + if (num_ep > USB_MAXENDPOINTS) { + dev_warn(ddev, "too many endpoints for config %d interface %d " + "altsetting %d: %d, using maximum allowed: %d\n", + cfgno, inum, asnum, num_ep, USB_MAXENDPOINTS); + num_ep = USB_MAXENDPOINTS; } - len = alt->desc.bNumEndpoints * sizeof(struct usb_host_endpoint); + len = sizeof(struct usb_host_endpoint) * num_ep; alt->endpoint = kmalloc(len, GFP_KERNEL); if (!alt->endpoint) return -ENOMEM; memset(alt->endpoint, 0, len); - for (i = 0; i < alt->desc.bNumEndpoints; i++) { - if (size < USB_DT_ENDPOINT_SIZE) { - dev_err(ddev, "too few endpoint descriptors for " - "config %d interface %d altsetting %d\n", - cfgno, inum, asnum); - return -EINVAL; - } - - retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, - alt->endpoint + i, buffer, size); + /* Parse all the endpoint descriptors */ + n = 0; + while (size > 0) { + if (((struct usb_descriptor_header *) buffer)->bDescriptorType + == USB_DT_INTERFACE) + break; + retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, alt, + num_ep, buffer, size); if (retval < 0) return retval; + ++n; buffer += retval; size -= retval; } + + if (n != num_ep_orig) + dev_warn(ddev, "config %d interface %d altsetting %d has %d " + "endpoint descriptor%s, different from the interface " + "descriptor's value: %d\n", + cfgno, inum, asnum, n, plural(n), num_ep_orig); return buffer - buffer0; skip_to_next_interface_descriptor: @@ -207,14 +224,16 @@ int usb_parse_configuration(struct device *ddev, int cfgidx, struct usb_host_config *config, unsigned char *buffer, int size) { + unsigned char *buffer0 = buffer; int cfgno; int nintf, nintf_orig; int i, j, n; - struct usb_interface *interface; + struct usb_interface_cache *intfc; unsigned char *buffer2; int size2; struct usb_descriptor_header *header; int len, retval; + u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES]; memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); if (config->desc.bDescriptorType != USB_DT_CONFIG || @@ -224,7 +243,6 @@ config->desc.bDescriptorType, config->desc.bLength); return -EINVAL; } - config->desc.wTotalLength = size; cfgno = config->desc.bConfigurationValue; buffer += config->desc.bLength; @@ -235,84 +253,110 @@ dev_warn(ddev, "config %d has too many interfaces: %d, " "using maximum allowed: %d\n", cfgno, nintf, USB_MAXINTERFACES); - config->desc.bNumInterfaces = nintf = USB_MAXINTERFACES; - } - - for (i = 0; i < nintf; ++i) { - interface = config->interface[i] = - kmalloc(sizeof(struct usb_interface), GFP_KERNEL); - if (!interface) - return -ENOMEM; - memset(interface, 0, sizeof(struct usb_interface)); + nintf = USB_MAXINTERFACES; } /* Go through the descriptors, checking their length and counting the * number of altsettings for each interface */ + n = 0; for ((buffer2 = buffer, size2 = size); - size2 >= sizeof(struct usb_descriptor_header); + size2 > 0; (buffer2 += header->bLength, size2 -= header->bLength)) { + if (size2 < sizeof(struct usb_descriptor_header)) { + dev_warn(ddev, "config %d descriptor has %d excess " + "byte%s, ignoring\n", + cfgno, size2, plural(size2)); + break; + } + header = (struct usb_descriptor_header *) buffer2; if ((header->bLength > size2) || (header->bLength < 2)) { - dev_err(ddev, "config %d has an invalid descriptor " - "of length %d\n", cfgno, header->bLength); - return -EINVAL; + dev_warn(ddev, "config %d has an invalid descriptor " + "of length %d, skipping remainder of the config\n", + cfgno, header->bLength); + break; } if (header->bDescriptorType == USB_DT_INTERFACE) { struct usb_interface_descriptor *d; + int inum; d = (struct usb_interface_descriptor *) header; if (d->bLength < USB_DT_INTERFACE_SIZE) { - dev_err(ddev, "config %d has an invalid " - "interface descriptor of length %d\n", - cfgno, d->bLength); - return -EINVAL; + dev_warn(ddev, "config %d has an invalid " + "interface descriptor of length %d, " + "skipping\n", cfgno, d->bLength); + continue; } - i = d->bInterfaceNumber; - if (i >= nintf_orig) { - dev_err(ddev, "config %d has an invalid " + inum = d->bInterfaceNumber; + if (inum >= nintf_orig) + dev_warn(ddev, "config %d has an invalid " "interface number: %d but max is %d\n", - cfgno, i, nintf_orig - 1); - return -EINVAL; + cfgno, inum, nintf_orig - 1); + + /* Have we already encountered this interface? + * Count its altsettings */ + for (i = 0; i < n; ++i) { + if (inums[i] == inum) + break; + } + if (i < n) { + if (nalts[i] < 255) + ++nalts[i]; + } else if (n < USB_MAXINTERFACES) { + inums[n] = inum; + nalts[n] = 1; + ++n; } - if (i < nintf) - ++config->interface[i]->num_altsetting; } else if (header->bDescriptorType == USB_DT_DEVICE || - header->bDescriptorType == USB_DT_CONFIG) { - dev_err(ddev, "config %d contains an unexpected " - "descriptor of type 0x%X\n", + header->bDescriptorType == USB_DT_CONFIG) + dev_warn(ddev, "config %d contains an unexpected " + "descriptor of type 0x%X, skipping\n", cfgno, header->bDescriptorType); - return -EINVAL; - } } /* for ((buffer2 = buffer, size2 = size); ...) */ + size = buffer2 - buffer; + config->desc.wTotalLength = buffer2 - buffer0; + + if (n != nintf) + dev_warn(ddev, "config %d has %d interface%s, different from " + "the descriptor's value: %d\n", + cfgno, n, plural(n), nintf_orig); + else if (n == 0) + dev_warn(ddev, "config %d has no interfaces?\n", cfgno); + config->desc.bNumInterfaces = nintf = n; - /* Allocate the altsetting arrays */ + /* Check for missing interface numbers */ for (i = 0; i < nintf; ++i) { - interface = config->interface[i]; - if (interface->num_altsetting > USB_MAXALTSETTING) { - dev_err(ddev, "too many alternate settings for " - "config %d interface %d: %d, " - "maximum allowed: %d\n", - cfgno, i, interface->num_altsetting, - USB_MAXALTSETTING); - return -EINVAL; + for (j = 0; j < nintf; ++j) { + if (inums[j] == i) + break; } - if (interface->num_altsetting == 0) { - dev_err(ddev, "config %d has no interface number " + if (j >= nintf) + dev_warn(ddev, "config %d has no interface number " "%d\n", cfgno, i); - return -EINVAL; + } + + /* Allocate the usb_interface_caches and altsetting arrays */ + for (i = 0; i < nintf; ++i) { + j = nalts[i]; + if (j > USB_MAXALTSETTING) { + dev_warn(ddev, "too many alternate settings for " + "config %d interface %d: %d, " + "using maximum allowed: %d\n", + cfgno, inums[i], j, USB_MAXALTSETTING); + nalts[i] = j = USB_MAXALTSETTING; } - len = sizeof(*interface->altsetting) * - interface->num_altsetting; - interface->altsetting = kmalloc(len, GFP_KERNEL); - if (!interface->altsetting) + len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j; + config->intf_cache[i] = intfc = kmalloc(len, GFP_KERNEL); + if (!intfc) return -ENOMEM; - memset(interface->altsetting, 0, len); + memset(intfc, 0, len); + kref_init(&intfc->ref, usb_release_interface_cache); } /* Skip over any Class Specific or Vendor Specific descriptors; @@ -322,15 +366,15 @@ USB_DT_INTERFACE, &n); config->extralen = i; if (n > 0) - dev_dbg(ddev, "skipped %d class/vendor specific " - "configuration descriptors\n", n); + dev_dbg(ddev, "skipped %d descriptor%s after %s\n", + n, plural(n), "configuration"); buffer += i; size -= i; /* Parse all the interface/altsetting descriptors */ - while (size >= sizeof(struct usb_descriptor_header)) { + while (size > 0) { retval = usb_parse_interface(ddev, cfgno, config, - buffer, size); + buffer, size, inums, nalts); if (retval < 0) return retval; @@ -340,17 +384,20 @@ /* Check for missing altsettings */ for (i = 0; i < nintf; ++i) { - interface = config->interface[i]; - for (j = 0; j < interface->num_altsetting; ++j) { - if (!interface->altsetting[j].desc.bLength) { - dev_err(ddev, "config %d interface %d has no " - "altsetting %d\n", cfgno, i, j); - return -EINVAL; + intfc = config->intf_cache[i]; + for (j = 0; j < intfc->num_altsetting; ++j) { + for (n = 0; n < intfc->num_altsetting; ++n) { + if (intfc->altsetting[n].desc. + bAlternateSetting == j) + break; } + if (n >= intfc->num_altsetting) + dev_warn(ddev, "config %d interface %d has no " + "altsetting %d\n", cfgno, inums[i], j); } } - return size; + return 0; } // hub-only!! ... and only exported for reset/reinit path. @@ -374,10 +421,8 @@ struct usb_host_config *cf = &dev->config[c]; for (i = 0; i < cf->desc.bNumInterfaces; i++) { - struct usb_interface *ifp = cf->interface[i]; - - if (ifp) - usb_free_intf(ifp); + if (cf->intf_cache[i]) + kref_put(&cf->intf_cache[i]->ref); } } kfree(dev->config); @@ -458,21 +503,16 @@ goto err; } if (result < length) { - dev_err(ddev, "config index %d descriptor too short " + dev_warn(ddev, "config index %d descriptor too short " "(expected %i, got %i)\n", cfgno, length, result); - result = -EINVAL; - kfree(bigbuffer); - goto err; + length = result; } dev->rawdescriptors[cfgno] = bigbuffer; result = usb_parse_configuration(&dev->dev, cfgno, &dev->config[cfgno], bigbuffer, length); - if (result > 0) - dev_dbg(ddev, "config index %d descriptor has %d " - "excess byte(s)\n", cfgno, result); - else if (result < 0) { + if (result < 0) { ++cfgno; goto err; } diff -Nru a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c --- a/drivers/usb/core/devices.c Wed May 12 20:33:40 2004 +++ b/drivers/usb/core/devices.c Wed May 12 20:33:40 2004 @@ -232,13 +232,21 @@ return start; } -static char *usb_dump_interface_descriptor(char *start, char *end, const struct usb_interface *iface, int setno) +static char *usb_dump_interface_descriptor(char *start, char *end, + const struct usb_interface_cache *intfc, + const struct usb_interface *iface, + int setno) { - struct usb_interface_descriptor *desc = &iface->altsetting[setno].desc; + const struct usb_interface_descriptor *desc = &intfc->altsetting[setno].desc; + char *driver_name = ""; if (start > end) return start; down_read(&usb_bus_type.subsys.rwsem); + if (iface) + driver_name = (iface->dev.driver + ? iface->dev.driver->name + : "(none)"); start += sprintf(start, format_iface, desc->bInterfaceNumber, desc->bAlternateSetting, @@ -247,9 +255,7 @@ class_decode(desc->bInterfaceClass), desc->bInterfaceSubClass, desc->bInterfaceProtocol, - iface->dev.driver - ? iface->dev.driver->name - : "(none)"); + driver_name); up_read(&usb_bus_type.subsys.rwsem); return start; } @@ -258,13 +264,14 @@ int speed, char *start, char *end, + const struct usb_interface_cache *intfc, const struct usb_interface *iface, int setno ) { - struct usb_host_interface *desc = &iface->altsetting[setno]; + const struct usb_host_interface *desc = &intfc->altsetting[setno]; int i; - start = usb_dump_interface_descriptor(start, end, iface, setno); + start = usb_dump_interface_descriptor(start, end, intfc, iface, setno); for (i = 0; i < desc->desc.bNumEndpoints; i++) { if (start > end) return start; @@ -303,6 +310,7 @@ ) { int i, j; + struct usb_interface_cache *intfc; struct usb_interface *interface; if (start > end) @@ -311,14 +319,13 @@ return start + sprintf(start, "(null Cfg. desc.)\n"); start = usb_dump_config_descriptor(start, end, &config->desc, active); for (i = 0; i < config->desc.bNumInterfaces; i++) { + intfc = config->intf_cache[i]; interface = config->interface[i]; - if (!interface) - break; - for (j = 0; j < interface->num_altsetting; j++) { + for (j = 0; j < intfc->num_altsetting; j++) { if (start > end) return start; start = usb_dump_interface(speed, - start, end, interface, j); + start, end, intfc, interface, j); } } return start; @@ -395,7 +402,7 @@ return start; start = usb_dump_device_strings (start, end, dev); - + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { if (start > end) return start; @@ -445,6 +452,7 @@ * nbytes - the maximum number of bytes to write * skip_bytes - the number of bytes to skip before writing anything * file_offset - the offset into the devices file on completion + * The caller must own the usbdev->serialize semaphore. */ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset, struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count) @@ -545,9 +553,13 @@ /* Now look at all of this device's children. */ for (chix = 0; chix < usbdev->maxchild; chix++) { - if (usbdev->children[chix]) { - ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, usbdev->children[chix], + struct usb_device *childdev = usbdev->children[chix]; + + if (childdev) { + down(&childdev->serialize); + ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, childdev, bus, level + 1, chix, ++cnt); + up(&childdev->serialize); if (ret == -EFAULT) return total_written; total_written += ret; @@ -575,8 +587,11 @@ for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) { /* print devices for this bus */ bus = list_entry(buslist, struct usb_bus, bus_list); + /* recurse through all children of the root hub */ + down(&bus->root_hub->serialize); ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0); + up(&bus->root_hub->serialize); if (ret < 0) { up(&usb_bus_list_lock); return ret; diff -Nru a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c --- a/drivers/usb/core/devio.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/core/devio.c Wed May 12 20:33:39 2004 @@ -60,6 +60,11 @@ struct urb *urb; }; +static inline int connected (struct usb_device *dev) +{ + return dev->state != USB_STATE_NOTATTACHED; +} + static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig) { loff_t ret; @@ -87,14 +92,15 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { struct dev_state *ps = (struct dev_state *)file->private_data; + struct usb_device *dev = ps->dev; ssize_t ret = 0; unsigned len; loff_t pos; int i; pos = *ppos; - down_read(&ps->devsem); - if (!ps->dev) { + down(&dev->serialize); + if (!connected(dev)) { ret = -ENODEV; goto err; } else if (pos < 0) { @@ -106,7 +112,7 @@ len = sizeof(struct usb_device_descriptor) - pos; if (len > nbytes) len = nbytes; - if (copy_to_user(buf, ((char *)&ps->dev->descriptor) + pos, len)) { + if (copy_to_user(buf, ((char *)&dev->descriptor) + pos, len)) { ret = -EFAULT; goto err; } @@ -118,9 +124,9 @@ } pos = sizeof(struct usb_device_descriptor); - for (i = 0; nbytes && i < ps->dev->descriptor.bNumConfigurations; i++) { + for (i = 0; nbytes && i < dev->descriptor.bNumConfigurations; i++) { struct usb_config_descriptor *config = - (struct usb_config_descriptor *)ps->dev->rawdescriptors[i]; + (struct usb_config_descriptor *)dev->rawdescriptors[i]; unsigned int length = le16_to_cpu(config->wTotalLength); if (*ppos < pos + length) { @@ -128,7 +134,7 @@ /* The descriptor may claim to be longer than it * really is. Here is the actual allocated length. */ unsigned alloclen = - ps->dev->config[i].desc.wTotalLength; + dev->config[i].desc.wTotalLength; len = length - (*ppos - pos); if (len > nbytes) @@ -138,7 +144,7 @@ if (alloclen > (*ppos - pos)) { alloclen -= (*ppos - pos); if (copy_to_user(buf, - ps->dev->rawdescriptors[i] + (*ppos - pos), + dev->rawdescriptors[i] + (*ppos - pos), min(len, alloclen))) { ret = -EFAULT; goto err; @@ -155,35 +161,10 @@ } err: - up_read(&ps->devsem); + up(&dev->serialize); return ret; } -extern inline unsigned int ld2(unsigned int x) -{ - unsigned int r = 0; - - if (x >= 0x10000) { - x >>= 16; - r += 16; - } - if (x >= 0x100) { - x >>= 8; - r += 8; - } - if (x >= 0x10) { - x >>= 4; - r += 4; - } - if (x >= 4) { - x >>= 2; - r += 2; - } - if (x >= 2) - r++; - return r; -} - /* * async list handling */ @@ -213,7 +194,7 @@ kfree(as); } -extern __inline__ void async_newpending(struct async *as) +static inline void async_newpending(struct async *as) { struct dev_state *ps = as->ps; unsigned long flags; @@ -223,7 +204,7 @@ spin_unlock_irqrestore(&ps->lock, flags); } -extern __inline__ void async_removepending(struct async *as) +static inline void async_removepending(struct async *as) { struct dev_state *ps = as->ps; unsigned long flags; @@ -233,7 +214,7 @@ spin_unlock_irqrestore(&ps->lock, flags); } -extern __inline__ struct async *async_getcompleted(struct dev_state *ps) +static inline struct async *async_getcompleted(struct dev_state *ps) { unsigned long flags; struct async *as = NULL; @@ -247,7 +228,7 @@ return as; } -extern __inline__ struct async *async_getpending(struct dev_state *ps, void __user *userurb) +static inline struct async *async_getpending(struct dev_state *ps, void __user *userurb) { unsigned long flags; struct async *as; @@ -315,7 +296,7 @@ destroy_async(ps, &hitlist); } -extern __inline__ void destroy_all_async(struct dev_state *ps) +static inline void destroy_all_async(struct dev_state *ps) { destroy_async(ps, &ps->async_pending); } @@ -335,6 +316,7 @@ static void driver_disconnect(struct usb_interface *intf) { struct dev_state *ps = usb_get_intfdata (intf); + unsigned int ifnum = intf->altsetting->desc.bInterfaceNumber; if (!ps) return; @@ -343,13 +325,15 @@ * all pending I/O requests; 2.6 does that. */ - /* prevent new I/O requests */ - ps->dev = 0; - clear_bit(intf->cur_altsetting->desc.bInterfaceNumber, &ps->ifclaimed); + if (likely(ifnum < 8*sizeof(ps->ifclaimed))) + clear_bit(ifnum, &ps->ifclaimed); + else + warn("interface number %u out of range", ifnum); + usb_set_intfdata (intf, NULL); /* force async requests to complete */ - destroy_all_async (ps); + destroy_async_on_interface(ps, ifnum); } struct usb_driver usbdevfs_driver = { @@ -365,7 +349,7 @@ struct usb_interface *iface; int err; - if (intf >= 8*sizeof(ps->ifclaimed) || !dev + if (intf >= 8*sizeof(ps->ifclaimed) || intf >= dev->actconfig->desc.bNumInterfaces) return -EINVAL; /* already claimed */ @@ -395,7 +379,6 @@ return -EINVAL; err = -EINVAL; dev = ps->dev; - down(&dev->serialize); /* lock against other changes to driver bindings */ down_write(&usb_bus_type.subsys.rwsem); if (test_and_clear_bit(intf, &ps->ifclaimed)) { @@ -404,7 +387,6 @@ err = 0; } up_write(&usb_bus_type.subsys.rwsem); - up(&dev->serialize); return err; } @@ -506,7 +488,7 @@ lock_kernel(); ret = -ENOENT; - dev = inode->u.generic_ip; + dev = usb_get_dev(inode->u.generic_ip); if (!dev) { kfree(ps); goto out; @@ -518,7 +500,6 @@ INIT_LIST_HEAD(&ps->async_pending); INIT_LIST_HEAD(&ps->async_completed); init_waitqueue_head(&ps->wait); - init_rwsem(&ps->devsem); ps->discsignr = 0; ps->disctask = current; ps->disccontext = NULL; @@ -535,18 +516,21 @@ static int usbdev_release(struct inode *inode, struct file *file) { struct dev_state *ps = (struct dev_state *)file->private_data; + struct usb_device *dev = ps->dev; unsigned int i; - lock_kernel(); + down(&dev->serialize); list_del_init(&ps->list); - if (ps->dev) { + if (connected(dev)) { for (i = 0; ps->ifclaimed && i < 8*sizeof(ps->ifclaimed); i++) if (test_bit(i, &ps->ifclaimed)) releaseintf(ps, i); + destroy_all_async(ps); } - unlock_kernel(); - destroy_all_async(ps); + up(&dev->serialize); + usb_put_dev(dev); + ps->dev = NULL; kfree(ps); return 0; } @@ -702,13 +686,15 @@ return -EFAULT; if ((ret = findintfif(ps->dev, gd.interface)) < 0) return ret; + down_read(&usb_bus_type.subsys.rwsem); interface = ps->dev->actconfig->interface[ret]; - if (!interface->dev.driver) + if (!interface || !interface->dev.driver) { + up_read(&usb_bus_type.subsys.rwsem); return -ENODATA; + } strncpy(gd.driver, interface->dev.driver->name, sizeof(gd.driver)); - if (copy_to_user(arg, &gd, sizeof(gd))) - return -EFAULT; - return 0; + up_read(&usb_bus_type.subsys.rwsem); + return copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0; } static int proc_connectinfo(struct dev_state *ps, void __user *arg) @@ -724,9 +710,6 @@ static int proc_resetdevice(struct dev_state *ps) { - /* FIXME when usb_reset_device() is fixed we'll need to grab - * ps->dev->serialize before calling it. - */ return usb_reset_device(ps->dev); } @@ -742,10 +725,8 @@ if ((ret = findintfif(ps->dev, setintf.interface)) < 0) return ret; interface = ps->dev->actconfig->interface[ret]; - if (interface->dev.driver) { - if ((ret = checkintf(ps, ret))) - return ret; - } + if ((ret = checkintf(ps, ret))) + return ret; if (usb_set_interface(ps->dev, setintf.interface, setintf.altsetting)) return -EINVAL; return 0; @@ -760,7 +741,6 @@ if (get_user(u, (unsigned int __user *)arg)) return -EFAULT; - down(&ps->dev->serialize); actconfig = ps->dev->actconfig; /* Don't touch the device if any interfaces are claimed. @@ -796,7 +776,6 @@ else status = usb_set_configuration(ps->dev, u); } - up(&ps->dev->serialize); return status; } @@ -873,6 +852,9 @@ /* arbitrary limit */ if (uurb.number_of_packets < 1 || uurb.number_of_packets > 128) return -EINVAL; + if (!(ep_desc = usb_epnum_to_ep_desc(ps->dev, uurb.endpoint))) + return -ENOENT; + interval = 1 << min (15, ep_desc->bInterval - 1); isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb.number_of_packets; if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL))) return -ENOMEM; @@ -898,7 +880,10 @@ uurb.number_of_packets = 0; if (!(ep_desc = usb_epnum_to_ep_desc(ps->dev, uurb.endpoint))) return -ENOENT; - interval = ep_desc->bInterval; + if (ps->dev->speed == USB_SPEED_HIGH) + interval = 1 << min (15, ep_desc->bInterval - 1); + else + interval = ep_desc->bInterval; if (uurb.buffer_length > 16384) return -EINVAL; if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length)) @@ -1012,18 +997,19 @@ DECLARE_WAITQUEUE(wait, current); struct async *as = NULL; void __user *addr; + struct usb_device *dev = ps->dev; int ret; add_wait_queue(&ps->wait, &wait); - while (ps->dev) { + while (connected(dev)) { __set_current_state(TASK_INTERRUPTIBLE); if ((as = async_getcompleted(ps))) break; if (signal_pending(current)) break; - up_read(&ps->devsem); + up(&dev->serialize); schedule(); - down_read(&ps->devsem); + down(&dev->serialize); } remove_wait_queue(&ps->wait, &wait); set_current_state(TASK_RUNNING); @@ -1125,13 +1111,12 @@ } } - if (!ps->dev) { + if (!connected(ps->dev)) { if (buf) kfree(buf); return -ENODEV; } - down(&ps->dev->serialize); if (ps->dev->state != USB_STATE_CONFIGURED) retval = -ENODEV; else if (!(ifp = usb_ifnum_to_if (ps->dev, ctrl.ifno))) @@ -1169,7 +1154,6 @@ } up_read(&usb_bus_type.subsys.rwsem); } - up(&ps->dev->serialize); /* cleanup and return */ if (retval >= 0 @@ -1190,13 +1174,14 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct dev_state *ps = (struct dev_state *)file->private_data; + struct usb_device *dev = ps->dev; int ret = -ENOTTY; if (!(file->f_mode & FMODE_WRITE)) return -EPERM; - down_read(&ps->devsem); - if (!ps->dev) { - up_read(&ps->devsem); + down(&dev->serialize); + if (!connected(dev)) { + up(&dev->serialize); return -ENODEV; } switch (cmd) { @@ -1278,7 +1263,7 @@ ret = proc_ioctl(ps, (void __user *) arg); break; } - up_read(&ps->devsem); + up(&dev->serialize); if (ret >= 0) inode->i_atime = CURRENT_TIME; return ret; @@ -1293,7 +1278,7 @@ poll_wait(file, &ps->wait, wait); if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed)) mask |= POLLOUT | POLLWRNORM; - if (!ps->dev) + if (!connected(ps->dev)) mask |= POLLERR | POLLHUP; return mask; } diff -Nru a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c --- a/drivers/usb/core/hcd-pci.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/core/hcd-pci.c Wed May 12 20:33:39 2004 @@ -279,41 +279,53 @@ { struct usb_hcd *hcd; int retval = 0; + int has_pci_pm; hcd = pci_get_drvdata(dev); - dev_dbg (hcd->self.controller, "suspend D%d --> D%d\n", - dev->current_state, state); - if (pci_find_capability(dev, PCI_CAP_ID_PM)) { - dev_dbg(hcd->self.controller, "No PM capability\n"); - return 0; - } + /* even when the PCI layer rejects some of the PCI calls + * below, HCs can try global suspend and reduce DMA traffic. + * PM-sensitive HCDs may already have done this. + */ + has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); + if (has_pci_pm) + dev_dbg(hcd->self.controller, "suspend D%d --> D%d\n", + dev->current_state, state); switch (hcd->state) { case USB_STATE_HALT: dev_dbg (hcd->self.controller, "halted; hcd not suspended\n"); break; - case USB_STATE_SUSPENDED: + case HCD_STATE_SUSPENDED: dev_dbg (hcd->self.controller, "hcd already suspended\n"); break; default: - /* remote wakeup needs hub->suspend() cooperation */ - // pci_enable_wake (dev, 3, 1); - - pci_save_state (dev, hcd->pci_state); - - /* driver may want to disable DMA etc */ - hcd->state = USB_STATE_QUIESCING; retval = hcd->driver->suspend (hcd, state); if (retval) dev_dbg (hcd->self.controller, "suspend fail, retval %d\n", retval); - else - hcd->state = USB_STATE_SUSPENDED; + else { + hcd->state = HCD_STATE_SUSPENDED; + pci_save_state (dev, hcd->pci_state); +#ifdef CONFIG_USB_SUSPEND + pci_enable_wake (dev, state, hcd->remote_wakeup); + pci_enable_wake (dev, 4, hcd->remote_wakeup); +#endif + /* no DMA or IRQs except in D0 */ + pci_disable_device (dev); + free_irq (hcd->irq, hcd); + + if (has_pci_pm) + retval = pci_set_power_state (dev, state); + if (retval < 0) { + dev_dbg (&dev->dev, + "PCI suspend fail, %d\n", + retval); + (void) usb_hcd_pci_resume (dev); + } + } } - - pci_set_power_state (dev, state); return retval; } EXPORT_SYMBOL (usb_hcd_pci_suspend); @@ -328,23 +340,36 @@ { struct usb_hcd *hcd; int retval; + int has_pci_pm; hcd = pci_get_drvdata(dev); - dev_dbg (hcd->self.controller, "resume from state D%d\n", - dev->current_state); + has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); + if (has_pci_pm) + dev_dbg(hcd->self.controller, "resume from state D%d\n", + dev->current_state); - if (hcd->state != USB_STATE_SUSPENDED) { + if (hcd->state != HCD_STATE_SUSPENDED) { dev_dbg (hcd->self.controller, "can't resume, not suspended!\n"); return -EL3HLT; } hcd->state = USB_STATE_RESUMING; - pci_set_power_state (dev, 0); + if (has_pci_pm) + pci_set_power_state (dev, 0); + retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ, + hcd->description, hcd); + if (retval < 0) { + dev_err (hcd->self.controller, + "can't restore IRQ after resume!\n"); + return retval; + } + pci_set_master (dev); pci_restore_state (dev, hcd->pci_state); - - /* remote wakeup needs hub->suspend() cooperation */ - // pci_enable_wake (dev, 3, 0); +#ifdef CONFIG_USB_SUSPEND + pci_enable_wake (dev, dev->current_state, 0); + pci_enable_wake (dev, 4, 0); +#endif retval = hcd->driver->resume (hcd); if (!HCD_IS_RUNNING (hcd->state)) { diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c --- a/drivers/usb/core/hcd.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/core/hcd.c Wed May 12 20:33:39 2004 @@ -171,10 +171,10 @@ 0x01, /* __u8 bNumInterfaces; (1) */ 0x01, /* __u8 bConfigurationValue; */ 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, + 0xc0, /* __u8 bmAttributes; + Bit 7: must be set, 6: Self-powered, - 5 Remote-wakwup, + 5: Remote wakeup, 4..0: resvd */ 0x00, /* __u8 MaxPower; */ @@ -218,10 +218,10 @@ 0x01, /* __u8 bNumInterfaces; (1) */ 0x01, /* __u8 bConfigurationValue; */ 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, + 0xc0, /* __u8 bmAttributes; + Bit 7: must be set, 6: Self-powered, - 5 Remote-wakwup, + 5: Remote wakeup, 4..0: resvd */ 0x00, /* __u8 MaxPower; */ @@ -324,13 +324,15 @@ /* Root hub control transfers execute synchronously */ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) { - struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet; + struct usb_ctrlrequest *cmd; u16 typeReq, wValue, wIndex, wLength; const u8 *bufp = 0; u8 *ubuf = urb->transfer_buffer; int len = 0; + int patch_wakeup = 0; unsigned long flags; + cmd = (struct usb_ctrlrequest *) urb->setup_packet; typeReq = (cmd->bRequestType << 8) | cmd->bRequest; wValue = le16_to_cpu (cmd->wValue); wIndex = le16_to_cpu (cmd->wIndex); @@ -347,13 +349,21 @@ /* DEVICE REQUESTS */ case DeviceRequest | USB_REQ_GET_STATUS: - // DEVICE_REMOTE_WAKEUP - ubuf [0] = 1; // selfpowered + ubuf [0] = (hcd->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP) + | (1 << USB_DEVICE_SELF_POWERED); ubuf [1] = 0; - /* FALLTHROUGH */ + break; case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (wValue == USB_DEVICE_REMOTE_WAKEUP) + hcd->remote_wakeup = 0; + else + goto error; + break; case DeviceOutRequest | USB_REQ_SET_FEATURE: - dev_dbg (hcd->self.controller, "no device features yet yet\n"); + if (hcd->can_wakeup && wValue == USB_DEVICE_REMOTE_WAKEUP) + hcd->remote_wakeup = 1; + else + goto error; break; case DeviceRequest | USB_REQ_GET_CONFIGURATION: ubuf [0] = 1; @@ -379,6 +389,8 @@ bufp = fs_rh_config_descriptor; len = sizeof fs_rh_config_descriptor; } + if (hcd->can_wakeup) + patch_wakeup = 1; break; case USB_DT_STRING << 8: urb->actual_length = rh_string ( @@ -444,6 +456,11 @@ urb->actual_length = len; // always USB_DIR_IN, toward host memcpy (ubuf, bufp, len); + + /* report whether RH hardware supports remote wakeup */ + if (patch_wakeup) + ((struct usb_config_descriptor *)ubuf)->bmAttributes + |= USB_CONFIG_ATT_WAKEUP; } /* any errors get returned through the urb completion */ @@ -762,10 +779,22 @@ set_bit (devnum, usb_dev->bus->devmap.devicemap); usb_dev->state = USB_STATE_ADDRESS; + usb_dev->epmaxpacketin[0] = usb_dev->epmaxpacketout[0] = 64; + retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); + if (retval != sizeof usb_dev->descriptor) { + dev_dbg (parent_dev, "can't read %s device descriptor %d\n", + usb_dev->dev.bus_id, retval); + return (retval < 0) ? retval : -EMSGSIZE; + } + + (void) usb_get_dev (usb_dev); + down (&usb_dev->serialize); retval = usb_new_device (usb_dev); if (retval) dev_err (parent_dev, "can't register root hub for %s, %d\n", usb_dev->dev.bus_id, retval); + up (&usb_dev->serialize); + usb_put_dev (usb_dev); return retval; } EXPORT_SYMBOL (usb_register_root_hub); diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h --- a/drivers/usb/core/hcd.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/core/hcd.h Wed May 12 20:33:39 2004 @@ -74,6 +74,8 @@ */ struct hc_driver *driver; /* hw-specific hooks */ unsigned saw_irq : 1; + unsigned can_wakeup:1; /* hw supports wakeup? */ + unsigned remote_wakeup:1;/* sw should use wakeup? */ int irq; /* irq allocated */ void *regs; /* device memory/io */ @@ -94,7 +96,7 @@ # define USB_STATE_RUNNING (__ACTIVE) # define USB_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE) # define USB_STATE_RESUMING (__SUSPEND|__TRANSIENT) -# define USB_STATE_SUSPENDED (__SUSPEND) +# define HCD_STATE_SUSPENDED (__SUSPEND) #define HCD_IS_RUNNING(state) ((state) & __ACTIVE) #define HCD_IS_SUSPENDED(state) ((state) & __SUSPEND) @@ -344,9 +346,16 @@ extern int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev); -/* for portability to 2.4, hcds should call this */ static inline int hcd_register_root (struct usb_hcd *hcd) { + /* hcd->driver->start() reported can_wakeup, probably with + * assistance from board's boot firmware. + * NOTE: normal devices won't enable wakeup by default. + */ + if (hcd->can_wakeup) + dev_dbg (hcd->self.controller, "supports USB remote wakeup\n"); + hcd->remote_wakeup = hcd->can_wakeup; + return usb_register_root_hub ( hcd_to_bus (hcd)->root_hub, hcd->self.controller); } diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c --- a/drivers/usb/core/hub.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/core/hub.c Wed May 12 20:33:39 2004 @@ -38,7 +38,6 @@ /* Wakes up khubd */ static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED; -static DECLARE_MUTEX(usb_address0_sem); static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */ static LIST_HEAD(hub_list); /* List of all hubs (for cleanup) */ @@ -373,12 +372,13 @@ struct usb_device *dev; int i; - /* Enable power to the ports */ - dev_dbg(hubdev(interface_to_usbdev(hub->intf)), - "enabling power on all ports\n"); - dev = interface_to_usbdev(hub->intf); - for (i = 0; i < hub->descriptor->bNbrPorts; i++) - set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); + /* if hub supports power switching, enable power on each port */ + if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) < 2) { + dev_dbg(&hub->intf->dev, "enabling power on all ports\n"); + dev = interface_to_usbdev(hub->intf); + for (i = 0; i < hub->descriptor->bNbrPorts; i++) + set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); + } /* Wait for power to be enabled */ wait_ms(hub->descriptor->bPwrOn2PwrGood * 2); @@ -545,8 +545,25 @@ dev_dbg(hub_dev, "power on to power good time: %dms\n", hub->descriptor->bPwrOn2PwrGood * 2); - dev_dbg(hub_dev, "hub controller current requirement: %dmA\n", - hub->descriptor->bHubContrCurrent); + + /* power budgeting mostly matters with bus-powered hubs, + * and battery-powered root hubs (may provide just 8 mA). + */ + ret = usb_get_status(dev, USB_RECIP_DEVICE, 0, &hubstatus); + if (ret < 0) { + message = "can't get hubdev status"; + goto fail; + } + cpu_to_le16s(&hubstatus); + if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) { + dev_dbg(hub_dev, "hub controller current requirement: %dmA\n", + hub->descriptor->bHubContrCurrent); + hub->power_budget = (501 - hub->descriptor->bHubContrCurrent) + / 2; + dev_dbg(hub_dev, "%dmA bus power budget for children\n", + hub->power_budget * 2); + } + ret = hub_hub_status(hub, &hubstatus, &hubchange); if (ret < 0) { @@ -554,12 +571,11 @@ goto fail; } - /* FIXME implement per-port power budgeting; - * enable it for bus-powered hubs. - */ - dev_dbg(hub_dev, "local power source is %s\n", - (hubstatus & HUB_STATUS_LOCAL_POWER) - ? "lost (inactive)" : "good"); + /* local power status reports aren't always correct */ + if (dev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_SELFPOWER) + dev_dbg(hub_dev, "local power source is %s\n", + (hubstatus & HUB_STATUS_LOCAL_POWER) + ? "lost (inactive)" : "good"); if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) == 0) dev_dbg(hub_dev, "%sover-current condition exists\n", @@ -611,6 +627,8 @@ return ret; } +static unsigned highspeed_hubs; + static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); @@ -620,6 +638,9 @@ if (!hub) return; + if (interface_to_usbdev(intf)->speed == USB_SPEED_HIGH) + highspeed_hubs--; + usb_set_intfdata (intf, NULL); spin_lock_irqsave(&hub_event_lock, flags); hub->urb_complete = &urb_complete; @@ -731,6 +752,9 @@ usb_set_intfdata (intf, hub); + if (dev->speed == USB_SPEED_HIGH) + highspeed_hubs++; + if (hub_configure(hub, endpoint) >= 0) return 0; @@ -841,8 +865,11 @@ return ret; } -#define HUB_RESET_TRIES 5 -#define HUB_PROBE_TRIES 2 +#define PORT_RESET_TRIES 5 +#define SET_ADDRESS_TRIES 2 +#define GET_DESCRIPTOR_TRIES 2 +#define SET_CONFIG_TRIES 2 + #define HUB_ROOT_RESET_TIME 50 /* times are in msec */ #define HUB_SHORT_RESET_TIME 10 #define HUB_LONG_RESET_TIME 200 @@ -907,7 +934,7 @@ int i, status; /* Reset the port */ - for (i = 0; i < HUB_RESET_TRIES; i++) { + for (i = 0; i < PORT_RESET_TRIES; i++) { set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); /* return on disconnect or reset */ @@ -934,7 +961,7 @@ return -1; } -int hub_port_disable(struct usb_device *hub, int port) +static int hub_port_disable(struct usb_device *hub, int port) { int ret; @@ -1003,40 +1030,22 @@ return ((portstatus&USB_PORT_STAT_CONNECTION)) ? 0 : 1; } -static void hub_port_connect_change(struct usb_hub *hubstate, int port, - u16 portstatus, u16 portchange) +/* reset device, (re)assign address, get device descriptor. + * device connection is stable, no more debouncing needed. + * returns device in USB_STATE_ADDRESS, except on error. + * on error return, device is no longer usable (ref dropped). + * + * caller owns dev->serialize for the device, guarding against + * config changes and disconnect processing. + */ +static int +hub_port_init (struct usb_device *hub, struct usb_device *dev, int port) { - struct usb_device *hub = interface_to_usbdev(hubstate->intf); - struct usb_device *dev; - unsigned int delay = HUB_SHORT_RESET_TIME; - int i; + static DECLARE_MUTEX(usb_address0_sem); - dev_dbg (&hubstate->intf->dev, - "port %d, status %x, change %x, %s\n", - port + 1, portstatus, portchange, portspeed (portstatus)); - - /* Clear the connection change status */ - clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION); - - /* Disconnect any existing devices under this port */ - if (hub->children[port]) - usb_disconnect(&hub->children[port]); - - /* Return now if nothing is connected */ - if (!(portstatus & USB_PORT_STAT_CONNECTION)) { - if (portstatus & USB_PORT_STAT_ENABLE) - hub_port_disable(hub, port); - - return; - } - - if (hub_port_debounce(hub, port)) { - dev_err (&hubstate->intf->dev, - "connect-debounce failed, port %d disabled\n", - port+1); - hub_port_disable(hub, port); - return; - } + int i, j, retval = -ENODEV; + unsigned delay = HUB_SHORT_RESET_TIME; + enum usb_device_speed oldspeed = dev->speed; /* root hub ports have a slightly longer reset period * (from USB 2.0 spec, section 7.1.7.5) @@ -1046,31 +1055,53 @@ /* Some low speed devices have problems with the quick delay, so */ /* be a bit pessimistic with those devices. RHbug #23670 */ - if (portstatus & USB_PORT_STAT_LOW_SPEED) + if (oldspeed == USB_SPEED_LOW) delay = HUB_LONG_RESET_TIME; down(&usb_address0_sem); - for (i = 0; i < HUB_PROBE_TRIES; i++) { - - /* Allocate a new device struct */ - dev = usb_alloc_dev(hub, hub->bus, port); - if (!dev) { - dev_err (&hubstate->intf->dev, - "couldn't allocate usb_device\n"); - break; - } - - dev->state = USB_STATE_POWERED; - - /* Reset the device, and detect its speed */ - if (hub_port_reset(hub, port, dev, delay)) { - usb_put_dev(dev); - break; - } - - /* Find a new address for it */ + /* Reset the device; full speed may morph to high speed */ + switch (hub_port_reset(hub, port, dev, delay)) { + case 0: /* success, speed is known */ + break; + case 1: /* disconnect, give to companion */ + retval = -EBUSY; + /* FALL THROUGH */ + default: /* error */ + goto fail; + } + if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != dev->speed) { + dev_dbg(&dev->dev, "device reset changed speed!\n"); + goto fail; + } + + /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... + * it's fixed size except for full speed devices. + */ + switch (dev->speed) { + case USB_SPEED_HIGH: /* fixed at 64 */ + i = 64; + break; + case USB_SPEED_FULL: /* 8, 16, 32, or 64 */ + /* to determine the ep0 maxpacket size, read the first 8 + * bytes from the device descriptor to get bMaxPacketSize0; + * then correct our initial (small) guess. + */ + // FALLTHROUGH + case USB_SPEED_LOW: /* fixed at 8 */ + i = 8; + break; + default: + goto fail; + } + dev->epmaxpacketin [0] = i; + dev->epmaxpacketout[0] = i; + + /* set the address */ + if (dev->devnum <= 0) { usb_choose_address(dev); + if (dev->devnum <= 0) + goto fail; /* Set up TT records, if needed */ if (hub->tt) { @@ -1078,12 +1109,21 @@ dev->ttport = hub->ttport; } else if (dev->speed != USB_SPEED_HIGH && hub->speed == USB_SPEED_HIGH) { + struct usb_hub *hubstate; + + hubstate = usb_get_intfdata (hub->actconfig + ->interface[0]); dev->tt = &hubstate->tt; dev->ttport = port + 1; } - dev_info (&dev->dev, - "new %s speed USB device using address %d\n", + /* force the right log message (below) at low speed */ + oldspeed = USB_SPEED_UNKNOWN; + } + + dev_info (&dev->dev, + "%s %s speed USB device using address %d\n", + (oldspeed == USB_SPEED_UNKNOWN) ? "new" : "reset", ({ char *speed; switch (dev->speed) { case USB_SPEED_LOW: speed = "low"; break; case USB_SPEED_FULL: speed = "full"; break; @@ -1091,23 +1131,265 @@ default: speed = "?"; break; }; speed;}), dev->devnum); + + /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way? + * Because device hardware and firmware is sometimes buggy in + * this area, and this is how Linux has done it for ages. + * Change it cautiously. + * + * NOTE: Windows gets the descriptor first, seemingly to help + * work around device bugs like "can't use addresses with bit 3 + * set in certain configurations". Yes, really. + */ + for (i = 0; i < GET_DESCRIPTOR_TRIES; ++i) { + for (j = 0; j < SET_ADDRESS_TRIES; ++j) { + retval = usb_set_address(dev); + if (retval >= 0) + break; + wait_ms(200); + } + if (retval < 0) { + dev_err(&dev->dev, + "device not accepting address %d, error %d\n", + dev->devnum, retval); + fail: + hub_port_disable(hub, port); + clear_bit(dev->devnum, dev->bus->devmap.devicemap); + dev->devnum = -1; + usb_put_dev(dev); + up(&usb_address0_sem); + return retval; + } + + /* cope with hardware quirkiness: + * - let SET_ADDRESS settle, some device hardware wants it + * - read ep0 maxpacket even for high and low speed, + */ + wait_ms(10); + retval = usb_get_device_descriptor(dev, 8); + if (retval >= 8) + break; + wait_ms(100); + } + if (retval != 8) { + dev_err(&dev->dev, "device descriptor read/%s, error %d\n", + "8", retval); + if (retval >= 0) + retval = -EMSGSIZE; + goto fail; + } + if (dev->speed == USB_SPEED_FULL + && (dev->epmaxpacketin [0] + != dev->descriptor.bMaxPacketSize0)) { + usb_disable_endpoint(dev, 0); + usb_endpoint_running(dev, 0, 1); + usb_endpoint_running(dev, 0, 0); + dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0; + dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; + } + + retval = usb_get_device_descriptor(dev, USB_DT_DEVICE_SIZE); + if (retval < (signed)sizeof(dev->descriptor)) { + dev_err(&dev->dev, "device descriptor read/%s, error %d\n", + "all", retval); + if (retval >= 0) + retval = -ENOMSG; + goto fail; + } - /* Run it through the hoops (find a driver, etc) */ - if (usb_new_device(dev) == 0) { - hub->children[port] = dev; - goto done; + /* now dev is visible to other tasks */ + hub->children[port] = dev; + + up(&usb_address0_sem); + return 0; +} + +static void +check_highspeed (struct usb_hub *hub, struct usb_device *dev, int port) +{ + struct usb_qualifier_descriptor *qual; + int status; + + qual = kmalloc (sizeof *qual, SLAB_KERNEL); + if (qual == 0) + return; + + status = usb_get_descriptor (dev, USB_DT_DEVICE_QUALIFIER, 0, + qual, sizeof *qual); + if (status == sizeof *qual) { + dev_info(&dev->dev, "not running at top speed; " + "connect to a high speed hub\n"); + /* hub LEDs are probably harder to miss than syslog */ + if (hub->has_indicators) { + hub->indicator[port] = INDICATOR_GREEN_BLINK; + schedule_work (&hub->leds); } + } + kfree (qual); +} - /* Free the configuration if there was an error */ - usb_put_dev(dev); +static unsigned +hub_power_remaining (struct usb_hub *hubstate, struct usb_device *hub) +{ + int remaining; + unsigned i; - /* Switch to a long reset time */ - delay = HUB_LONG_RESET_TIME; + remaining = hubstate->power_budget; + if (!remaining) /* self-powered */ + return 0; + + for (i = 0; i < hub->maxchild; i++) { + struct usb_device *dev = hub->children[i]; + int delta; + + if (!dev) + continue; + + if (dev->actconfig) + delta = dev->actconfig->desc.bMaxPower; + else + delta = 50; + // dev_dbg(&dev->dev, "budgeted %dmA\n", 2 * delta); + remaining -= delta; + } + if (remaining < 0) { + dev_warn(&hubstate->intf->dev, + "%dmA over power budget!\n", + -2 * remaining); + remaining = 0; } + return remaining; +} + +static void hub_port_connect_change(struct usb_hub *hubstate, int port, + u16 portstatus, u16 portchange) +{ + struct usb_device *hub = interface_to_usbdev(hubstate->intf); + int status, i; + + dev_dbg (&hubstate->intf->dev, + "port %d, status %04x, change %04x, %s\n", + port + 1, portstatus, portchange, portspeed (portstatus)); + + /* Clear the connection change status */ + clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION); - hub_port_disable(hub, port); + if (hubstate->has_indicators) { + set_port_led(hub, hubstate, port + 1, HUB_LED_AUTO); + hubstate->indicator[port] = INDICATOR_AUTO; + } + + /* Disconnect any existing devices under this port */ + if (hub->children[port]) + usb_disconnect(&hub->children[port]); + + /* Return now if nothing is connected */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) { + + /* maybe switch power back on (e.g. root hub was reset) */ + if ((hubstate->descriptor->wHubCharacteristics + & HUB_CHAR_LPSM) < 2 + && !(portstatus & (1 << USB_PORT_FEAT_POWER))) + set_port_feature(hub, port + 1, USB_PORT_FEAT_POWER); + + if (portstatus & USB_PORT_STAT_ENABLE) + goto done; + return; + } + + if (hub_port_debounce(hub, port)) { + dev_err (&hubstate->intf->dev, + "connect-debounce failed, port %d disabled\n", + port+1); + goto done; + } + + for (i = 0; i < SET_CONFIG_TRIES; i++) { + struct usb_device *dev; + + /* reallocate for each attempt, since references + * to the previous one can escape in various ways + */ + dev = usb_alloc_dev(hub, hub->bus, port); + if (!dev) { + dev_err (&hubstate->intf->dev, + "couldn't allocate port %d usb_device\n", port+1); + goto done; + } + dev->state = USB_STATE_POWERED; + + /* hub can tell if it's lowspeed already: D- pullup (not D+) */ + if (portstatus & USB_PORT_STAT_LOW_SPEED) + dev->speed = USB_SPEED_LOW; + else + dev->speed = USB_SPEED_UNKNOWN; + + /* reset, set address, get descriptor, add to hub's children */ + down (&dev->serialize); + status = hub_port_init(hub, dev, port); + if (status == -EBUSY) + break; + if (status < 0) + continue; + + /* consecutive bus-powered hubs aren't reliable; they can + * violate the voltage drop budget. if the new child has + * a "powered" LED, users should notice we didn't enable it + * (without reading syslog), even without per-port LEDs + * on the parent. + */ + if (dev->descriptor.bDeviceClass == USB_CLASS_HUB + && hubstate->power_budget) { + u16 devstat; + + status = usb_get_status(dev, USB_RECIP_DEVICE, 0, + &devstat); + if (status < 0) { + dev_dbg(&dev->dev, "get status %d ?\n", status); + continue; + } + cpu_to_le16s(&devstat); + if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) { + dev_err(&dev->dev, + "can't connect bus-powered hub " + "to this port\n"); + if (hubstate->has_indicators) { + hubstate->indicator[port] = + INDICATOR_AMBER_BLINK; + schedule_work (&hubstate->leds); + } + hub->children[port] = NULL; + usb_put_dev(dev); + hub_port_disable(hub, port); + return; + } + } + + /* check for devices running slower than they could */ + if (dev->descriptor.bcdUSB >= 0x0200 + && dev->speed == USB_SPEED_FULL + && highspeed_hubs != 0) + check_highspeed (hubstate, dev, port); + + /* Run it through the hoops (find a driver, etc) */ + status = usb_new_device(dev); + if (status != 0) { + hub->children[port] = NULL; + continue; + } + up (&dev->serialize); + + status = hub_power_remaining(hubstate, hub); + if (status) + dev_dbg(&hubstate->intf->dev, + "%dmA power budget left\n", + 2 * status); + + return; + } + done: - up(&usb_address0_sem); + hub_port_disable(hub, port); } static void hub_events(void) @@ -1173,7 +1455,7 @@ hub_port_connect_change(hub, i, portstatus, portchange); } else if (portchange & USB_PORT_STAT_C_ENABLE) { dev_dbg (hubdev (dev), - "port %d enable change, status %x\n", + "port %d enable change, status %08x\n", i + 1, portstatus); clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); @@ -1331,25 +1613,68 @@ usb_deregister(&hub_driver); } /* usb_hub_cleanup() */ + +static int config_descriptors_changed(struct usb_device *dev) +{ + unsigned index; + unsigned len = 0; + struct usb_config_descriptor *buf; + + for (index = 0; index < dev->descriptor.bNumConfigurations; index++) { + if (len < dev->config[index].desc.wTotalLength) + len = dev->config[index].desc.wTotalLength; + } + buf = kmalloc (len, SLAB_KERNEL); + if (buf == 0) { + dev_err(&dev->dev, "no mem to re-read configs after reset\n"); + /* assume the worst */ + return 1; + } + for (index = 0; index < dev->descriptor.bNumConfigurations; index++) { + int length; + int old_length = dev->config[index].desc.wTotalLength; + + length = usb_get_descriptor(dev, USB_DT_CONFIG, index, buf, + old_length); + if (length < old_length) { + dev_dbg(&dev->dev, "config index %d, error %d\n", + index, length); + break; + } + if (memcmp (buf, dev->rawdescriptors[index], old_length) + != 0) { + dev_dbg(&dev->dev, "config index %d changed (#%d)\n", + index, buf->bConfigurationValue); +/* FIXME enable this when we can re-enumerate after reset; + * until then DFU-ish drivers need this and other workarounds + */ +// break; + } + } + kfree(buf); + return index != dev->descriptor.bNumConfigurations; +} + /* - * WARNING - If a driver calls usb_reset_device, you should simulate a - * disconnect() and probe() for other interfaces you doesn't claim. This - * is left up to the driver writer right now. This insures other drivers - * have a chance to re-setup their interface. + * WARNING - don't reset any device unless drivers for all of its + * interfaces are expecting that reset! Maybe some driver->reset() + * method should eventually help ensure sufficient cooperation. * - * Take a look at proc_resetdevice in devio.c for some sample code to - * do this. - * Use this only from within your probe function, otherwise use - * usb_reset_device() below, which ensure proper locking + * This is the same as usb_reset_device() except that the caller + * already holds dev->serialize. For example, it's safe to use + * this from a driver probe() routine after downloading new firmware. */ -int usb_physical_reset_device(struct usb_device *dev) +int __usb_reset_device(struct usb_device *dev) { struct usb_device *parent = dev->parent; - struct usb_device_descriptor *descriptor; + struct usb_device_descriptor descriptor = dev->descriptor; int i, ret, port = -1; - if (!parent) { - err("attempting to reset root hub!"); + if (dev->maxchild) { + /* this requires hub- or hcd-specific logic; + * see hub_reset() and OHCI hc_restart() + */ + dev_dbg(&dev->dev, "%s for hub!\n", __FUNCTION__); return -EINVAL; } @@ -1362,114 +1687,69 @@ if (port < 0) return -ENOENT; - descriptor = kmalloc(sizeof *descriptor, GFP_NOIO); - if (!descriptor) { - return -ENOMEM; - } - - down(&usb_address0_sem); - - /* Send a reset to the device */ - if (hub_port_reset(parent, port, dev, HUB_SHORT_RESET_TIME)) { - hub_port_disable(parent, port); - up(&usb_address0_sem); - kfree(descriptor); - return(-ENODEV); - } - - /* Reprogram the Address */ - ret = usb_set_address(dev); - if (ret < 0) { - err("USB device not accepting new address (error=%d)", ret); - hub_port_disable(parent, port); - up(&usb_address0_sem); - kfree(descriptor); - return ret; - } - - /* Let the SET_ADDRESS settle */ - wait_ms(10); - - up(&usb_address0_sem); - - /* - * Now we fetch the configuration descriptors for the device and - * see if anything has changed. If it has, we dump the current - * parsed descriptors and reparse from scratch. Then we leave - * the device alone for the caller to finish setting up. - * - * If nothing changed, we reprogram the configuration and then - * the alternate settings. - */ - - ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, descriptor, - sizeof(*descriptor)); - if (ret < 0) { - kfree(descriptor); - return ret; - } - - le16_to_cpus(&descriptor->bcdUSB); - le16_to_cpus(&descriptor->idVendor); - le16_to_cpus(&descriptor->idProduct); - le16_to_cpus(&descriptor->bcdDevice); - - if (memcmp(&dev->descriptor, descriptor, sizeof(*descriptor))) { - kfree(descriptor); - usb_destroy_configuration(dev); - - /* FIXME Linux doesn't yet handle these "device morphed" - * paths. DFU variants need this to work ... and they - * include the "config descriptors changed" case this - * doesn't yet detect! - */ - dev->state = USB_STATE_NOTATTACHED; - dev_err(&dev->dev, "device morphed (DFU?), nyet supported\n"); - - return -ENODEV; - } - - kfree(descriptor); + ret = hub_port_init(parent, dev, port); + if (ret < 0) + goto re_enumerate; + + /* Device might have changed firmware (DFU or similar) */ + if (memcmp(&dev->descriptor, &descriptor, sizeof descriptor) + || config_descriptors_changed (dev)) { + dev_info(&dev->dev, "device firmware changed\n"); + dev->descriptor = descriptor; /* for disconnect() calls */ + goto re_enumerate; + } + + if (!dev->actconfig) + return 0; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, dev->actconfig->desc.bConfigurationValue, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT); if (ret < 0) { - err("failed to set dev %s active configuration (error=%d)", - dev->devpath, ret); - return ret; - } + dev_err(&dev->dev, + "can't restore configuration #%d (error=%d)\n", + dev->actconfig->desc.bConfigurationValue, ret); + goto re_enumerate; + } dev->state = USB_STATE_CONFIGURED; for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { struct usb_interface *intf = dev->actconfig->interface[i]; struct usb_interface_descriptor *desc; + /* set_interface resets host side toggle and halt status even + * for altsetting zero. the interface may have no driver. + */ desc = &intf->cur_altsetting->desc; ret = usb_set_interface(dev, desc->bInterfaceNumber, desc->bAlternateSetting); if (ret < 0) { - err("failed to set active alternate setting " - "for dev %s interface %d (error=%d)", - dev->devpath, desc->bInterfaceNumber, ret); - return ret; + dev_err(&dev->dev, "failed to restore interface %d " + "altsetting %d (error=%d)\n", + desc->bInterfaceNumber, + desc->bAlternateSetting, + ret); + goto re_enumerate; } } return 0; + +re_enumerate: + /* FIXME make some task re-enumerate; don't just mark unusable */ + dev->state = USB_STATE_NOTATTACHED; + return -ENODEV; } +EXPORT_SYMBOL(__usb_reset_device); int usb_reset_device(struct usb_device *udev) { - struct device *gdev = &udev->dev; int r; - down_read(&gdev->bus->subsys.rwsem); - r = usb_physical_reset_device(udev); - up_read(&gdev->bus->subsys.rwsem); + down(&udev->serialize); + r = __usb_reset_device(udev); + up(&udev->serialize); return r; } - - diff -Nru a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h --- a/drivers/usb/core/hub.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/core/hub.h Wed May 12 20:33:39 2004 @@ -209,6 +209,8 @@ struct semaphore khubd_sem; struct usb_tt tt; /* Transaction Translator */ + u8 power_budget; /* in 2mA units; or zero */ + unsigned has_indicators:1; enum hub_led_mode indicator[USB_MAXCHILDREN]; struct work_struct leds; diff -Nru a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c --- a/drivers/usb/core/inode.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/core/inode.c Wed May 12 20:33:39 2004 @@ -717,9 +717,6 @@ while (!list_empty(&dev->filelist)) { ds = list_entry(dev->filelist.next, struct dev_state, list); list_del_init(&ds->list); - down_write(&ds->devsem); - ds->dev = NULL; - up_write(&ds->devsem); if (ds->discsignr) { sinfo.si_signo = SIGPIPE; sinfo.si_errno = EPIPE; diff -Nru a/drivers/usb/core/message.c b/drivers/usb/core/message.c --- a/drivers/usb/core/message.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/core/message.c Wed May 12 20:33:39 2004 @@ -796,10 +796,6 @@ } } -static void release_interface(struct device *dev) -{ -} - /* * usb_disable_device - Disable all the endpoints for a USB device * @dev: the device whose endpoints are being disabled @@ -835,6 +831,7 @@ dev_dbg (&dev->dev, "unregistering interface %s\n", interface->dev.bus_id); device_unregister (&interface->dev); + dev->actconfig->interface[i] = NULL; } dev->actconfig = 0; if (dev->state == USB_STATE_CONFIGURED) @@ -1071,6 +1068,16 @@ return 0; } +static void release_interface(struct device *dev) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_interface_cache *intfc = + altsetting_to_usb_interface_cache(intf->altsetting); + + kref_put(&intfc->ref); + kfree(intf); +} + /* * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated @@ -1109,19 +1116,19 @@ { int i, ret; struct usb_host_config *cp = NULL; - + struct usb_interface **new_interfaces = NULL; + int n, nintf; + /* dev->serialize guards all config changes */ - for (i=0; idescriptor.bNumConfigurations; i++) { + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { if (dev->config[i].desc.bConfigurationValue == configuration) { cp = &dev->config[i]; break; } } - if ((!cp && configuration != 0)) { - ret = -EINVAL; - goto out; - } + if ((!cp && configuration != 0)) + return -EINVAL; /* The USB spec says configuration 0 means unconfigured. * But if a device includes a configuration numbered 0, @@ -1130,6 +1137,34 @@ if (cp && configuration == 0) dev_warn(&dev->dev, "config 0 descriptor??\n"); + /* Allocate memory for new interfaces before doing anything else, + * so that if we run out then nothing will have changed. */ + n = nintf = 0; + if (cp) { + nintf = cp->desc.bNumInterfaces; + new_interfaces = kmalloc(nintf * sizeof(*new_interfaces), + GFP_KERNEL); + if (!new_interfaces) { + dev_err(&dev->dev, "Out of memory"); + return -ENOMEM; + } + + for (; n < nintf; ++n) { + new_interfaces[n] = kmalloc( + sizeof(struct usb_interface), + GFP_KERNEL); + if (!new_interfaces[n]) { + dev_err(&dev->dev, "Out of memory"); + ret = -ENOMEM; +free_interfaces: + while (--n >= 0) + kfree(new_interfaces[n]); + kfree(new_interfaces); + return ret; + } + } + } + /* if it's already configured, clear out old state first. * getting rid of old interfaces means unbinding their drivers. */ @@ -1139,7 +1174,7 @@ if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 0) - goto out; + goto free_interfaces; dev->actconfig = cp; if (!cp) @@ -1147,14 +1182,21 @@ else { dev->state = USB_STATE_CONFIGURED; - /* re-initialize hc/hcd/usbcore interface/endpoint state. - * this triggers binding of drivers to interfaces; and - * maybe probe() calls will choose different altsettings. + /* Initialize the new interface structures and the + * hc/hcd/usbcore interface/endpoint state. */ - for (i = 0; i < cp->desc.bNumInterfaces; ++i) { - struct usb_interface *intf = cp->interface[i]; + for (i = 0; i < nintf; ++i) { + struct usb_interface_cache *intfc; + struct usb_interface *intf; struct usb_host_interface *alt; + cp->interface[i] = intf = new_interfaces[i]; + memset(intf, 0, sizeof(*intf)); + intfc = cp->intf_cache[i]; + intf->altsetting = intfc->altsetting; + intf->num_altsetting = intfc->num_altsetting; + kref_get(&intfc->ref); + alt = usb_altnum_to_altsetting(intf, 0); /* No altsetting 0? We'll assume the first altsetting. @@ -1178,12 +1220,15 @@ configuration, alt->desc.bInterfaceNumber); } + kfree(new_interfaces); - /* Now that all interfaces are setup, probe() calls - * may claim() any interface that's not yet bound. - * Many class drivers need that: CDC, audio, video, etc. + /* Now that all the interfaces are set up, register them + * to trigger binding of drivers to interfaces. probe() + * routines may install different altsettings and may + * claim() any interfaces not yet bound. Many class drivers + * need that: CDC, audio, video, etc. */ - for (i = 0; i < cp->desc.bNumInterfaces; ++i) { + for (i = 0; i < nintf; ++i) { struct usb_interface *intf = cp->interface[i]; struct usb_interface_descriptor *desc; @@ -1204,7 +1249,6 @@ } } -out: return ret; } diff -Nru a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c --- a/drivers/usb/core/urb.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/core/urb.c Wed May 12 20:33:39 2004 @@ -13,6 +13,14 @@ #include #include "hcd.h" +#define to_urb(d) container_of(d, struct urb, kref) + +static void urb_destroy(struct kref *kref) +{ + struct urb *urb = to_urb(kref); + kfree(urb); +} + /** * usb_init_urb - initializes a urb so that it can be used by a USB driver * @urb: pointer to the urb to initialize @@ -31,7 +39,7 @@ { if (urb) { memset(urb, 0, sizeof(*urb)); - urb->count = (atomic_t)ATOMIC_INIT(1); + kref_init(&urb->kref, urb_destroy); spin_lock_init(&urb->lock); } } @@ -80,8 +88,7 @@ void usb_free_urb(struct urb *urb) { if (urb) - if (atomic_dec_and_test(&urb->count)) - kfree(urb); + kref_put(&urb->kref); } /** @@ -96,11 +103,9 @@ */ struct urb * usb_get_urb(struct urb *urb) { - if (urb) { - atomic_inc(&urb->count); - return urb; - } else - return NULL; + if (urb) + kref_get(&urb->kref); + return urb; } @@ -232,6 +237,8 @@ (dev->state < USB_STATE_DEFAULT) || (!dev->bus) || (dev->devnum <= 0)) return -ENODEV; + if (dev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; if (!(op = dev->bus->op) || !op->submit_urb) return -ENODEV; diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c --- a/drivers/usb/core/usb.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/core/usb.c Wed May 12 20:33:39 2004 @@ -1064,92 +1064,29 @@ } /* - * By the time we get here, we chose a new device address - * and is in the default state. We need to identify the thing and - * get the ball rolling.. + * usb_new_device - perform initial device setup (usbcore-internal) + * @dev: newly addressed device (in ADDRESS state) * - * Returns 0 for success, != 0 for error. + * This is called with devices which have been enumerated, but not yet + * configured. The device descriptor is available, but not descriptors + * for any device configuration. The caller owns dev->serialize, and + * the device is not visible through sysfs or other filesystem code. + * + * Returns 0 for success (device is configured and listed, with its + * interfaces, in sysfs); else a negative errno value. On error, one + * reference count to the device has been dropped. * * This call is synchronous, and may not be used in an interrupt context. * * Only the hub driver should ever call this; root hub registration * uses it only indirectly. */ -#define NEW_DEVICE_RETRYS 2 -#define SET_ADDRESS_RETRYS 2 int usb_new_device(struct usb_device *dev) { - int err = -EINVAL; + int err; int i; - int j; int config; - /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... - * it's fixed size except for full speed devices. - */ - switch (dev->speed) { - case USB_SPEED_HIGH: /* fixed at 64 */ - i = 64; - break; - case USB_SPEED_FULL: /* 8, 16, 32, or 64 */ - /* to determine the ep0 maxpacket size, read the first 8 - * bytes from the device descriptor to get bMaxPacketSize0; - * then correct our initial (small) guess. - */ - // FALLTHROUGH - case USB_SPEED_LOW: /* fixed at 8 */ - i = 8; - break; - default: - goto fail; - } - dev->epmaxpacketin [0] = i; - dev->epmaxpacketout[0] = i; - - for (i = 0; i < NEW_DEVICE_RETRYS; ++i) { - - for (j = 0; j < SET_ADDRESS_RETRYS; ++j) { - err = usb_set_address(dev); - if (err >= 0) - break; - wait_ms(200); - } - if (err < 0) { - dev_err(&dev->dev, - "device not accepting address %d, error %d\n", - dev->devnum, err); - goto fail; - } - - wait_ms(10); /* Let the SET_ADDRESS settle */ - - /* high and low speed devices don't need this... */ - err = usb_get_device_descriptor(dev, 8); - if (err >= 8) - break; - wait_ms(100); - } - - if (err < 8) { - dev_err(&dev->dev, "device descriptor read/8, error %d\n", err); - goto fail; - } - if (dev->speed == USB_SPEED_FULL) { - usb_disable_endpoint(dev, 0); - usb_endpoint_running(dev, 0, 1); - usb_endpoint_running(dev, 0, 0); - dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0; - dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; - } - - /* USB device state == addressed ... still not usable */ - - err = usb_get_device_descriptor(dev, sizeof(dev->descriptor)); - if (err != (signed)sizeof(dev->descriptor)) { - dev_err(&dev->dev, "device descriptor read/all, error %d\n", err); - goto fail; - } - err = usb_get_configuration(dev); if (err < 0) { dev_err(&dev->dev, "can't read configurations, error %d\n", @@ -1170,13 +1107,10 @@ usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber); #endif - down(&dev->serialize); - /* put device-specific files into sysfs */ err = device_add (&dev->dev); if (err) { dev_err(&dev->dev, "can't device_add, error %d\n", err); - up(&dev->serialize); goto fail; } usb_create_driverfs_dev_files (dev); @@ -1193,7 +1127,7 @@ /* heuristic: Linux is more likely to have class * drivers, so avoid vendor-specific interfaces. */ - desc = &dev->config[i].interface[0] + desc = &dev->config[i].intf_cache[0] ->altsetting->desc; if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC) continue; @@ -1211,7 +1145,6 @@ dev->descriptor.bNumConfigurations); } err = usb_set_configuration(dev, config); - up(&dev->serialize); if (err) { dev_err(&dev->dev, "can't set config #%d, error %d\n", config, err); @@ -1226,9 +1159,10 @@ return 0; fail: - dev->state = USB_STATE_DEFAULT; + dev->state = USB_STATE_NOTATTACHED; clear_bit(dev->devnum, dev->bus->devmap.devicemap); dev->devnum = -1; + usb_put_dev(dev); return err; } @@ -1577,20 +1511,40 @@ */ static int __init usb_init(void) { + int retval; if (nousb) { pr_info ("%s: USB support disabled\n", usbcore_name); return 0; } - bus_register(&usb_bus_type); + retval = bus_register(&usb_bus_type); + if (retval) + goto out; usb_host_init(); - usb_major_init(); - usbfs_init(); - usb_hub_init(); - - driver_register(&usb_generic_driver); - - return 0; + retval = usb_major_init(); + if (retval) + goto major_init_failed; + retval = usbfs_init(); + if (retval) + goto fs_init_failed; + retval = usb_hub_init(); + if (retval) + goto hub_init_failed; + + retval = driver_register(&usb_generic_driver); + if (!retval) + goto out; + + usb_hub_cleanup(); +hub_init_failed: + usbfs_cleanup(); +fs_init_failed: + usb_major_cleanup(); +major_init_failed: + usb_host_cleanup(); + bus_unregister(&usb_bus_type); +out: + return retval; } /* diff -Nru a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c --- a/drivers/usb/gadget/dummy_hcd.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/gadget/dummy_hcd.c Wed May 12 20:33:39 2004 @@ -144,6 +144,7 @@ struct usb_gadget_driver *driver; struct dummy_request fifo_req; u8 fifo_buf [FIFO_SIZE]; + u16 devstatus; struct hcd_dev *hdev; @@ -156,6 +157,8 @@ u32 port_status; int started; struct completion released; + unsigned resuming:1; + unsigned long re_timeout; }; static struct dummy *the_controller; @@ -556,8 +559,37 @@ return tv.tv_usec / 1000; } +static int dummy_wakeup (struct usb_gadget *_gadget) +{ + struct dummy *dum; + + dum = container_of (_gadget, struct dummy, gadget); + if ((dum->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) == 0 + || !(dum->port_status & (1 << USB_PORT_FEAT_SUSPEND))) + return -EINVAL; + + /* hub notices our request, issues downstream resume, etc */ + dum->resuming = 1; + dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND); + return 0; +} + +static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value) +{ + struct dummy *dum; + + dum = container_of (_gadget, struct dummy, gadget); + if (value) + dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED); + else + dum->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); + return 0; +} + static const struct usb_gadget_ops dummy_ops = { .get_frame = dummy_g_get_frame, + .wakeup = dummy_wakeup, + .set_selfpowered = dummy_set_selfpowered, }; /*-------------------------------------------------------------------------*/ @@ -653,6 +685,9 @@ dum->gadget.ops = &dummy_ops; dum->gadget.is_dualspeed = 1; + dum->devstatus = 0; + dum->resuming = 0; + INIT_LIST_HEAD (&dum->gadget.ep_list); for (i = 0; i < DUMMY_ENDPOINTS; i++) { struct dummy_ep *ep = &dum->ep [i]; @@ -1130,8 +1165,19 @@ break; case USB_REQ_SET_FEATURE: if (setup.bRequestType == Dev_Request) { - // remote wakeup, and (hs) test mode - value = -EOPNOTSUPP; + value = 0; + switch (setup.wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + break; + default: + value = -EOPNOTSUPP; + } + if (value == 0) { + dum->devstatus |= + (1 << setup.wValue); + maybe_set_status (urb, 0); + } + } else if (setup.bRequestType == Ep_Request) { // endpoint halt ep2 = find_endpoint (dum, @@ -1147,9 +1193,17 @@ break; case USB_REQ_CLEAR_FEATURE: if (setup.bRequestType == Dev_Request) { - // remote wakeup - value = 0; - maybe_set_status (urb, 0); + switch (setup.wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + dum->devstatus &= ~(1 << + USB_DEVICE_REMOTE_WAKEUP); + value = 0; + maybe_set_status (urb, 0); + break; + default: + value = -EOPNOTSUPP; + break; + } } else if (setup.bRequestType == Ep_Request) { // endpoint halt ep2 = find_endpoint (dum, @@ -1185,6 +1239,10 @@ break; } buf [0] = ep2->halted; + } else if (setup.bRequestType == + Dev_InRequest) { + buf [0] = (u8) + dum->devstatus; } else buf [0] = 0; } @@ -1338,8 +1396,21 @@ case ClearHubFeature: break; case ClearPortFeature: - // FIXME won't some of these need special handling? - dum->port_status &= ~(1 << wValue); + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + /* 20msec resume signaling */ + dum->resuming = 1; + dum->re_timeout = jiffies + ((HZ * 20)/1000); + break; + case USB_PORT_FEAT_POWER: + dum->port_status = 0; + dum->address = 0; + dum->hdev = 0; + dum->resuming = 0; + break; + default: + dum->port_status &= ~(1 << wValue); + } break; case GetHubDescriptor: hub_descriptor ((struct usb_hub_descriptor *) buf); @@ -1350,33 +1421,28 @@ case GetPortStatus: if (wIndex != 1) retval = -EPIPE; - ((u16 *) buf)[0] = cpu_to_le16 (dum->port_status); - ((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16); - break; - case SetHubFeature: - retval = -EPIPE; - break; - case SetPortFeature: - if (wValue == USB_PORT_FEAT_RESET) { - /* if it's already running, disconnect first */ - if (dum->port_status & USB_PORT_STAT_ENABLE) { - dum->port_status &= ~(USB_PORT_STAT_ENABLE - | USB_PORT_STAT_LOW_SPEED - | USB_PORT_STAT_HIGH_SPEED); - if (dum->driver) { - dev_dbg (hardware, "disconnect\n"); - stop_activity (dum, dum->driver); - } - /* FIXME test that code path! */ - } else - dum->port_status |= - (1 << USB_PORT_FEAT_C_ENABLE); - - dum->port_status |= USB_PORT_STAT_ENABLE | - (1 << USB_PORT_FEAT_C_RESET); + /* whoever resets or resumes must GetPortStatus to + * complete it!! + */ + if (dum->resuming && time_after (jiffies, dum->re_timeout)) { + dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND); + dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND); + dum->resuming = 0; + dum->re_timeout = 0; + if (dum->driver->resume) { + spin_unlock (&dum->lock); + dum->driver->resume (&dum->gadget); + spin_lock (&dum->lock); + } + } + if ((dum->port_status & (1 << USB_PORT_FEAT_RESET)) != 0 + && time_after (jiffies, dum->re_timeout)) { + dum->port_status |= (1 << USB_PORT_FEAT_C_RESET); + dum->port_status &= ~(1 << USB_PORT_FEAT_RESET); + dum->re_timeout = 0; if (dum->driver) { - + dum->port_status |= USB_PORT_STAT_ENABLE; /* give it the best speed we agree on */ dum->gadget.speed = dum->driver->speed; dum->gadget.ep0->maxpacket = 64; @@ -1395,8 +1461,42 @@ break; } } - } else + } + ((u16 *) buf)[0] = cpu_to_le16 (dum->port_status); + ((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16); + break; + case SetHubFeature: + retval = -EPIPE; + break; + case SetPortFeature: + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + dum->port_status |= (1 << USB_PORT_FEAT_SUSPEND); + if (dum->driver->suspend) { + spin_unlock (&dum->lock); + dum->driver->suspend (&dum->gadget); + spin_lock (&dum->lock); + } + break; + case USB_PORT_FEAT_RESET: + /* if it's already running, disconnect first */ + if (dum->port_status & USB_PORT_STAT_ENABLE) { + dum->port_status &= ~(USB_PORT_STAT_ENABLE + | USB_PORT_STAT_LOW_SPEED + | USB_PORT_STAT_HIGH_SPEED); + if (dum->driver) { + dev_dbg (hardware, "disconnect\n"); + stop_activity (dum, dum->driver); + } + + /* FIXME test that code path! */ + } + /* 50msec reset signaling */ + dum->re_timeout = jiffies + ((HZ * 50)/1000); + /* FALLTHROUGH */ + default: dum->port_status |= (1 << wValue); + } break; default: diff -Nru a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c --- a/drivers/usb/gadget/file_storage.c Wed May 12 20:33:40 2004 +++ b/drivers/usb/gadget/file_storage.c Wed May 12 20:33:40 2004 @@ -2054,7 +2054,7 @@ buf[0] = 0x80 | 0x70; // Valid, current error buf[2] = SK(sd); put_be32(&buf[3], sdinfo); // Sense information - buf[7] = 18 - 7; // Additional sense length + buf[7] = 18 - 8; // Additional sense length buf[12] = ASC(sd); buf[13] = ASCQ(sd); return 18; diff -Nru a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h --- a/drivers/usb/gadget/gadget_chips.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/gadget/gadget_chips.h Wed May 12 20:33:39 2004 @@ -20,7 +20,7 @@ #define gadget_is_dummy(g) 0 #endif -#ifdef CONFIG_USB_GADGET_PXA +#ifdef CONFIG_USB_GADGET_PXA2XX #define gadget_is_pxa(g) !strcmp("pxa2xx_udc", (g)->name) #else #define gadget_is_pxa(g) 0 diff -Nru a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c --- a/drivers/usb/gadget/serial.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/gadget/serial.c Wed May 12 20:33:39 2004 @@ -157,8 +157,12 @@ /* debug macro */ #if G_SERIAL_DEBUG - static int debug = G_SERIAL_DEBUG; +#else +static int debug = 0; +#endif + +#if G_SERIAL_DEBUG #define gs_debug(format, arg...) \ do { if (debug) printk(KERN_DEBUG format, ## arg); } while(0) diff -Nru a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c --- a/drivers/usb/gadget/zero.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/gadget/zero.c Wed May 12 20:33:39 2004 @@ -130,6 +130,9 @@ */ u8 config; struct usb_ep *in_ep, *out_ep; + + /* autoresume timer */ + struct timer_list resume; }; #define xprintk(d,level,fmt,args...) \ @@ -167,6 +170,12 @@ module_param (qlen, uint, S_IRUGO|S_IWUSR); module_param (pattern, uint, S_IRUGO|S_IWUSR); +/* + * if it's nonzero, autoresume says how many seconds to wait + * before trying to wake up the host after suspend. + */ +static unsigned autoresume = 0; +module_param (autoresume, uint, 0); /* * Normally the "loopback" configuration is second (index 1) so @@ -224,7 +233,7 @@ .bNumConfigurations = 2, }; -static const struct usb_config_descriptor +static struct usb_config_descriptor source_sink_config = { .bLength = sizeof source_sink_config, .bDescriptorType = USB_DT_CONFIG, @@ -237,7 +246,7 @@ .bMaxPower = 1, /* self-powered */ }; -static const struct usb_config_descriptor +static struct usb_config_descriptor loopback_config = { .bLength = sizeof loopback_config, .bDescriptorType = USB_DT_CONFIG, @@ -1060,6 +1069,19 @@ */ } +static void +zero_autoresume (unsigned long _dev) +{ + struct zero_dev *dev = (struct zero_dev *) _dev; + int status; + + /* normally the host would be woken up for something + * more significant than just a timer firing... + */ + status = usb_gadget_wakeup (dev->gadget); + DBG (dev, "wakeup --> %d\n", status); +} + /*-------------------------------------------------------------------------*/ static void @@ -1072,6 +1094,7 @@ /* we've already been disconnected ... no i/o is active */ if (dev->req) free_ep_req (gadget->ep0, dev->req); + del_timer_sync (&dev->resume); kfree (dev); set_gadget_data (gadget, 0); } @@ -1176,6 +1199,14 @@ usb_gadget_set_selfpowered (gadget); + init_timer (&dev->resume); + dev->resume.function = zero_autoresume; + dev->resume.data = (unsigned long) dev; + if (autoresume) { + source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + gadget->ep0->driver_data = dev; INFO (dev, "%s, version: " DRIVER_VERSION "\n", longname); @@ -1195,6 +1226,33 @@ /*-------------------------------------------------------------------------*/ +static void +zero_suspend (struct usb_gadget *gadget) +{ + struct zero_dev *dev = get_gadget_data (gadget); + + if (gadget->speed == USB_SPEED_UNKNOWN) + return; + + if (autoresume) { + mod_timer (&dev->resume, jiffies + (HZ * autoresume)); + DBG (dev, "suspend, wakeup in %d seconds\n", autoresume); + } else + DBG (dev, "suspend\n"); +} + +static void +zero_resume (struct usb_gadget *gadget) +{ + struct zero_dev *dev = get_gadget_data (gadget); + + DBG (dev, "resume\n"); + del_timer (&dev->resume); +} + + +/*-------------------------------------------------------------------------*/ + static struct usb_gadget_driver zero_driver = { #ifdef CONFIG_USB_GADGET_DUALSPEED .speed = USB_SPEED_HIGH, @@ -1207,6 +1265,9 @@ .setup = zero_setup, .disconnect = zero_disconnect, + + .suspend = zero_suspend, + .resume = zero_resume, .driver = { .name = (char *) shortname, diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c --- a/drivers/usb/host/ehci-dbg.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/host/ehci-dbg.c Wed May 12 20:33:39 2004 @@ -170,6 +170,20 @@ itd->index[6], itd->index[7]); } +static void __attribute__((__unused__)) +dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd) +{ + ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n", + label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb); + ehci_dbg (ehci, + " addr %08x sched %04x result %08x buf %08x %08x\n", + le32_to_cpu(sitd->hw_fullspeed_ep), + le32_to_cpu(sitd->hw_uframe), + le32_to_cpu(sitd->hw_results), + le32_to_cpu(sitd->hw_buf [0]), + le32_to_cpu(sitd->hw_buf [1])); +} + static int __attribute__((__unused__)) dbg_status_buf (char *buf, unsigned len, char *label, u32 status) { @@ -625,11 +639,20 @@ spin_lock_irqsave (&ehci->lock, flags); + if (bus->controller->power.power_state) { + size = scnprintf (next, size, + "bus %s, device %s (driver " DRIVER_VERSION ")\n" + "SUSPENDED (no register access)\n", + hcd->self.controller->bus->name, + hcd->self.controller->bus_id); + goto done; + } + /* Capability Registers */ i = HC_VERSION(readl (&ehci->caps->hc_capbase)); temp = scnprintf (next, size, - "bus %s device %s\n" - "EHCI %x.%02x, hcd state %d (driver " DRIVER_VERSION ")\n", + "bus %s, device %s (driver " DRIVER_VERSION ")\n" + "EHCI %x.%02x, hcd state %d\n", hcd->self.controller->bus->name, hcd->self.controller->bus_id, i >> 8, i & 0x0ff, ehci->hcd.state); @@ -672,7 +695,7 @@ next += temp; for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) { - temp = dbg_port_buf (scratch, sizeof scratch, label, i, + temp = dbg_port_buf (scratch, sizeof scratch, label, i + 1, readl (&ehci->regs->port_status [i])); temp = scnprintf (next, size, fmt, temp, scratch); size -= temp; @@ -701,6 +724,7 @@ next += temp; #endif +done: spin_unlock_irqrestore (&ehci->lock, flags); return PAGE_SIZE - size; diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c --- a/drivers/usb/host/ehci-hcd.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/host/ehci-hcd.c Wed May 12 20:33:39 2004 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2002 by David Brownell + * Copyright (c) 2000-2004 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -69,6 +69,7 @@ * * HISTORY: * + * 2004-05-10 Root hub and PCI suspend/resume support; remote wakeup. (db) * 2004-02-24 Replace pci_* with generic dma_* API calls (dsaxena@plexity.net) * 2003-12-29 Rewritten high speed iso transfer support (by Michal Sojka, * , updates by DB). @@ -96,7 +97,7 @@ * 2001-June Works with usb-storage and NEC EHCI on 2.4 */ -#define DRIVER_VERSION "2003-Dec-29" +#define DRIVER_VERSION "2004-May-10" #define DRIVER_AUTHOR "David Brownell" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" @@ -128,7 +129,7 @@ module_param (log2_irq_thresh, int, S_IRUGO); MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); -#define INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT) +#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) /*-------------------------------------------------------------------------*/ @@ -201,6 +202,7 @@ dbg_cmd (ehci, "reset", command); writel (command, &ehci->regs->command); ehci->hcd.state = USB_STATE_HALT; + ehci->next_statechange = jiffies; return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000); } @@ -241,6 +243,8 @@ /*-------------------------------------------------------------------------*/ +static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs); + #include "ehci-hub.c" #include "ehci-mem.c" #include "ehci-q.c" @@ -248,8 +252,6 @@ /*-------------------------------------------------------------------------*/ -static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs); - static void ehci_watchdog (unsigned long param) { struct ehci_hcd *ehci = (struct ehci_hcd *) param; @@ -428,12 +430,17 @@ #ifdef CONFIG_PCI if (hcd->self.controller->bus == &pci_bus_type) { struct pci_dev *pdev; + u16 port_wake; pdev = to_pci_dev(hcd->self.controller); /* Serial Bus Release Number is at PCI 0x60 offset */ pci_read_config_byte(pdev, 0x60, &sbrn); + /* port wake capability, reported by boot firmware */ + pci_read_config_word(pdev, 0x62, &port_wake); + hcd->can_wakeup = (port_wake & 1) != 0; + /* help hc dma work well with cachelines */ pci_set_mwi (pdev); @@ -615,41 +622,26 @@ /* suspend/resume, section 4.3 */ +/* These routines rely on PCI to handle powerdown and wakeup, and + * transceivers that don't need any software attention to set up + * the right sort of wakeup. + */ + static int ehci_suspend (struct usb_hcd *hcd, u32 state) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - int ports; - int i; - - ehci_dbg (ehci, "suspend to %d\n", state); - ports = HCS_N_PORTS (ehci->hcs_params); + while (time_before (jiffies, ehci->next_statechange)) + msec_delay (100); - // FIXME: This assumes what's probably a D3 level suspend... - - // FIXME: usb wakeup events on this bus should resume the machine. - // pci config register PORTWAKECAP controls which ports can do it; - // bios may have initted the register... - - /* suspend each port, then stop the hc */ - for (i = 0; i < ports; i++) { - int temp = readl (&ehci->regs->port_status [i]); - - if ((temp & PORT_PE) == 0 - || (temp & PORT_OWNER) != 0) - continue; - ehci_dbg (ehci, "suspend port %d", i); - temp |= PORT_SUSPEND; - writel (temp, &ehci->regs->port_status [i]); - } - - if (hcd->state == USB_STATE_RUNNING) - ehci_ready (ehci); - writel (readl (&ehci->regs->command) & ~CMD_RUN, &ehci->regs->command); - -// save pci FLADJ value +#ifdef CONFIG_USB_SUSPEND + (void) usb_suspend_device (hcd->self.root_hub); +#else + /* FIXME lock root hub */ + (void) ehci_hub_suspend (hcd); +#endif - /* who tells PCI to reduce power consumption? */ + // save (PCI) FLADJ in case of Vaux power loss return 0; } @@ -657,40 +649,22 @@ static int ehci_resume (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - int ports; - int i; + int retval; - ehci_dbg (ehci, "resume\n"); + // maybe restore (PCI) FLADJ - ports = HCS_N_PORTS (ehci->hcs_params); + while (time_before (jiffies, ehci->next_statechange)) + msec_delay (100); - // FIXME: if controller didn't retain state, - // return and let generic code clean it up - // test configured_flag ? - - /* resume HC and each port */ -// restore pci FLADJ value - // khubd and drivers will set HC running, if needed; - hcd->state = USB_STATE_RUNNING; - // FIXME Philips/Intel/... etc don't really have a "READY" - // state ... turn on CMD_RUN too - for (i = 0; i < ports; i++) { - int temp = readl (&ehci->regs->port_status [i]); - - if ((temp & PORT_PE) == 0 - || (temp & PORT_SUSPEND) != 0) - continue; - ehci_dbg (ehci, "resume port %d", i); - temp |= PORT_RESUME; - writel (temp, &ehci->regs->port_status [i]); - readl (&ehci->regs->command); /* unblock posted writes */ - - wait_ms (20); - temp &= ~PORT_RESUME; - writel (temp, &ehci->regs->port_status [i]); - } - readl (&ehci->regs->command); /* unblock posted writes */ - return 0; +#ifdef CONFIG_USB_SUSPEND + retval = usb_resume_device (hcd->self.root_hub); +#else + /* FIXME lock root hub */ + retval = ehci_hub_resume (hcd); +#endif + if (retval == 0) + hcd->self.controller->power.power_state = 0; + return retval; } #endif @@ -752,7 +726,7 @@ bh = 0; #ifdef EHCI_VERBOSE_DEBUG - /* unrequested/ignored: Port Change Detect, Frame List Rollover */ + /* unrequested/ignored: Frame List Rollover */ dbg_status (ehci, "irq", status); #endif @@ -774,6 +748,34 @@ bh = 1; } + /* remote wakeup [4.3.1] */ + if ((status & STS_PCD) && ehci->hcd.remote_wakeup) { + unsigned i = HCS_N_PORTS (ehci->hcs_params); + + /* resume root hub? */ + status = readl (&ehci->regs->command); + if (!(status & CMD_RUN)) + writel (status | CMD_RUN, &ehci->regs->command); + + while (i--) { + status = readl (&ehci->regs->port_status [i]); + if (status & PORT_OWNER) + continue; + if (!(status & PORT_RESUME) + || ehci->reset_done [i] != 0) + continue; + + /* start 20 msec resume signaling from this port, + * and make khubd collect PORT_STAT_C_SUSPEND to + * stop that signaling. + */ + ehci->reset_done [i] = jiffies + MSEC_TO_JIFFIES (20); + mod_timer (&ehci->hcd.rh_timer, + ehci->reset_done [i] + 1); + ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); + } + } + /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { ehci_err (ehci, "fatal error\n"); @@ -814,7 +816,6 @@ struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct list_head qtd_list; - urb->transfer_flags &= ~EHCI_STATE_UNLINK; INIT_LIST_HEAD (&qtd_list); switch (usb_pipetype (urb->pipe)) { @@ -914,7 +915,6 @@ // wait till next completion, do it then. // completion irqs can wait up to 1024 msec, - urb->transfer_flags |= EHCI_STATE_UNLINK; break; } spin_unlock_irqrestore (&ehci->lock, flags); @@ -965,7 +965,7 @@ goto rescan; case QH_STATE_IDLE: /* fully unlinked */ if (list_empty (&qh->qtd_list)) { - qh_put (ehci, qh); + qh_put (qh); break; } /* else FALL THROUGH */ diff -Nru a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c --- a/drivers/usb/host/ehci-hub.c Wed May 12 20:33:40 2004 +++ b/drivers/usb/host/ehci-hub.c Wed May 12 20:33:40 2004 @@ -28,6 +28,131 @@ /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_PM + +static int ehci_hub_suspend (struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub; + int port; + int status = 0; + + if (root->dev.power.power_state != 0) + return 0; + if (time_before (jiffies, ehci->next_statechange)) + return -EAGAIN; + + port = HCS_N_PORTS (ehci->hcs_params); + spin_lock_irq (&ehci->lock); + + /* suspend any active/unsuspended ports, maybe allow wakeup */ + while (port--) { + u32 t1 = readl (&ehci->regs->port_status [port]); + u32 t2 = t1; + + if ((t1 & PORT_PE) && !(t1 & PORT_OWNER)) + t2 |= PORT_SUSPEND; + if (ehci->hcd.remote_wakeup) + t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E; + else + t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); + + if (t1 != t2) { + ehci_vdbg (ehci, "port %d, %08x -> %08x\n", + port + 1, t1, t2); + writel (t2, &ehci->regs->port_status [port]); + } + } + + /* stop schedules, then turn off HC and clean any completed work */ + if (hcd->state == USB_STATE_RUNNING) + ehci_ready (ehci); + ehci->command = readl (&ehci->regs->command); + writel (ehci->command & ~CMD_RUN, &ehci->regs->command); + if (ehci->reclaim) + ehci->reclaim_ready = 1; + ehci_work (ehci, 0); + (void) handshake (&ehci->regs->status, STS_HALT, STS_HALT, 2000); + + root->dev.power.power_state = 3; + ehci->next_statechange = jiffies + MSEC_TO_JIFFIES(10); + spin_unlock_irq (&ehci->lock); + return status; +} + + +/* caller owns root->serialize, and should reset/reinit on error */ +static int ehci_hub_resume (struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub; + u32 temp; + int i; + + if (!root->dev.power.power_state) + return 0; + if (time_before (jiffies, ehci->next_statechange)) + return -EAGAIN; + + /* re-init operational registers in case we lost power */ + if (readl (&ehci->regs->intr_enable) == 0) { + writel (INTR_MASK, &ehci->regs->intr_enable); + writel (0, &ehci->regs->segment); + writel (ehci->periodic_dma, &ehci->regs->frame_list); + writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next); + /* FIXME will this work even (pci) vAUX was lost? */ + } + + /* restore CMD_RUN, framelist size, and irq threshold */ + writel (ehci->command, &ehci->regs->command); + + /* take ports out of suspend */ + i = HCS_N_PORTS (ehci->hcs_params); + while (i--) { + temp = readl (&ehci->regs->port_status [i]); + temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); + if (temp & PORT_SUSPEND) { + ehci->reset_done [i] = jiffies + MSEC_TO_JIFFIES (20); + temp |= PORT_RESUME; + } + writel (temp, &ehci->regs->port_status [i]); + } + i = HCS_N_PORTS (ehci->hcs_params); + wait_ms (20); + while (i--) { + temp = readl (&ehci->regs->port_status [i]); + if ((temp & PORT_SUSPEND) == 0) + continue; + temp &= ~PORT_RESUME; + writel (temp, &ehci->regs->port_status [i]); + ehci_vdbg (ehci, "resumed port %d\n", i + 1); + } + (void) readl (&ehci->regs->command); + + /* maybe re-activate the schedule(s) */ + temp = 0; + if (ehci->async->qh_next.qh) + temp |= CMD_ASE; + if (ehci->periodic_sched) + temp |= CMD_PSE; + if (temp) + writel (ehci->command | temp, &ehci->regs->command); + + root->dev.power.power_state = 0; + ehci->next_statechange = jiffies + MSEC_TO_JIFFIES(5); + ehci->hcd.state = USB_STATE_RUNNING; + return 0; +} + +#else + +#define ehci_hub_suspend 0 +#define ehci_hub_resume 0 + +#endif /* CONFIG_PM */ + +/*-------------------------------------------------------------------------*/ + static int check_reset_complete ( struct ehci_hcd *ehci, int index, @@ -99,7 +224,11 @@ } if (!(temp & PORT_CONNECT)) ehci->reset_done [i] = 0; - if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) { + if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0 + // PORT_STAT_C_SUSPEND? + || ((temp & PORT_RESUME) != 0 + && time_after (jiffies, + ehci->reset_done [i]))) { if (i < 7) buf [0] |= 1 << (i + 1); else @@ -143,6 +272,8 @@ /*-------------------------------------------------------------------------*/ +#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) + static int ehci_hub_control ( struct usb_hcd *hcd, u16 typeReq, @@ -194,8 +325,20 @@ &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_SUSPEND: + if (temp & PORT_RESET) + goto error; + if (temp & PORT_SUSPEND) { + if ((temp & PORT_PE) == 0) + goto error; + /* resume signaling for 20 msec */ + writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME, + &ehci->regs->port_status [wIndex]); + ehci->reset_done [wIndex] = jiffies + + MSEC_TO_JIFFIES (20); + } + break; case USB_PORT_FEAT_C_SUSPEND: - /* ? */ + /* we auto-clear this feature */ break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) @@ -239,15 +382,37 @@ status |= 1 << USB_PORT_FEAT_C_CONNECTION; if (temp & PORT_PEC) status |= 1 << USB_PORT_FEAT_C_ENABLE; - // USB_PORT_FEAT_C_SUSPEND if (temp & PORT_OCC) status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; + /* whoever resumes must GetPortStatus to complete it!! */ + if ((temp & PORT_RESUME) + && time_after (jiffies, + ehci->reset_done [wIndex])) { + status |= 1 << USB_PORT_FEAT_C_SUSPEND; + ehci->reset_done [wIndex] = 0; + + /* stop resume signaling */ + temp = readl (&ehci->regs->port_status [wIndex]); + writel (temp & ~PORT_RESUME, + &ehci->regs->port_status [wIndex]); + retval = handshake ( + &ehci->regs->port_status [wIndex], + PORT_RESUME, 0, 2000 /* 2msec */); + if (retval != 0) { + ehci_err (ehci, "port %d resume error %d\n", + wIndex + 1, retval); + goto error; + } + temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); + } + /* whoever resets must GetPortStatus to complete it!! */ if ((temp & PORT_RESET) && time_after (jiffies, ehci->reset_done [wIndex])) { status |= 1 << USB_PORT_FEAT_C_RESET; + ehci->reset_done [wIndex] = 0; /* force reset to complete */ writel (temp & ~PORT_RESET, @@ -275,7 +440,7 @@ } if (temp & PORT_PE) status |= 1 << USB_PORT_FEAT_ENABLE; - if (temp & PORT_SUSPEND) + if (temp & (PORT_SUSPEND|PORT_RESUME)) status |= 1 << USB_PORT_FEAT_SUSPEND; if (temp & PORT_OC) status |= 1 << USB_PORT_FEAT_OVER_CURRENT; @@ -312,6 +477,11 @@ switch (wValue) { case USB_PORT_FEAT_SUSPEND: + if ((temp & PORT_PE) == 0 + || (temp & PORT_RESET) != 0) + goto error; + if (ehci->hcd.remote_wakeup) + temp |= PORT_WAKE_BITS; writel (temp | PORT_SUSPEND, &ehci->regs->port_status [wIndex]); break; @@ -321,6 +491,8 @@ &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_RESET: + if (temp & PORT_RESUME) + goto error; /* line status bits may report this as low speed, * which can be fine if this root hub has a * transaction translator built in. @@ -342,7 +514,7 @@ * usb 2.0 spec says 50 ms resets on root */ ehci->reset_done [wIndex] = jiffies - + ((50 /* msec */ * HZ) / 1000); + + MSEC_TO_JIFFIES (50); } writel (temp, &ehci->regs->port_status [wIndex]); break; diff -Nru a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c --- a/drivers/usb/host/ehci-mem.c Wed May 12 20:33:40 2004 +++ b/drivers/usb/host/ehci-mem.c Wed May 12 20:33:40 2004 @@ -87,6 +87,22 @@ } +static void qh_destroy (struct kref *kref) +{ + struct ehci_qh *qh = container_of(kref, struct ehci_qh, kref); + struct ehci_hcd *ehci = qh->ehci; + + /* clean qtds first, and know this is not linked */ + if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) { + ehci_dbg (ehci, "unused qh not empty!\n"); + BUG (); + } + if (qh->dummy) + ehci_qtd_free (ehci, qh->dummy); + usb_put_dev (qh->dev); + dma_pool_free (ehci->qh_pool, qh, qh->qh_dma); +} + static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags) { struct ehci_qh *qh; @@ -98,7 +114,8 @@ return qh; memset (qh, 0, sizeof *qh); - atomic_set (&qh->refcount, 1); + kref_init(&qh->kref, qh_destroy); + qh->ehci = ehci; qh->qh_dma = dma; // INIT_LIST_HEAD (&qh->qh_list); INIT_LIST_HEAD (&qh->qtd_list); @@ -114,25 +131,15 @@ } /* to share a qh (cpu threads, or hc) */ -static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh) +static inline struct ehci_qh *qh_get (struct ehci_qh *qh) { - atomic_inc (&qh->refcount); + kref_get(&qh->kref); return qh; } -static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh) +static inline void qh_put (struct ehci_qh *qh) { - if (!atomic_dec_and_test (&qh->refcount)) - return; - /* clean qtds first, and know this is not linked */ - if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) { - ehci_dbg (ehci, "unused qh not empty!\n"); - BUG (); - } - if (qh->dummy) - ehci_qtd_free (ehci, qh->dummy); - usb_put_dev (qh->dev); - dma_pool_free (ehci->qh_pool, qh, qh->qh_dma); + kref_put(&qh->kref); } /*-------------------------------------------------------------------------*/ @@ -145,7 +152,7 @@ static void ehci_mem_cleanup (struct ehci_hcd *ehci) { if (ehci->async) - qh_put (ehci, ehci->async); + qh_put (ehci->async); ehci->async = 0; /* DMA consistent memory and pools */ diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c --- a/drivers/usb/host/ehci-q.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/host/ehci-q.c Wed May 12 20:33:39 2004 @@ -193,7 +193,7 @@ /* ... update hc-wide periodic stats (for usbfs) */ hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--; } - qh_put (ehci, qh); + qh_put (qh); } spin_lock (&urb->lock); @@ -708,7 +708,7 @@ default: dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed); done: - qh_put (ehci, qh); + qh_put (qh); return 0; } @@ -951,7 +951,7 @@ // qh->hw_next = cpu_to_le32 (qh->qh_dma); qh->qh_state = QH_STATE_IDLE; qh->qh_next.qh = 0; - qh_put (ehci, qh); // refcount from reclaim + qh_put (qh); // refcount from reclaim /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ next = qh->reclaim; @@ -965,7 +965,7 @@ && HCD_IS_RUNNING (ehci->hcd.state)) qh_link_async (ehci, qh); else { - qh_put (ehci, qh); // refcount from async list + qh_put (qh); // refcount from async list /* it's not free to turn the async schedule on/off; leave it * active but idle for a while once it empties. @@ -1067,7 +1067,7 @@ qh = qh_get (qh); qh->stamp = ehci->stamp; temp = qh_completions (ehci, qh, regs); - qh_put (ehci, qh); + qh_put (qh); if (temp != 0) { goto rescan; } diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c --- a/drivers/usb/host/ehci-sched.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/host/ehci-sched.c Wed May 12 20:33:39 2004 @@ -312,7 +312,7 @@ do { periodic_unlink (ehci, frame, qh); - qh_put (ehci, qh); + qh_put (qh); frame += qh->period; } while (frame < ehci->periodic_size); @@ -355,7 +355,7 @@ dbg ("descheduled qh %p, period = %d frame = %d count = %d, urbs = %d", qh, qh->period, frame, - atomic_read (&qh->refcount), ehci->periodic_sched); + atomic_read (&qh->kref.refcount), ehci->periodic_sched); } static int check_period ( @@ -1846,7 +1846,7 @@ modified = qh_completions (ehci, temp.qh, regs); if (unlikely (list_empty (&temp.qh->qtd_list))) intr_deschedule (ehci, temp.qh, 0); - qh_put (ehci, temp.qh); + qh_put (temp.qh); break; case Q_TYPE_FSTN: /* for "save place" FSTNs, look at QH entries diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h --- a/drivers/usb/host/ehci.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/host/ehci.h Wed May 12 20:33:39 2004 @@ -84,6 +84,8 @@ struct notifier_block reboot_notifier; unsigned long actions; unsigned stamp; + unsigned long next_statechange; + u32 command; unsigned is_arc_rh_tt:1; /* ARC roothub with TT */ @@ -99,8 +101,6 @@ /* unwrap an HCD pointer to get an EHCI_HCD pointer */ #define hcd_to_ehci(hcd_ptr) container_of(hcd_ptr, struct ehci_hcd, hcd) -/* NOTE: urb->transfer_flags expected to not use this bit !!! */ -#define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */ enum ehci_timer_action { TIMER_IO_WATCHDOG, @@ -221,7 +221,7 @@ u32 segment; /* address bits 63:32 if needed */ /* PERIODICLISTBASE: offset 0x14 */ u32 frame_list; /* points to periodic list */ - /* ASYNCICLISTADDR: offset 0x18 */ + /* ASYNCLISTADDR: offset 0x18 */ u32 async_next; /* address of next async queue head */ u32 reserved [9]; @@ -237,7 +237,10 @@ #define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ #define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ /* 19:16 for port testing */ -/* 15:14 for using port indicator leds (if HCS_INDICATOR allows) */ +#define PORT_LED_OFF (0<<14) +#define PORT_LED_AMBER (1<<14) +#define PORT_LED_GREEN (2<<14) +#define PORT_LED_MASK (3<<14) #define PORT_OWNER (1<<13) /* true: companion hc owns this port */ #define PORT_POWER (1<<12) /* true: has power (see PPC) */ #define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */ @@ -366,7 +369,8 @@ struct ehci_qtd *dummy; struct ehci_qh *reclaim; /* next to reclaim */ - atomic_t refcount; + struct ehci_hcd *ehci; + struct kref kref; unsigned stamp; u8 qh_state; @@ -591,6 +595,14 @@ #endif /*-------------------------------------------------------------------------*/ + +#define MSEC_TO_JIFFIES(msec) ((HZ * (msec) + 999) / 1000) + +static inline void msec_delay(int msec) +{ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(MSEC_TO_JIFFIES(msec)); +} #ifndef DEBUG #define STUB_DEBUG_FILES diff -Nru a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c --- a/drivers/usb/host/ohci-dbg.c Wed May 12 20:33:40 2004 +++ b/drivers/usb/host/ohci-dbg.c Wed May 12 20:33:40 2004 @@ -617,7 +617,17 @@ /* dump driver info, then registers in spec order */ ohci_dbg_sw (ohci, &next, &size, - "%s version " DRIVER_VERSION "\n", hcd_name); + "bus %s, device %s\n" + "%s version " DRIVER_VERSION "\n", + hcd->self.controller->bus->name, + hcd->self.controller->bus_id, + hcd_name); + + if (bus->controller->power.power_state) { + size -= scnprintf (next, size, + "SUSPENDED (no register access)\n"); + goto done; + } ohci_dump_status(ohci, &next, &size); @@ -657,8 +667,8 @@ /* roothub */ ohci_dump_roothub (ohci, 1, &next, &size); +done: spin_unlock_irqrestore (&ohci->lock, flags); - return PAGE_SIZE - size; } static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL); diff -Nru a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c --- a/drivers/usb/host/ohci-hcd.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/host/ohci-hcd.c Wed May 12 20:33:39 2004 @@ -117,8 +117,8 @@ /* For initializing controller (mask in an HCFS mode too) */ #define OHCI_CONTROL_INIT OHCI_CTRL_CBSR - -#define OHCI_UNLINK_TIMEOUT (HZ / 10) +#define OHCI_INTR_INIT \ + (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH) /*-------------------------------------------------------------------------*/ @@ -126,11 +126,6 @@ #include "ohci.h" -static inline void disable (struct ohci_hcd *ohci) -{ - ohci->hcd.state = USB_STATE_HALT; -} - #include "ohci-hub.c" #include "ohci-dbg.c" #include "ohci-mem.c" @@ -206,8 +201,7 @@ if (!urb_priv) return -ENOMEM; memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *)); - - /* fill the private part of the URB */ + INIT_LIST_HEAD (&urb_priv->pending); urb_priv->length = size; urb_priv->ed = ed; @@ -397,6 +391,16 @@ { u32 temp; + /* boot firmware should have set this up (5.1.1.3.1) */ + if (!ohci->fminterval) { + temp = readl (&ohci->regs->fminterval); + if (temp & 0x3fff0000) + ohci->fminterval = temp; + else + ohci->fminterval = DEFAULT_FMINTERVAL; + /* also: power/overcurrent flags in roothub.a */ + } + /* SMM owns the HC? not for long! * On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */ @@ -413,7 +417,7 @@ writel (OHCI_INTR_OC, &ohci->regs->intrenable); writel (OHCI_OCR, &ohci->regs->cmdstatus); while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { - wait_ms (10); + msec_delay (10); if (--temp == 0) { ohci_err (ohci, "USB HC TakeOver failed!\n"); return -1; @@ -430,9 +434,12 @@ /* Reset USB (needed by some controllers); RemoteWakeupConnected * saved if boot firmware (BIOS/SMM/...) told us it's connected + * (for OHCI integrated on mainboard, it normally is) */ ohci->hc_control = readl (&ohci->regs->control); ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */ + if (ohci->hc_control) + ohci->hcd.can_wakeup = 1; writel (ohci->hc_control, &ohci->regs->control); if (power_switching) { unsigned ports = roothub_a (ohci) & RH_A_NDP; @@ -444,7 +451,7 @@ } // flush those pci writes (void) readl (&ohci->regs->control); - wait_ms (50); + msec_delay (50); /* HC Reset requires max 10 us delay */ writel (OHCI_HCR, &ohci->regs->cmdstatus); @@ -473,9 +480,6 @@ /*-------------------------------------------------------------------------*/ -#define FI 0x2edf /* 12000 bits per frame (-1) */ -#define LSTHRESH 0x628 /* lowspeed bit threshold */ - /* Start an OHCI controller, set the BUS operational * enable interrupts * connect the virtual root hub @@ -486,7 +490,6 @@ struct usb_device *udev; struct usb_bus *bus; - spin_lock_init (&ohci->lock); disable (ohci); /* Tell the controller where the control and bulk lists are @@ -497,12 +500,7 @@ /* a reset clears this */ writel ((u32) ohci->hcca_dma, &ohci->regs->hcca); - /* force default fmInterval (we won't adjust it); init thresholds - * for last FS and LS packets, reserve 90% for periodic. - */ - writel ((((6 * (FI - 210)) / 7) << 16) | FI, &ohci->regs->fminterval); - writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart); - writel (LSTHRESH, &ohci->regs->lsthresh); + periodic_reinit (ohci); /* some OHCI implementations are finicky about how they init. * bogus values here mean not even enumeration could work. @@ -519,8 +517,11 @@ writel (ohci->hc_control, &ohci->regs->control); ohci->hcd.state = USB_STATE_RUNNING; + /* wake on ConnectStatusChange, matching external hubs */ + writel (RH_HS_DRWE, &ohci->regs->roothub.status); + /* Choose the interrupts we care about now, others later on demand */ - mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH; + mask = OHCI_INTR_INIT; writel (mask, &ohci->regs->intrstatus); writel (mask, &ohci->regs->intrenable); @@ -551,9 +552,14 @@ // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((roothub_a (ohci) >> 23) & 0x1fe); + bus = hcd_to_bus (&ohci->hcd); + + if (bus->root_hub) { + ohci->hcd.state = USB_STATE_RUNNING; + return 0; + } /* connect the virtual root hub */ - bus = hcd_to_bus (&ohci->hcd); bus->root_hub = udev = usb_alloc_dev (NULL, bus, 0); ohci->hcd.state = USB_STATE_RUNNING; if (!udev) { @@ -610,11 +616,18 @@ ohci_dump (ohci, 1); hc_reset (ohci); } - + + if (ints & OHCI_INTR_RD) { + ohci_vdbg (ohci, "resume detect\n"); + schedule_work(&ohci->rh_resume); + } + if (ints & OHCI_INTR_WDH) { if (HCD_IS_RUNNING(hcd->state)) writel (OHCI_INTR_WDH, ®s->intrdisable); - dl_done_list (ohci, dl_reverse_done_list (ohci), ptregs); + spin_lock (&ohci->lock); + dl_done_list (ohci, ptregs); + spin_unlock (&ohci->lock); if (HCD_IS_RUNNING(hcd->state)) writel (OHCI_INTR_WDH, ®s->intrenable); } @@ -654,6 +667,7 @@ ohci->hcd.state); ohci_dump (ohci, 1); + flush_scheduled_work(); if (HCD_IS_RUNNING(ohci->hcd.state)) hc_reset (ohci); @@ -670,22 +684,68 @@ /*-------------------------------------------------------------------------*/ -// FIXME: this restart logic should be generic, -// and handle full hcd state cleanup - -/* controller died; cleanup debris, then restart */ /* must not be called from interrupt context */ #ifdef CONFIG_PM + +static void mark_children_gone (struct usb_device *dev) +{ + unsigned i; + + for (i = 0; i < dev->maxchild; i++) { + if (dev->children [i] == 0) + continue; + dev->children [i]->state = USB_STATE_NOTATTACHED; + mark_children_gone (dev->children [i]); + } +} + static int hc_restart (struct ohci_hcd *ohci) { int temp; int i; + struct urb_priv *priv; + /* mark any devices gone, so they do nothing till khubd disconnects. + * recycle any "live" eds/tds (and urbs) right away. + * later, khubd disconnect processing will recycle the other state, + * (either as disconnect/reconnect, or maybe someday as a reset). + */ + spin_lock_irq(&ohci->lock); disable (ohci); - if (hcd_to_bus (&ohci->hcd)->root_hub) - usb_disconnect (&hcd_to_bus (&ohci->hcd)->root_hub); - + mark_children_gone (ohci->hcd.self.root_hub); + if (!list_empty (&ohci->pending)) + ohci_dbg(ohci, "abort schedule...\n"); + list_for_each_entry (priv, &ohci->pending, pending) { + struct urb *urb = priv->td[0]->urb; + struct ed *ed = priv->ed; + + switch (ed->state) { + case ED_OPER: + ed->state = ED_UNLINK; + ed->hwINFO |= ED_DEQUEUE; + ed_deschedule (ohci, ed); + + ed->ed_next = ohci->ed_rm_list; + ed->ed_prev = 0; + ohci->ed_rm_list = ed; + /* FALLTHROUGH */ + case ED_UNLINK: + break; + default: + ohci_dbg(ohci, "bogus ed %p state %d\n", + ed, ed->state); + } + + spin_lock (&urb->lock); + urb->status = -ESHUTDOWN; + spin_unlock (&urb->lock); + } + finish_unlinks (ohci, 0, 0); + spin_unlock_irq(&ohci->lock); + + /* paranoia, in case that didn't work: */ + /* empty the interrupt branches */ for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0; for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0; @@ -700,8 +760,20 @@ if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { ohci_err (ohci, "can't restart, %d\n", temp); return temp; - } else + } else { + /* here we "know" root ports should always stay powered, + * and that if we try to turn them back on the root hub + * will respond to CSC processing. + */ + i = roothub_a (ohci) & RH_A_NDP; + while (i--) + writel (RH_PS_PSS, + &ohci->regs->roothub.portstatus [temp]); + ohci->hcd.self.root_hub->dev.power.power_state = 0; + ohci->hcd.state = USB_STATE_RUNNING; ohci_dbg (ohci, "restart complete\n"); + ohci_dump (ohci, 1); + } return 0; } #endif diff -Nru a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c --- a/drivers/usb/host/ohci-hub.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/host/ohci-hub.c Wed May 12 20:33:39 2004 @@ -60,6 +60,261 @@ (temp & RH_PS_CCS) ? " CCS" : "" \ ); +/*-------------------------------------------------------------------------*/ + +#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM) + +#define OHCI_SCHED_ENABLES \ + (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) + +static void dl_done_list (struct ohci_hcd *, struct pt_regs *); +static void finish_unlinks (struct ohci_hcd *, u16 , struct pt_regs *); + +static int ohci_hub_suspend (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + struct usb_device *root = hcd_to_bus (&ohci->hcd)->root_hub; + int status = 0; + + if (root->dev.power.power_state != 0) + return 0; + if (time_before (jiffies, ohci->next_statechange)) + return -EAGAIN; + + spin_lock_irq (&ohci->lock); + + ohci->hc_control = readl (&ohci->regs->control); + switch (ohci->hc_control & OHCI_CTRL_HCFS) { + case OHCI_USB_RESUME: + ohci_dbg (ohci, "resume/suspend?\n"); + ohci->hc_control &= ~OHCI_CTRL_HCFS; + ohci->hc_control |= OHCI_USB_RESET; + writel (ohci->hc_control, &ohci->regs->control); + (void) readl (&ohci->regs->control); + /* FALL THROUGH */ + case OHCI_USB_RESET: + status = -EBUSY; + ohci_dbg (ohci, "needs reinit!\n"); + goto done; + case OHCI_USB_SUSPEND: + ohci_dbg (ohci, "already suspended?\n"); + goto succeed; + } + ohci_dbg (ohci, "suspend root hub\n"); + + /* First stop any processing */ + ohci->hcd.state = USB_STATE_QUIESCING; + if (ohci->hc_control & OHCI_SCHED_ENABLES) { + int limit; + + ohci->hc_control &= ~OHCI_SCHED_ENABLES; + writel (ohci->hc_control, &ohci->regs->control); + ohci->hc_control = readl (&ohci->regs->control); + writel (OHCI_INTR_SF, &ohci->regs->intrstatus); + + /* sched disables take effect on the next frame, + * then the last WDH could take 6+ msec + */ + ohci_dbg (ohci, "stopping schedules ...\n"); + limit = 2000; + while (limit > 0) { + udelay (250); + limit =- 250; + if (readl (&ohci->regs->intrstatus) & OHCI_INTR_SF) + break; + } + dl_done_list (ohci, 0); + mdelay (7); + } + dl_done_list (ohci, 0); + finish_unlinks (ohci, OHCI_FRAME_NO(ohci->hcca), 0); + writel (readl (&ohci->regs->intrstatus), &ohci->regs->intrstatus); + + /* maybe resume can wake root hub */ + if (ohci->hcd.remote_wakeup) + ohci->hc_control |= OHCI_CTRL_RWE; + else + ohci->hc_control &= ~OHCI_CTRL_RWE; + + /* Suspend hub */ + ohci->hc_control &= ~OHCI_CTRL_HCFS; + ohci->hc_control |= OHCI_USB_SUSPEND; + writel (ohci->hc_control, &ohci->regs->control); + (void) readl (&ohci->regs->control); + + /* no resumes until devices finish suspending */ + ohci->next_statechange = jiffies + MSEC_TO_JIFFIES (5); + +succeed: + /* it's not USB_STATE_SUSPENDED unless access to this + * hub from the non-usb side (PCI, SOC, etc) stopped + */ + root->dev.power.power_state = 3; +done: + spin_unlock_irq (&ohci->lock); + return status; +} + +static inline struct ed *find_head (struct ed *ed) +{ + /* for bulk and control lists */ + while (ed->ed_prev) + ed = ed->ed_prev; + return ed; +} + +static int hc_restart (struct ohci_hcd *ohci); + +/* caller owns root->serialize */ +static int ohci_hub_resume (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + struct usb_device *root = hcd_to_bus (&ohci->hcd)->root_hub; + u32 temp, enables; + int status = -EINPROGRESS; + + if (!root->dev.power.power_state) + return 0; + if (time_before (jiffies, ohci->next_statechange)) + return -EAGAIN; + + spin_lock_irq (&ohci->lock); + ohci->hc_control = readl (&ohci->regs->control); + switch (ohci->hc_control & OHCI_CTRL_HCFS) { + case OHCI_USB_SUSPEND: + ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES); + ohci->hc_control |= OHCI_USB_RESUME; + writel (ohci->hc_control, &ohci->regs->control); + (void) readl (&ohci->regs->control); + ohci_dbg (ohci, "resume root hub\n"); + break; + case OHCI_USB_RESUME: + /* HCFS changes sometime after INTR_RD */ + ohci_info (ohci, "remote wakeup\n"); + break; + case OHCI_USB_OPER: + ohci_dbg (ohci, "odd resume\n"); + root->dev.power.power_state = 0; + status = 0; + break; + default: /* RESET, we lost power */ + ohci_dbg (ohci, "root hub hardware reset\n"); + status = -EBUSY; + } + spin_unlock_irq (&ohci->lock); + if (status == -EBUSY) + return hc_restart (ohci); + if (status != -EINPROGRESS) + return status; + + temp = roothub_a (ohci) & RH_A_NDP; + enables = 0; + while (temp--) { + u32 stat = readl (&ohci->regs->roothub.portstatus [temp]); + + /* force global, not selective, resume */ + if (!(stat & RH_PS_PSS)) + continue; + writel (RH_PS_POCI, &ohci->regs->roothub.portstatus [temp]); + } + + /* Some controllers (lucent) need extra-long delays */ + ohci->hcd.state = USB_STATE_RESUMING; + mdelay (20 /* usb 11.5.1.10 */ + 15); + + temp = readl (&ohci->regs->control); + temp &= OHCI_CTRL_HCFS; + if (temp != OHCI_USB_RESUME) { + ohci_err (ohci, "controller won't resume\n"); + return -EBUSY; + } + + /* disable old schedule state, reinit from scratch */ + writel (0, &ohci->regs->ed_controlhead); + writel (0, &ohci->regs->ed_controlcurrent); + writel (0, &ohci->regs->ed_bulkhead); + writel (0, &ohci->regs->ed_bulkcurrent); + writel (0, &ohci->regs->ed_periodcurrent); + writel ((u32) ohci->hcca_dma, &ohci->regs->hcca); + + periodic_reinit (ohci); + + /* interrupts might have been disabled */ + writel (OHCI_INTR_INIT, &ohci->regs->intrenable); + if (ohci->ed_rm_list) + writel (OHCI_INTR_SF, &ohci->regs->intrenable); + writel (readl (&ohci->regs->intrstatus), &ohci->regs->intrstatus); + + /* Then re-enable operations */ + writel (OHCI_USB_OPER, &ohci->regs->control); + (void) readl (&ohci->regs->control); + msec_delay (3); + + temp = OHCI_CONTROL_INIT | OHCI_USB_OPER; + if (ohci->hcd.can_wakeup) + temp |= OHCI_CTRL_RWC; + ohci->hc_control = temp; + writel (temp, &ohci->regs->control); + (void) readl (&ohci->regs->control); + + /* TRSMRCY */ + msec_delay (10); + root->dev.power.power_state = 0; + + /* keep it alive for ~5x suspend + resume costs */ + ohci->next_statechange = jiffies + MSEC_TO_JIFFIES (250); + + /* maybe turn schedules back on */ + enables = 0; + temp = 0; + if (!ohci->ed_rm_list) { + if (ohci->ed_controltail) { + writel (find_head (ohci->ed_controltail)->dma, + &ohci->regs->ed_controlhead); + enables |= OHCI_CTRL_CLE; + temp |= OHCI_CLF; + } + if (ohci->ed_bulktail) { + writel (find_head (ohci->ed_bulktail)->dma, + &ohci->regs->ed_bulkhead); + enables |= OHCI_CTRL_BLE; + temp |= OHCI_BLF; + } + } + if (hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs + || hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs) + enables |= OHCI_CTRL_PLE|OHCI_CTRL_IE; + if (enables) { + ohci_dbg (ohci, "restarting schedules ... %08x\n", enables); + ohci->hc_control |= enables; + writel (ohci->hc_control, &ohci->regs->control); + if (temp) + writel (status, &ohci->regs->cmdstatus); + (void) readl (&ohci->regs->control); + } + + ohci->hcd.state = USB_STATE_RUNNING; + return 0; +} + +static void ohci_rh_resume (void *_hcd) +{ + struct usb_hcd *hcd = _hcd; + + down (&hcd->self.root_hub->serialize); + (void) ohci_hub_resume (hcd); + up (&hcd->self.root_hub->serialize); +} + +#else + +static void ohci_rh_resume (void *_hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + ohci_dbg(ohci, "rh_resume ??\n"); +} + +#endif /* CONFIG_USB_SUSPEND || CONFIG_PM */ /*-------------------------------------------------------------------------*/ @@ -70,6 +325,7 @@ { struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ports, i, changed = 0, length = 1; + int can_suspend = 1; ports = roothub_a (ohci) & RH_A_NDP; if (ports > MAX_ROOT_PORTS) { @@ -95,16 +351,44 @@ for (i = 0; i < ports; i++) { u32 status = roothub_portstatus (ohci, i); - status &= RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC - | RH_PS_OCIC | RH_PS_PRSC; - if (status) { + if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC + | RH_PS_OCIC | RH_PS_PRSC)) { changed = 1; if (i < 7) buf [0] |= 1 << (i + 1); else buf [1] |= 1 << (i - 7); + continue; } + + /* can suspend if no ports are enabled; or if all all + * enabled ports are suspended AND remote wakeup is on. + */ + if (!(status & RH_PS_CCS)) + continue; + if ((status & RH_PS_PSS) && ohci->hcd.remote_wakeup) + continue; + can_suspend = 0; } + +#ifdef CONFIG_PM + /* save power by suspending idle root hubs; + * INTR_RD wakes us when there's work + */ + if (can_suspend + && !changed + && !ohci->ed_rm_list + && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES) + & ohci->hc_control) + == OHCI_USB_OPER + && down_trylock (&hcd->self.root_hub->serialize) == 0 + ) { + ohci_vdbg (ohci, "autosuspend\n"); + (void) ohci_hub_suspend (&ohci->hcd); + up (&hcd->self.root_hub->serialize); + } +#endif + return changed ? length : 0; } @@ -188,6 +472,9 @@ break; case USB_PORT_FEAT_SUSPEND: temp = RH_PS_POCI; + if ((ohci->hc_control & OHCI_CTRL_HCFS) + != OHCI_USB_OPER) + schedule_work (&ohci->rh_resume); break; case USB_PORT_FEAT_C_SUSPEND: temp = RH_PS_PSSC; diff -Nru a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c --- a/drivers/usb/host/ohci-mem.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/host/ohci-mem.c Wed May 12 20:33:39 2004 @@ -31,6 +31,9 @@ if (ohci != 0) { memset (ohci, 0, sizeof (struct ohci_hcd)); ohci->hcd.product_desc = "OHCI Host Controller"; + spin_lock_init (&ohci->lock); + INIT_LIST_HEAD (&ohci->pending); + INIT_WORK (&ohci->rh_resume, ohci_rh_resume, &ohci->hcd); return &ohci->hcd; } return 0; diff -Nru a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c --- a/drivers/usb/host/ohci-pci.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/host/ohci-pci.c Wed May 12 20:33:39 2004 @@ -36,6 +36,7 @@ struct ohci_hcd *ohci = hcd_to_ohci (hcd); ohci->regs = hcd->regs; + ohci->next_statechange = jiffies; return hc_reset (ohci); } @@ -118,74 +119,25 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - u16 cmd; - u32 tmp; - if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) { - ohci_dbg (ohci, "can't suspend (state is %s)\n", - hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS)); - return -EIO; - } + /* suspend root hub, hoping it keeps power during suspend */ + while (time_before (jiffies, ohci->next_statechange)) + msec_delay (100); + +#ifdef CONFIG_USB_SUSPEND + (void) usb_suspend_device (hcd->self.root_hub); +#else + /* FIXME lock root hub */ + (void) ohci_hub_suspend (hcd); +#endif - /* act as if usb suspend can always be used */ - ohci_dbg (ohci, "suspend to %d\n", state); + /* let things settle down a bit */ + msec_delay (100); - /* First stop processing */ - spin_lock_irq (&ohci->lock); - ohci->hc_control &= - ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); - writel (ohci->hc_control, &ohci->regs->control); - writel (OHCI_INTR_SF, &ohci->regs->intrstatus); - (void) readl (&ohci->regs->intrstatus); - spin_unlock_irq (&ohci->lock); - - /* Wait a frame or two */ - mdelay (1); - if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF) - mdelay (1); - #ifdef CONFIG_PMAC_PBOOK if (_machine == _MACH_Pmac) disable_irq ((to_pci_dev(hcd->self.controller))->irq); - /* else, 2.4 assumes shared irqs -- don't disable */ -#endif - - /* Enable remote wakeup */ - writel (readl (&ohci->regs->intrenable) | OHCI_INTR_RD, - &ohci->regs->intrenable); - - /* Suspend chip and let things settle down a bit */ - spin_lock_irq (&ohci->lock); - ohci->hc_control = OHCI_USB_SUSPEND; - writel (ohci->hc_control, &ohci->regs->control); - (void) readl (&ohci->regs->control); - spin_unlock_irq (&ohci->lock); - - set_current_state (TASK_UNINTERRUPTIBLE); - schedule_timeout (HZ/2); - - tmp = readl (&ohci->regs->control) | OHCI_CTRL_HCFS; - switch (tmp) { - case OHCI_USB_RESET: - case OHCI_USB_RESUME: - case OHCI_USB_OPER: - ohci_err (ohci, "can't suspend; hcfs %d\n", tmp); - break; - case OHCI_USB_SUSPEND: - ohci_dbg (ohci, "suspended\n"); - break; - } - /* In some rare situations, Apple's OHCI have happily trashed - * memory during sleep. We disable its bus master bit during - * suspend - */ - pci_read_config_word (to_pci_dev(hcd->self.controller), PCI_COMMAND, - &cmd); - cmd &= ~PCI_COMMAND_MASTER; - pci_write_config_word (to_pci_dev(hcd->self.controller), PCI_COMMAND, - cmd); -#ifdef CONFIG_PMAC_PBOOK { struct device_node *of_node; @@ -202,7 +154,6 @@ static int ohci_pci_resume (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int temp; int retval = 0; #ifdef CONFIG_PMAC_PBOOK @@ -215,90 +166,25 @@ pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1); } #endif - /* did we suspend, or were we powered off? */ - ohci->hc_control = readl (&ohci->regs->control); - temp = ohci->hc_control & OHCI_CTRL_HCFS; - -#ifdef DEBUG - /* the registers may look crazy here */ - ohci_dump_status (ohci, 0, 0); -#endif - /* Re-enable bus mastering */ - pci_set_master (to_pci_dev(ohci->hcd.self.controller)); - - switch (temp) { - - case OHCI_USB_RESET: // lost power -restart: - ohci_info (ohci, "USB restart\n"); - retval = hc_restart (ohci); - break; - - case OHCI_USB_SUSPEND: // host wakeup - case OHCI_USB_RESUME: // remote wakeup - ohci_info (ohci, "USB continue from %s wakeup\n", - (temp == OHCI_USB_SUSPEND) - ? "host" : "remote"); - - /* we "should" only need RESUME if we're SUSPENDed ... */ - ohci->hc_control = OHCI_USB_RESUME; - writel (ohci->hc_control, &ohci->regs->control); - (void) readl (&ohci->regs->control); - /* Some controllers (lucent) need extra-long delays */ - mdelay (35); /* no schedule here ! */ - - temp = readl (&ohci->regs->control); - temp = ohci->hc_control & OHCI_CTRL_HCFS; - if (temp != OHCI_USB_RESUME) { - ohci_err (ohci, "controller won't resume\n"); - /* maybe we can reset */ - goto restart; - } - - /* Then re-enable operations */ - writel (OHCI_USB_OPER, &ohci->regs->control); - (void) readl (&ohci->regs->control); - mdelay (3); - - spin_lock_irq (&ohci->lock); - ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; - if (!ohci->ed_rm_list) { - if (ohci->ed_controltail) - ohci->hc_control |= OHCI_CTRL_CLE; - if (ohci->ed_bulktail) - ohci->hc_control |= OHCI_CTRL_BLE; - } - if (hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs - || hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs) - ohci->hc_control |= OHCI_CTRL_PLE|OHCI_CTRL_IE; - hcd->state = USB_STATE_RUNNING; - writel (ohci->hc_control, &ohci->regs->control); - - /* trigger a start-frame interrupt (why?) */ - writel (OHCI_INTR_SF, &ohci->regs->intrstatus); - writel (OHCI_INTR_SF, &ohci->regs->intrenable); - - writel (OHCI_INTR_WDH, &ohci->regs->intrdisable); - (void) readl (&ohci->regs->intrdisable); - spin_unlock_irq (&ohci->lock); + /* resume root hub */ + while (time_before (jiffies, ohci->next_statechange)) + msec_delay (100); +#ifdef CONFIG_USB_SUSPEND + /* get extra cleanup even if remote wakeup isn't in use */ + retval = usb_resume_device (hcd->self.root_hub); +#else + down (&hcd->self.root_hub->serialize); + retval = ohci_hub_resume (hcd); + up (&hcd->self.root_hub->serialize); +#endif + if (retval == 0) { + hcd->self.controller->power.power_state = 0; #ifdef CONFIG_PMAC_PBOOK if (_machine == _MACH_Pmac) enable_irq (to_pci_dev(hcd->self.controller)->irq); #endif - - /* Check for a pending done list */ - if (ohci->hcca->done_head) - dl_done_list (ohci, dl_reverse_done_list (ohci), NULL); - writel (OHCI_INTR_WDH, &ohci->regs->intrenable); - - /* assume there are TDs on the bulk and control lists */ - writel (OHCI_BLF | OHCI_CLF, &ohci->regs->cmdstatus); - break; - - default: - ohci_warn (ohci, "odd PCI resume\n"); } return retval; } diff -Nru a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c --- a/drivers/usb/host/ohci-q.c Wed May 12 20:33:40 2004 +++ b/drivers/usb/host/ohci-q.c Wed May 12 20:33:40 2004 @@ -22,6 +22,7 @@ } } + list_del (&urb_priv->pending); kfree (urb_priv); } @@ -169,6 +170,9 @@ { int branch; + if (ohci->hcd.state == USB_STATE_QUIESCING) + return -EAGAIN; + ed->state = ED_OPER; ed->ed_prev = 0; ed->ed_next = 0; @@ -419,7 +423,7 @@ } /* NOTE: only ep0 currently needs this "re"init logic, during - * enumeration (after set_address, or if ep0 maxpacket >8). + * enumeration (after set_address). */ if (ed->state == ED_IDLE) { u32 info; @@ -593,6 +597,7 @@ } urb_priv->td_cnt = 0; + list_add (&urb_priv->pending, &ohci->pending); if (data_len) data = urb->transfer_dma; @@ -865,9 +870,6 @@ u32 td_dma; struct td *td_rev = NULL; struct td *td = NULL; - unsigned long flags; - - spin_lock_irqsave (&ohci->lock, flags); td_dma = le32_to_cpup (&ohci->hcca->done_head); ohci->hcca->done_head = 0; @@ -899,7 +901,6 @@ td_rev = td; td_dma = le32_to_cpup (&td->hwNextTD); } - spin_unlock_irqrestore (&ohci->lock, flags); return td_rev; } @@ -1013,7 +1014,9 @@ } /* maybe reenable control and bulk lists */ - if (HCD_IS_RUNNING(ohci->hcd.state) && !ohci->ed_rm_list) { + if (HCD_IS_RUNNING(ohci->hcd.state) + && ohci->hcd.state != USB_STATE_QUIESCING + && !ohci->ed_rm_list) { u32 command = 0, control = 0; if (ohci->ed_controltail) { @@ -1053,11 +1056,10 @@ * scanning the (re-reversed) donelist as this does. */ static void -dl_done_list (struct ohci_hcd *ohci, struct td *td, struct pt_regs *regs) +dl_done_list (struct ohci_hcd *ohci, struct pt_regs *regs) { - unsigned long flags; + struct td *td = dl_reverse_done_list (ohci); - spin_lock_irqsave (&ohci->lock, flags); while (td) { struct td *td_next = td->next_dl_td; struct urb *urb = td->urb; @@ -1098,5 +1100,4 @@ td = td_next; } - spin_unlock_irqrestore (&ohci->lock, flags); } diff -Nru a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h --- a/drivers/usb/host/ohci.h Wed May 12 20:33:40 2004 +++ b/drivers/usb/host/ohci.h Wed May 12 20:33:40 2004 @@ -318,8 +318,9 @@ /* hcd-private per-urb state */ typedef struct urb_priv { struct ed *ed; - __u16 length; // # tds in this request - __u16 td_cnt; // tds already serviced + u16 length; // # tds in this request + u16 td_cnt; // tds already serviced + struct list_head pending; struct td *td [0]; // all TDs in this request } urb_priv_t; @@ -364,12 +365,17 @@ struct dma_pool *td_cache; struct dma_pool *ed_cache; struct td *td_hash [TD_HASH_SIZE]; + struct list_head pending; /* * driver state */ int load [NUM_INTS]; u32 hc_control; /* copy of hc control reg */ + unsigned long next_statechange; /* suspend/resume */ + u32 fminterval; /* saved register */ + + struct work_struct rh_resume; unsigned long flags; /* for HC bugs */ #define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ @@ -383,6 +389,32 @@ }; #define hcd_to_ohci(hcd_ptr) container_of(hcd_ptr, struct ohci_hcd, hcd) + +/*-------------------------------------------------------------------------*/ + +static inline void disable (struct ohci_hcd *ohci) +{ + ohci->hcd.state = USB_STATE_HALT; +} + +#define MSEC_TO_JIFFIES(msec) ((HZ * (msec) + 999) / 1000) + +static inline void msec_delay(int msec) +{ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(MSEC_TO_JIFFIES(msec)); +} + +#define FI 0x2edf /* 12000 bits per frame (-1) */ +#define DEFAULT_FMINTERVAL ((((6 * (FI - 210)) / 7) << 16) | FI) +#define LSTHRESH 0x628 /* lowspeed bit threshold */ + +static inline void periodic_reinit (struct ohci_hcd *ohci) +{ + writel (ohci->fminterval, &ohci->regs->fminterval); + writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart); + writel (LSTHRESH, &ohci->regs->lsthresh); +} /*-------------------------------------------------------------------------*/ diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c --- a/drivers/usb/host/uhci-hcd.c Wed May 12 20:33:40 2004 +++ b/drivers/usb/host/uhci-hcd.c Wed May 12 20:33:40 2004 @@ -157,8 +157,8 @@ return td; } -static inline void uhci_fill_td(struct uhci_td *td, __u32 status, - __u32 token, __u32 buffer) +static inline void uhci_fill_td(struct uhci_td *td, u32 status, + u32 token, u32 buffer) { td->status = cpu_to_le32(status); td->token = cpu_to_le32(token); @@ -184,11 +184,11 @@ list_add_tail(&td->fl_list, &ftd->fl_list); td->link = ltd->link; - mb(); + wmb(); ltd->link = cpu_to_le32(td->dma_handle); } else { td->link = uhci->fl->frame[framenum]; - mb(); + wmb(); uhci->fl->frame[framenum] = cpu_to_le32(td->dma_handle); uhci->fl->frame_cpu[framenum] = td; } @@ -218,7 +218,7 @@ ptd->link = td->link; } - mb(); + wmb(); td->link = UHCI_PTR_TERM; list_del_init(&td->fl_list); @@ -332,17 +332,7 @@ /* Grab the last QH */ lqh = list_entry(skelqh->list.prev, struct uhci_qh, list); - /* - * Patch this endpoint's URB's QHs to point to the next skelqh: - * skelqh --> ... lqh --> newqh --> next skelqh - * Do this first, so the HC always sees the right QH after this one. - */ - list_for_each (tmp, &urbp->queue_list) { - struct urb_priv *turbp = - list_entry(tmp, struct urb_priv, queue_list); - - turbp->qh->link = lqh->link; - } + /* Point to the next skelqh */ urbp->qh->link = lqh->link; wmb(); /* Ordering is important */ @@ -362,15 +352,15 @@ * * The HC could see (and use!) any of these as we write them. */ + lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; if (lqh->urbp) { list_for_each (tmp, &lqh->urbp->queue_list) { struct urb_priv *turbp = list_entry(tmp, struct urb_priv, queue_list); - turbp->qh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; + turbp->qh->link = lqh->link; } } - lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; list_add_tail(&urbp->qh->list, &skelqh->list); } @@ -382,7 +372,7 @@ static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) { struct uhci_qh *pqh; - __u32 newlink; + u32 newlink; if (!qh) return; @@ -423,7 +413,7 @@ turbp->qh->link = newlink; } } - mb(); + wmb(); /* Leave qh->link in case the HC is on the QH now, it will */ /* continue the rest of the schedule */ @@ -510,7 +500,7 @@ /* All qh's in the queue need to link to the next queue */ urbp->qh->link = eurbp->qh->link; - mb(); /* Make sure we flush everything */ + wmb(); /* Make sure we flush everything */ lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; @@ -1044,9 +1034,13 @@ usb_pipeout(urb->pipe)); } - /* Set the flag on the last packet */ - if (!(urb->transfer_flags & URB_NO_INTERRUPT)) - td->status |= cpu_to_le32(TD_CTRL_IOC); + /* Set the interrupt-on-completion flag on the last packet. + * A more-or-less typical 4 KB URB (= size of one memory page) + * will require about 3 ms to transfer; that's a little on the + * fast side but not enough to justify delaying an interrupt + * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT + * flag setting. */ + td->status |= cpu_to_le32(TD_CTRL_IOC); qh = uhci_alloc_qh(uhci, urb->dev); if (!qh) @@ -1786,6 +1780,9 @@ spin_unlock(&uhci->schedule_lock); + /* Wake up anyone waiting for an URB to complete */ + wake_up_all(&uhci->waitqh); + return IRQ_HANDLED; } @@ -2086,6 +2083,8 @@ INIT_LIST_HEAD(&uhci->complete_list); + init_waitqueue_head(&uhci->waitqh); + uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl), &dma_handle, 0); if (!uhci->fl) { @@ -2296,6 +2295,9 @@ uhci_free_pending_qhs(uhci); uhci_free_pending_tds(uhci); spin_unlock_irq(&uhci->schedule_lock); + + /* Wake up anyone waiting for an URB to complete */ + wake_up_all(&uhci->waitqh); release_uhci(uhci); } @@ -2361,6 +2363,46 @@ kfree(hcd_to_uhci(hcd)); } +/* Are there any URBs for a particular device/endpoint on a given list? */ +static int urbs_for_ep_list(struct list_head *head, + struct hcd_dev *hdev, int ep) +{ + struct urb_priv *urbp; + + list_for_each_entry(urbp, head, urb_list) { + struct urb *urb = urbp->urb; + + if (hdev == urb->dev->hcpriv && ep == + (usb_pipeendpoint(urb->pipe) | + usb_pipein(urb->pipe))) + return 1; + } + return 0; +} + +/* Are there any URBs for a particular device/endpoint? */ +static int urbs_for_ep(struct uhci_hcd *uhci, struct hcd_dev *hdev, int ep) +{ + int rc; + + spin_lock_irq(&uhci->schedule_lock); + rc = (urbs_for_ep_list(&uhci->urb_list, hdev, ep) || + urbs_for_ep_list(&uhci->complete_list, hdev, ep) || + urbs_for_ep_list(&uhci->urb_remove_list, hdev, ep)); + spin_unlock_irq(&uhci->schedule_lock); + return rc; +} + +/* Wait until all the URBs for a particular device/endpoint are gone */ +static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd, + struct hcd_dev *hdev, int endpoint) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + + wait_event_interruptible(uhci->waitqh, + !urbs_for_ep(uhci, hdev, endpoint)); +} + static int uhci_hcd_get_frame_number(struct usb_hcd *hcd) { return uhci_get_current_frame_number(hcd_to_uhci(hcd)); @@ -2390,6 +2432,7 @@ .urb_enqueue = uhci_urb_enqueue, .urb_dequeue = uhci_urb_dequeue, + .endpoint_disable = uhci_hcd_endpoint_disable, .get_frame_number = uhci_hcd_get_frame_number, .hub_status_data = uhci_hub_status_data, diff -Nru a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h --- a/drivers/usb/host/uhci-hcd.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/host/uhci-hcd.h Wed May 12 20:33:39 2004 @@ -80,7 +80,7 @@ #define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */ struct uhci_frame_list { - __u32 frame[UHCI_NUMFRAMES]; + u32 frame[UHCI_NUMFRAMES]; void *frame_cpu[UHCI_NUMFRAMES]; @@ -105,8 +105,8 @@ */ struct uhci_qh { /* Hardware fields */ - __u32 link; /* Next queue */ - __u32 element; /* Queue element pointer */ + u32 link; /* Next queue */ + u32 element; /* Queue element pointer */ /* Software fields */ dma_addr_t dma_handle; @@ -185,10 +185,10 @@ */ struct uhci_td { /* Hardware fields */ - __u32 link; - __u32 status; - __u32 token; - __u32 buffer; + u32 link; + u32 status; + u32 token; + u32 buffer; /* Software fields */ dma_addr_t dma_handle; @@ -370,6 +370,8 @@ int rh_numports; struct timer_list stall_timer; + + wait_queue_head_t waitqh; /* endpoint_disable waiters */ }; struct urb_priv { diff -Nru a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c --- a/drivers/usb/image/mdc800.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/image/mdc800.c Wed May 12 20:33:39 2004 @@ -319,7 +319,9 @@ set_current_state(TASK_INTERRUPTIBLE); if (!mdc800->irq_woken) { - schedule_timeout (msec*HZ/1000); + long timeout = msec*HZ/1000; + while(timeout) + timeout = schedule_timeout (timeout); } remove_wait_queue(&mdc800->irq_wait, &wait); set_current_state(TASK_RUNNING); @@ -718,7 +720,9 @@ set_current_state(TASK_INTERRUPTIBLE); if (!mdc800->downloaded) { - schedule_timeout (TO_DOWNLOAD_GET_READY*HZ/1000); + long timeout = TO_DOWNLOAD_GET_READY*HZ/1000; + while(timeout) + timeout = schedule_timeout (timeout); } set_current_state(TASK_RUNNING); remove_wait_queue(&mdc800->download_wait, &wait); @@ -842,7 +846,9 @@ set_current_state(TASK_INTERRUPTIBLE); if (!mdc800->written) { - schedule_timeout (TO_WRITE_GET_READY*HZ/1000); + long timeout = TO_WRITE_GET_READY*HZ/1000; + while(timeout) + timeout = schedule_timeout (timeout); } set_current_state(TASK_RUNNING); remove_wait_queue(&mdc800->write_wait, &wait); diff -Nru a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig --- a/drivers/usb/input/Kconfig Wed May 12 20:33:39 2004 +++ b/drivers/usb/input/Kconfig Wed May 12 20:33:39 2004 @@ -191,6 +191,19 @@ To compile this driver as a module, choose M here: the module will be called mtouchusb. +config USB_EGALAX + tristate "eGalax TouchKit USB Touchscreen Driver" + depends on USB && INPUT + ---help--- + Say Y here if you want to use a eGalax TouchKit USB + Touchscreen controller. + + The driver has been tested on a Xenarc 700TSV monitor + with eGalax touchscreen. + + To compile this driver as a module, choose M here: the + module will be called touchkitusb. + config USB_XPAD tristate "X-Box gamepad support" depends on USB && INPUT diff -Nru a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile --- a/drivers/usb/input/Makefile Wed May 12 20:33:39 2004 +++ b/drivers/usb/input/Makefile Wed May 12 20:33:39 2004 @@ -33,6 +33,7 @@ obj-$(CONFIG_USB_KBTAB) += kbtab.o obj-$(CONFIG_USB_MOUSE) += usbmouse.o obj-$(CONFIG_USB_MTOUCH) += mtouchusb.o +obj-$(CONFIG_USB_EGALAX) += touchkitusb.o obj-$(CONFIG_USB_POWERMATE) += powermate.o obj-$(CONFIG_USB_WACOM) += wacom.o obj-$(CONFIG_USB_XPAD) += xpad.o diff -Nru a/drivers/usb/input/aiptek.c b/drivers/usb/input/aiptek.c --- a/drivers/usb/input/aiptek.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/input/aiptek.c Wed May 12 20:33:39 2004 @@ -188,13 +188,13 @@ __FUNCTION__, retval); } -struct aiptek_features aiptek_features[] = { +static struct aiptek_features aiptek_features[] = { {"Aiptek 6000U/8000U", 8, 3000, 2250, 26, 511, aiptek_irq, 0, 0, 0, 0}, {NULL, 0} }; -struct usb_device_id aiptek_ids[] = { +static struct usb_device_id aiptek_ids[] = { {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20), .driver_info = 0}, {} }; diff -Nru a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c --- a/drivers/usb/input/hid-core.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/input/hid-core.c Wed May 12 20:33:39 2004 @@ -958,7 +958,7 @@ * Create a report. */ -void hid_output_report(struct hid_report *report, __u8 *data) +static void hid_output_report(struct hid_report *report, __u8 *data) { unsigned n; @@ -1038,7 +1038,8 @@ return -1; } -int hid_find_field_in_report(struct hid_report *report, __u32 wanted_usage, struct hid_field **field) +#if 0 +static int hid_find_field_in_report(struct hid_report *report, __u32 wanted_usage, struct hid_field **field) { int i, j; @@ -1051,6 +1052,7 @@ return -1; } +#endif static int hid_submit_out(struct hid_device *hid) { @@ -1412,7 +1414,15 @@ #define USB_VENDOR_ID_CHIC 0x05fe #define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014 -struct hid_blacklist { +#define USB_VENDOR_ID_GLAB 0x06c2 +#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 +#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039 + +#define USB_VENDOR_ID_WISEGROUP 0x0925 +#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 +#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 + +static struct hid_blacklist { __u16 idVendor; __u16 idProduct; unsigned quirks; @@ -1459,6 +1469,10 @@ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 7, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_VOLITO, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PTU, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, diff -Nru a/drivers/usb/input/kbtab.c b/drivers/usb/input/kbtab.c --- a/drivers/usb/input/kbtab.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/input/kbtab.c Wed May 12 20:33:39 2004 @@ -95,7 +95,7 @@ __FUNCTION__, retval); } -struct usb_device_id kbtab_ids[] = { +static struct usb_device_id kbtab_ids[] = { { USB_DEVICE(USB_VENDOR_ID_KBGEAR, 0x1001), .driver_info = 0 }, { } }; diff -Nru a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/input/touchkitusb.c Wed May 12 20:33:40 2004 @@ -0,0 +1,310 @@ +/****************************************************************************** + * touchkitusb.c -- Driver for eGalax TouchKit USB Touchscreens + * + * Copyright (C) 2004 by Daniel Ritz + * Copyright (C) by Todd E. Johnson (mtouchusb.c) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Based upon mtouchusb.c + * + *****************************************************************************/ + +//#define DEBUG + +#include +#include +#include +#include +#include +#include + +#if !defined(DEBUG) && defined(CONFIG_USB_DEBUG) +#define DEBUG +#endif +#include + + +#define TOUCHKIT_MIN_XC 0x0 +#define TOUCHKIT_MAX_XC 0x07ff +#define TOUCHKIT_XC_FUZZ 0x0 +#define TOUCHKIT_XC_FLAT 0x0 +#define TOUCHKIT_MIN_YC 0x0 +#define TOUCHKIT_MAX_YC 0x07ff +#define TOUCHKIT_YC_FUZZ 0x0 +#define TOUCHKIT_YC_FLAT 0x0 +#define TOUCHKIT_REPORT_DATA_SIZE 8 + +#define TOUCHKIT_DOWN 0x01 +#define TOUCHKIT_POINT_TOUCH 0x81 +#define TOUCHKIT_POINT_NOTOUCH 0x80 + +#define TOUCHKIT_GET_TOUCHED(dat) ((((dat)[0]) & TOUCHKIT_DOWN) ? 1 : 0) +#define TOUCHKIT_GET_X(dat) (((dat)[3] << 7) | (dat)[4]) +#define TOUCHKIT_GET_Y(dat) (((dat)[1] << 7) | (dat)[2]) + +#define DRIVER_VERSION "v0.1" +#define DRIVER_AUTHOR "Daniel Ritz " +#define DRIVER_DESC "eGalax TouchKit USB HID Touchscreen Driver" + +struct touchkit_usb { + unsigned char *data; + dma_addr_t data_dma; + struct urb *irq; + struct usb_device *udev; + struct input_dev input; + int open; + char name[128]; + char phys[64]; +}; + +static struct usb_device_id touchkit_devices[] = { + {USB_DEVICE(0x3823, 0x0001)}, + {USB_DEVICE(0x0eef, 0x0001)}, + {} +}; + +static void touchkit_irq(struct urb *urb, struct pt_regs *regs) +{ + struct touchkit_usb *touchkit = urb->context; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ETIMEDOUT: + /* this urb is timing out */ + dbg("%s - urb timed out - was the device unplugged?", + __FUNCTION__); + return; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + + input_regs(&touchkit->input, regs); + input_report_key(&touchkit->input, BTN_TOUCH, + TOUCHKIT_GET_TOUCHED(touchkit->data)); + input_report_abs(&touchkit->input, ABS_X, + TOUCHKIT_GET_X(touchkit->data)); + input_report_abs(&touchkit->input, ABS_Y, + TOUCHKIT_GET_Y(touchkit->data)); + input_sync(&touchkit->input); + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err("%s - usb_submit_urb failed with result: %d", + __FUNCTION__, retval); +} + +static int touchkit_open(struct input_dev *input) +{ + struct touchkit_usb *touchkit = input->private; + + if (touchkit->open++) + return 0; + + touchkit->irq->dev = touchkit->udev; + + if (usb_submit_urb(touchkit->irq, GFP_ATOMIC)) { + touchkit->open--; + return -EIO; + } + + return 0; +} + +static void touchkit_close(struct input_dev *input) +{ + struct touchkit_usb *touchkit = input->private; + + if (!--touchkit->open) + usb_unlink_urb(touchkit->irq); +} + +static int touchkit_alloc_buffers(struct usb_device *udev, + struct touchkit_usb *touchkit) +{ + touchkit->data = usb_buffer_alloc(udev, TOUCHKIT_REPORT_DATA_SIZE, + SLAB_ATOMIC, &touchkit->data_dma); + + if (!touchkit->data) + return -1; + + return 0; +} + +static void touchkit_free_buffers(struct usb_device *udev, + struct touchkit_usb *touchkit) +{ + if (touchkit->data) + usb_buffer_free(udev, TOUCHKIT_REPORT_DATA_SIZE, + touchkit->data, touchkit->data_dma); +} + +static int touchkit_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret; + struct touchkit_usb *touchkit; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_device *udev = interface_to_usbdev(intf); + char path[64]; + char *buf; + + interface = intf->cur_altsetting; + endpoint = &interface->endpoint[0].desc; + + touchkit = kmalloc(sizeof(struct touchkit_usb), GFP_KERNEL); + if (!touchkit) + return -ENOMEM; + + memset(touchkit, 0, sizeof(struct touchkit_usb)); + touchkit->udev = udev; + + if (touchkit_alloc_buffers(udev, touchkit)) { + ret = -ENOMEM; + goto out_free; + } + + touchkit->input.private = touchkit; + touchkit->input.open = touchkit_open; + touchkit->input.close = touchkit_close; + + usb_make_path(udev, path, 64); + sprintf(touchkit->phys, "%s/input0", path); + + touchkit->input.name = touchkit->name; + touchkit->input.phys = touchkit->phys; + touchkit->input.id.bustype = BUS_USB; + touchkit->input.id.vendor = udev->descriptor.idVendor; + touchkit->input.id.product = udev->descriptor.idProduct; + touchkit->input.id.version = udev->descriptor.bcdDevice; + touchkit->input.dev = &intf->dev; + + touchkit->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + touchkit->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + touchkit->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + + /* Used to Scale Compensated Data */ + touchkit->input.absmin[ABS_X] = TOUCHKIT_MIN_XC; + touchkit->input.absmax[ABS_X] = TOUCHKIT_MAX_XC; + touchkit->input.absfuzz[ABS_X] = TOUCHKIT_XC_FUZZ; + touchkit->input.absflat[ABS_X] = TOUCHKIT_XC_FLAT; + touchkit->input.absmin[ABS_Y] = TOUCHKIT_MIN_YC; + touchkit->input.absmax[ABS_Y] = TOUCHKIT_MAX_YC; + touchkit->input.absfuzz[ABS_Y] = TOUCHKIT_YC_FUZZ; + touchkit->input.absflat[ABS_Y] = TOUCHKIT_YC_FLAT; + + buf = kmalloc(63, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out_free_buffers; + } + + if (udev->descriptor.iManufacturer && + usb_string(udev, udev->descriptor.iManufacturer, buf, 63) > 0) + strcat(touchkit->name, buf); + if (udev->descriptor.iProduct && + usb_string(udev, udev->descriptor.iProduct, buf, 63) > 0) + sprintf(touchkit->name, "%s %s", touchkit->name, buf); + + if (!strlen(touchkit->name)) + sprintf(touchkit->name, "USB Touchscreen %04x:%04x", + touchkit->input.id.vendor, touchkit->input.id.product); + + kfree(buf); + + touchkit->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!touchkit->irq) { + dbg("%s - usb_alloc_urb failed: touchkit->irq", __FUNCTION__); + ret = -ENOMEM; + goto out_free_buffers; + } + + usb_fill_int_urb(touchkit->irq, touchkit->udev, + usb_rcvintpipe(touchkit->udev, 0x81), + touchkit->data, TOUCHKIT_REPORT_DATA_SIZE, + touchkit_irq, touchkit, endpoint->bInterval); + + input_register_device(&touchkit->input); + + printk(KERN_INFO "input: %s on %s\n", touchkit->name, path); + usb_set_intfdata(intf, touchkit); + + return 0; + +out_free_buffers: + touchkit_free_buffers(udev, touchkit); +out_free: + kfree(touchkit); + return ret; +} + +static void touchkit_disconnect(struct usb_interface *intf) +{ + struct touchkit_usb *touchkit = usb_get_intfdata(intf); + + dbg("%s - called", __FUNCTION__); + + if (!touchkit) + return; + + dbg("%s - touchkit is initialized, cleaning up", __FUNCTION__); + usb_set_intfdata(intf, NULL); + input_unregister_device(&touchkit->input); + usb_unlink_urb(touchkit->irq); + usb_free_urb(touchkit->irq); + touchkit_free_buffers(interface_to_usbdev(intf), touchkit); + kfree(touchkit); +} + +MODULE_DEVICE_TABLE(usb, touchkit_devices); + +static struct usb_driver touchkit_driver = { + .owner = THIS_MODULE, + .name = "touchkitusb", + .probe = touchkit_probe, + .disconnect = touchkit_disconnect, + .id_table = touchkit_devices, +}; + +static int __init touchkit_init(void) +{ + return usb_register(&touchkit_driver); +} + +static void __exit touchkit_cleanup(void) +{ + usb_deregister(&touchkit_driver); +} + +module_init(touchkit_init); +module_exit(touchkit_cleanup); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c --- a/drivers/usb/input/wacom.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/input/wacom.c Wed May 12 20:33:39 2004 @@ -527,7 +527,7 @@ __FUNCTION__, retval); } -struct wacom_features wacom_features[] = { +static struct wacom_features wacom_features[] = { { "Wacom Penpartner", 7, 5040, 3780, 255, 32, 0, wacom_penpartner_irq }, { "Wacom Graphire", 8, 10206, 7422, 511, 32, 1, wacom_graphire_irq }, { "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 32, 1, wacom_graphire_irq }, @@ -556,7 +556,7 @@ { } }; -struct usb_device_id wacom_ids[] = { +static struct usb_device_id wacom_ids[] = { { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x00) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x10) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x11) }, diff -Nru a/drivers/usb/media/dsbr100.c b/drivers/usb/media/dsbr100.c --- a/drivers/usb/media/dsbr100.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/media/dsbr100.c Wed May 12 20:33:39 2004 @@ -33,6 +33,9 @@ History: + Version 0.40: + Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing + Version 0.30: Markus: Updates for 2.5.x kernel and more ISO compliant source @@ -75,13 +78,17 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.30" +#define DRIVER_VERSION "v0.40" #define DRIVER_AUTHOR "Markus Demleitner " #define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver" #define DSB100_VENDOR 0x04b4 #define DSB100_PRODUCT 0x1002 +/* Commands the device appears to understand */ +#define DSB100_TUNE 1 +#define DSB100_ONOFF 2 + #define TB_LEN 16 /* Frequency limits in MHz -- these are European values. For Japanese @@ -102,15 +109,19 @@ static int radio_nr = -1; MODULE_PARM(radio_nr, "i"); -typedef struct -{ - struct usb_device *dev; +/* Data for one (physical) device */ +typedef struct { + struct usb_device *usbdev; + struct video_device *videodev; unsigned char transfer_buffer[TB_LEN]; int curfreq; int stereo; -} usb_dsbr100; + int users; + int removed; +} dsbr100_device; +/* File system interface */ static struct file_operations usb_dsbr100_fops = { .owner = THIS_MODULE, .open = usb_dsbr100_open, @@ -118,65 +129,84 @@ .ioctl = usb_dsbr100_ioctl, .llseek = no_llseek, }; -static struct video_device usb_dsbr100_radio= + +/* V4L interface */ +static struct video_device dsbr100_videodev_template= { .owner = THIS_MODULE, .name = "D-Link DSB-R 100", .type = VID_TYPE_TUNER, .hardware = VID_HARDWARE_AZTECH, .fops = &usb_dsbr100_fops, + .release = video_device_release, }; -static int users = 0; - -static struct usb_device_id usb_dsbr100_table [] = { +static struct usb_device_id usb_dsbr100_device_table [] = { { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, usb_dsbr100_table); +MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); +/* USB subsystem interface */ static struct usb_driver usb_dsbr100_driver = { .owner = THIS_MODULE, .name = "dsbr100", .probe = usb_dsbr100_probe, .disconnect = usb_dsbr100_disconnect, - .id_table = usb_dsbr100_table, + .id_table = usb_dsbr100_device_table, }; +/* Low-level device interface begins here */ -static int dsbr100_start(usb_dsbr100 *radio) +/* switch on radio */ +static int dsbr100_start(dsbr100_device *radio) { - if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 || - usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300)<0) + if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_ONOFF, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x01, 0x00, radio->transfer_buffer, 8, 300)<0) return -1; return (radio->transfer_buffer)[0]; } -static int dsbr100_stop(usb_dsbr100 *radio) +/* switch off radio */ +static int dsbr100_stop(dsbr100_device *radio) { - if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 || - usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300)<0) + if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_ONOFF, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0x00, radio->transfer_buffer, 8, 300)<0) return -1; return (radio->transfer_buffer)[0]; } - -static int dsbr100_setfreq(usb_dsbr100 *radio, int freq) +/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ +static int dsbr100_setfreq(dsbr100_device *radio, int freq) { freq = (freq/16*80)/1000+856; - if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x01, 0xC0, (freq>>8)&0x00ff, freq&0xff, - radio->transfer_buffer, 8, 300)<0 || - usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 || - usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) { + if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_TUNE, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + (freq>>8)&0x00ff, freq&0xff, + radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) { radio->stereo = -1; return -1; } @@ -184,61 +214,91 @@ return (radio->transfer_buffer)[0]; } -static void dsbr100_getstat(usb_dsbr100 *radio) -{ - if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x00, 0xC0, 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0) +/* return the device status. This is, in effect, just whether it +sees a stereo signal or not. Pity. */ +static void dsbr100_getstat(dsbr100_device *radio) +{ + if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0) radio->stereo = -1; else radio->stereo = ! (radio->transfer_buffer[0]&0x01); } +/* USB subsystem interface begins here */ + +/* check if the device is present and register with v4l and +usb if it is */ static int usb_dsbr100_probe(struct usb_interface *intf, const struct usb_device_id *id) { - usb_dsbr100 *radio; + dsbr100_device *radio; - if (!(radio = kmalloc(sizeof(usb_dsbr100),GFP_KERNEL))) + if (!(radio = kmalloc(sizeof(dsbr100_device), GFP_KERNEL))) return -ENOMEM; - usb_dsbr100_radio.priv = radio; - radio->dev = interface_to_usbdev (intf); + if (!(radio->videodev = video_device_alloc())) { + kfree(radio); + return -ENOMEM; + } + memcpy(radio->videodev, &dsbr100_videodev_template, + sizeof(dsbr100_videodev_template)); + radio->removed = 0; + radio->users = 0; + radio->usbdev = interface_to_usbdev(intf); radio->curfreq = FREQ_MIN*FREQ_MUL; - usb_set_intfdata (intf, radio); + video_set_drvdata(radio->videodev, radio); + if (video_register_device(radio->videodev, VFL_TYPE_RADIO, + radio_nr)) { + warn("Could not register video device"); + video_device_release(radio->videodev); + kfree(radio); + return -EIO; + } + usb_set_intfdata(intf, radio); return 0; } +/* handle unplugging of the device, release data structures +if nothing keeps us from doing it. If something is still +keeping us busy, the release callback of v4l will take care +of releasing it. stv680.c does not relase its private +data, so I don't do this here either. Checking out the +code I'd expect I better did that, but if there's a memory +leak here it's tiny (~50 bytes per disconnect) */ static void usb_dsbr100_disconnect(struct usb_interface *intf) { - usb_dsbr100 *radio = usb_get_intfdata (intf); + dsbr100_device *radio = usb_get_intfdata(intf); usb_set_intfdata (intf, NULL); - if (radio) { - lock_kernel(); - if (users) { - unlock_kernel(); - return; + video_unregister_device(radio->videodev); + radio->videodev = NULL; + if (radio->users) { + kfree(radio); + } else { + radio->removed = 1; } - kfree(radio); - usb_dsbr100_radio.priv = NULL; - unlock_kernel(); } } + +/* Video for Linux interface */ + static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { - struct video_device *dev = video_devdata(file); - usb_dsbr100 *radio=dev->priv; + dsbr100_device *radio=video_get_drvdata(video_devdata(file)); if (!radio) - return -EINVAL; + return -EIO; - switch(cmd) - { + switch(cmd) { case VIDIOCGCAP: { struct video_capability *v = arg; + memset(v, 0, sizeof(*v)); v->type = VID_TYPE_TUNER; v->channels = 1; @@ -248,6 +308,7 @@ } case VIDIOCGTUNER: { struct video_tuner *v = arg; + dsbr100_getstat(radio); if(v->tuner) /* Only 1 tuner */ return -EINVAL; @@ -263,21 +324,21 @@ } case VIDIOCSTUNER: { struct video_tuner *v = arg; + if(v->tuner!=0) return -EINVAL; /* Only 1 tuner so no setting needed ! */ return 0; } - case VIDIOCGFREQ: - { + case VIDIOCGFREQ: { int *freq = arg; + if (radio->curfreq==-1) return -EINVAL; *freq = radio->curfreq; return 0; } - case VIDIOCSFREQ: - { + case VIDIOCSFREQ: { int *freq = arg; radio->curfreq = *freq; @@ -287,6 +348,7 @@ } case VIDIOCGAUDIO: { struct video_audio *v = arg; + memset(v, 0, sizeof(*v)); v->flags |= VIDEO_AUDIO_MUTABLE; v->mode = VIDEO_SOUND_STEREO; @@ -297,9 +359,9 @@ } case VIDIOCSAUDIO: { struct video_audio *v = arg; + if (v->audio) return -EINVAL; - if (v->flags&VIDEO_AUDIO_MUTE) { if (dsbr100_stop(radio)==-1) warn("Radio did not respond properly"); @@ -322,64 +384,40 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - usb_dsbr100 *radio=dev->priv; + dsbr100_device *radio=video_get_drvdata(video_devdata(file)); - if (! radio) { - warn("Radio not initialised"); - return -EAGAIN; - } - if(users) - { - warn("Radio in use"); - return -EBUSY; - } - users++; - if (dsbr100_start(radio)<0) + radio->users = 1; + if (dsbr100_start(radio)<0) { warn("Radio did not start up properly"); + radio->users = 0; + return -EIO; + } dsbr100_setfreq(radio, radio->curfreq); return 0; } static int usb_dsbr100_close(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - usb_dsbr100 *radio=dev->priv; + dsbr100_device *radio=video_get_drvdata(video_devdata(file)); if (!radio) return -ENODEV; - users--; + radio->users = 0; + if (radio->removed) { + kfree(radio); + } return 0; } static int __init dsbr100_init(void) { - int retval; - usb_dsbr100_radio.priv = NULL; - retval = usb_register(&usb_dsbr100_driver); - if (retval) - goto failed_usb_register; - retval = video_register_device(&usb_dsbr100_radio, VFL_TYPE_RADIO, - radio_nr); - if (retval) { - warn("Couldn't register video device"); - goto failed_video_register; - } + int retval = usb_register(&usb_dsbr100_driver); info(DRIVER_VERSION ":" DRIVER_DESC); - return 0; -failed_video_register: - usb_deregister(&usb_dsbr100_driver); -failed_usb_register: return retval; } static void __exit dsbr100_exit(void) { - usb_dsbr100 *radio=usb_dsbr100_radio.priv; - - if (radio) - dsbr100_stop(radio); - video_unregister_device(&usb_dsbr100_radio); usb_deregister(&usb_dsbr100_driver); } diff -Nru a/drivers/usb/media/ibmcam.c b/drivers/usb/media/ibmcam.c --- a/drivers/usb/media/ibmcam.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/media/ibmcam.c Wed May 12 20:33:39 2004 @@ -3647,7 +3647,7 @@ { struct usb_device *dev = interface_to_usbdev(intf); struct uvd *uvd = NULL; - int i, nas, model=0, canvasX=0, canvasY=0; + int ix, i, nas, model=0, canvasX=0, canvasY=0; int actInterface=-1, inactInterface=-1, maxPS=0; __u8 ifnum = intf->altsetting->desc.bInterfaceNumber; unsigned char video_ep = 0; @@ -3718,7 +3718,7 @@ } while (0); /* Validate found interface: must have one ISO endpoint */ - nas = dev->actconfig->interface[ifnum]->num_altsetting; + nas = intf->num_altsetting; if (debug > 0) info("Number of alternate settings=%d.", nas); if (nas < 2) { @@ -3726,11 +3726,12 @@ return -ENODEV; } /* Validate all alternate settings */ - for (i=0; i < nas; i++) { + for (ix=0; ix < nas; ix++) { const struct usb_host_interface *interface; const struct usb_endpoint_descriptor *endpoint; - interface = &dev->actconfig->interface[ifnum]->altsetting[i]; + interface = &intf->altsetting[ix]; + i = interface->desc.bAlternateSetting; if (interface->desc.bNumEndpoints != 1) { err("Interface %d. has %u. endpoints!", ifnum, (unsigned)(interface->desc.bNumEndpoints)); diff -Nru a/drivers/usb/media/konicawc.c b/drivers/usb/media/konicawc.c --- a/drivers/usb/media/konicawc.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/media/konicawc.c Wed May 12 20:33:39 2004 @@ -381,9 +381,15 @@ int i, errFlag; struct konicawc *cam = (struct konicawc *)uvd->user_data; int pktsz; - struct usb_host_interface *interface; + struct usb_interface *intf; + struct usb_host_interface *interface = NULL; - interface = &dev->actconfig->interface[uvd->iface]->altsetting[spd_to_iface[cam->speed]]; + intf = usb_ifnum_to_if(dev, uvd->iface); + if (intf) + interface = usb_altnum_to_altsetting(intf, + spd_to_iface[cam->speed]); + if (!interface) + return -ENXIO; pktsz = interface->endpoint[1].desc.wMaxPacketSize; DEBUG(1, "pktsz = %d", pktsz); if (!CAMERA_IS_OPERATIONAL(uvd)) { @@ -721,7 +727,7 @@ { struct usb_device *dev = interface_to_usbdev(intf); struct uvd *uvd = NULL; - int i, nas; + int ix, i, nas; int actInterface=-1, inactInterface=-1, maxPS=0; unsigned char video_ep = 0; @@ -741,11 +747,12 @@ return -ENODEV; } /* Validate all alternate settings */ - for (i=0; i < nas; i++) { + for (ix=0; ix < nas; ix++) { const struct usb_host_interface *interface; const struct usb_endpoint_descriptor *endpoint; - interface = &intf->altsetting[i]; + interface = &intf->altsetting[ix]; + i = interface->desc.bAlternateSetting; if (interface->desc.bNumEndpoints != 2) { err("Interface %d. has %u. endpoints!", interface->desc.bInterfaceNumber, diff -Nru a/drivers/usb/media/ov511.c b/drivers/usb/media/ov511.c --- a/drivers/usb/media/ov511.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/media/ov511.c Wed May 12 20:33:39 2004 @@ -5603,8 +5603,16 @@ if (ov->bridge == BRG_OV518) { - struct usb_interface *ifp = ov->dev->config[0].interface[0]; - __u16 mxps = ifp->altsetting[7].endpoint[0].desc.wMaxPacketSize; + struct usb_interface *ifp; + struct usb_host_interface *alt; + __u16 mxps = 0; + + ifp = usb_ifnum_to_if(ov->dev, 0); + if (ifp) { + alt = usb_altnum_to_altsetting(ifp, 7); + if (alt) + mxps = alt->endpoint[0].desc.wMaxPacketSize; + } /* Some OV518s have packet numbering by default, some don't */ if (mxps == 897) @@ -5805,7 +5813,7 @@ if (dev->descriptor.bNumConfigurations != 1) return -ENODEV; - idesc = &intf->altsetting[0].desc; + idesc = &intf->cur_altsetting->desc; if (idesc->bInterfaceClass != 0xFF) return -ENODEV; diff -Nru a/drivers/usb/media/pwc-if.c b/drivers/usb/media/pwc-if.c --- a/drivers/usb/media/pwc-if.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/media/pwc-if.c Wed May 12 20:33:39 2004 @@ -789,7 +789,8 @@ struct urb *urb; int i, j, ret; - struct usb_host_interface *idesc; + struct usb_interface *intf; + struct usb_host_interface *idesc = NULL; if (pdev == NULL) return -EFAULT; @@ -801,7 +802,9 @@ /* Get the current alternate interface, adjust packet size */ if (!udev->actconfig) return -EFAULT; - idesc = &udev->actconfig->interface[0]->altsetting[pdev->valternate]; + intf = usb_ifnum_to_if(udev, 0); + if (intf) + idesc = usb_altnum_to_altsetting(intf, pdev->valternate); if (!idesc) return -EFAULT; diff -Nru a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c --- a/drivers/usb/media/se401.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/media/se401.c Wed May 12 20:33:39 2004 @@ -1326,7 +1326,7 @@ if (dev->descriptor.bNumConfigurations != 1) return -ENODEV; - interface = &intf->altsetting[0].desc; + interface = &intf->cur_altsetting->desc; /* Is it an se401? */ if (dev->descriptor.idVendor == 0x03e8 && diff -Nru a/drivers/usb/media/ultracam.c b/drivers/usb/media/ultracam.c --- a/drivers/usb/media/ultracam.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/media/ultracam.c Wed May 12 20:33:39 2004 @@ -95,7 +95,7 @@ * 02-Nov-2000 First (mostly dummy) version. * 06-Nov-2000 Rewrote to dump all data into frame. */ -void ultracam_ProcessIsocData(struct uvd *uvd, struct usbvideo_frame *frame) +static void ultracam_ProcessIsocData(struct uvd *uvd, struct usbvideo_frame *frame) { int n; @@ -513,7 +513,7 @@ { struct usb_device *dev = interface_to_usbdev(intf); struct uvd *uvd = NULL; - int i, nas; + int ix, i, nas; int actInterface=-1, inactInterface=-1, maxPS=0; unsigned char video_ep = 0; @@ -540,11 +540,12 @@ return -ENODEV; } /* Validate all alternate settings */ - for (i=0; i < nas; i++) { + for (ix=0; ix < nas; ix++) { const struct usb_host_interface *interface; const struct usb_endpoint_descriptor *endpoint; - interface = &intf->altsetting[i]; + interface = &intf->altsetting[ix]; + i = interface->desc.bAlternateSetting; if (interface->desc.bNumEndpoints != 1) { err("Interface %d. has %u. endpoints!", interface->desc.bInterfaceNumber, diff -Nru a/drivers/usb/media/vicam.c b/drivers/usb/media/vicam.c --- a/drivers/usb/media/vicam.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/media/vicam.c Wed May 12 20:33:39 2004 @@ -1303,7 +1303,7 @@ printk(KERN_INFO "ViCam based webcam connected\n"); - interface = &intf->altsetting[0]; + interface = intf->cur_altsetting; DBG(KERN_DEBUG "Interface %d. has %u. endpoints!\n", interface->desc.bInterfaceNumber, (unsigned) (interface->desc.bNumEndpoints)); diff -Nru a/drivers/usb/media/w9968cf.h b/drivers/usb/media/w9968cf.h --- a/drivers/usb/media/w9968cf.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/media/w9968cf.h Wed May 12 20:33:39 2004 @@ -189,8 +189,8 @@ VPP_UYVY_TO_RGBX = 0x08, }; -struct list_head w9968cf_dev_list; /* head of V4L registered cameras list */ -LIST_HEAD(w9968cf_dev_list); +static struct list_head w9968cf_dev_list; /* head of V4L registered cameras list */ +static LIST_HEAD(w9968cf_dev_list); struct semaphore w9968cf_devlist_sem; /* semaphore for list traversal */ /* Main device driver structure */ diff -Nru a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig --- a/drivers/usb/misc/Kconfig Wed May 12 20:33:40 2004 +++ b/drivers/usb/misc/Kconfig Wed May 12 20:33:40 2004 @@ -133,6 +133,18 @@ To compile this driver as a module, choose M here: the module will be called speedtch. +config USB_PHIDGETSERVO + tristate "USB PhidgetServo support" + depends on USB + help + Say Y here if you want to connect an 1 or 4 Motor PhidgetServo + servo controller version 2.0 or 3.0. + + Phidgets Inc. has a web page at . + + To compile this driver as a module, choose M here: the + module will be called phidgetservo. + config USB_TEST tristate "USB testing driver (DEVELOPMENT)" depends on USB && USB_DEVICEFS && EXPERIMENTAL diff -Nru a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile --- a/drivers/usb/misc/Makefile Wed May 12 20:33:39 2004 +++ b/drivers/usb/misc/Makefile Wed May 12 20:33:39 2004 @@ -15,3 +15,4 @@ obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_TIGL) += tiglusb.o obj-$(CONFIG_USB_USS720) += uss720.o +obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o diff -Nru a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c --- a/drivers/usb/misc/cytherm.c Wed May 12 20:33:40 2004 +++ b/drivers/usb/misc/cytherm.c Wed May 12 20:33:40 2004 @@ -46,9 +46,6 @@ static int cytherm_probe(struct usb_interface *interface, const struct usb_device_id *id); static void cytherm_disconnect(struct usb_interface *interface); -int vendor_command(struct usb_device *dev, unsigned char request, - unsigned char value, unsigned char index, - void *buf, int size); /* usb specific object needed to register this driver with the usb subsystem */ @@ -71,9 +68,9 @@ /* Send a vendor command to device */ -int vendor_command(struct usb_device *dev, unsigned char request, - unsigned char value, unsigned char index, - void *buf, int size) +static int vendor_command(struct usb_device *dev, unsigned char request, + unsigned char value, unsigned char index, + void *buf, int size) { return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request, diff -Nru a/drivers/usb/misc/emi26.c b/drivers/usb/misc/emi26.c --- a/drivers/usb/misc/emi26.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/misc/emi26.c Wed May 12 20:33:39 2004 @@ -226,7 +226,7 @@ { } -struct usb_driver emi26_driver = { +static struct usb_driver emi26_driver = { .owner = THIS_MODULE, .name = "emi26 - firmware loader", .probe = emi26_probe, diff -Nru a/drivers/usb/misc/emi26_fw.h b/drivers/usb/misc/emi26_fw.h --- a/drivers/usb/misc/emi26_fw.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/misc/emi26_fw.h Wed May 12 20:33:40 2004 @@ -17,7 +17,7 @@ * any driver which includes this firmware, in whole or in part, * requires the inclusion of this statement. */ -INTEL_HEX_RECORD g_bitstream[]={ +static INTEL_HEX_RECORD g_bitstream[]={ { 16, 0x8010, 0, {0xff,0xff,0xff,0xff,0xaa,0x99,0x55,0x66,0x30,0x00,0x80,0x01,0x00,0x00,0x00,0x07 } }, { 16, 0x8020, 0, {0x30,0x01,0x60,0x01,0x00,0x00,0x00,0x0b,0x30,0x01,0x20,0x01,0x00,0x80,0x3f,0x2d } }, { 16, 0x8030, 0, {0x30,0x00,0xc0,0x01,0x00,0x00,0x00,0x00,0x30,0x00,0x80,0x01,0x00,0x00,0x00,0x09 } }, @@ -4406,7 +4406,7 @@ * any driver which includes this firmware, in whole or in part, * requires the inclusion of this statement. */ -INTEL_HEX_RECORD g_Firmware[] = { +static INTEL_HEX_RECORD g_Firmware[] = { { 3,0x0000,0,{0x02,0x43,0x56} }, { 3,0x0003,0,{0x02,0x4b,0xcd} }, { 3,0x000b,0,{0x02,0x4b,0xd2} }, @@ -5669,7 +5669,7 @@ * any driver which includes this firmware, in whole or in part, * requires the inclusion of this statement. */ -INTEL_HEX_RECORD g_Loader[] = { +static INTEL_HEX_RECORD g_Loader[] = { { 3,0x0000,0,{0x02,0x03,0x1c} }, { 3,0x0043,0,{0x02,0x04,0x00} }, { 16,0x0100,0,{0x90,0x7f,0xe9,0xe0,0x24,0x5b,0x60,0x60,0x24,0x02,0x60,0x03,0x02,0x01,0xbe,0x90} }, diff -Nru a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c --- a/drivers/usb/misc/emi62.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/misc/emi62.c Wed May 12 20:33:39 2004 @@ -265,7 +265,7 @@ { } -struct usb_driver emi62_driver = { +static struct usb_driver emi62_driver = { .owner = THIS_MODULE, .name = "emi62 - firmware loader", .probe = emi62_probe, diff -Nru a/drivers/usb/misc/emi62_fw_m.h b/drivers/usb/misc/emi62_fw_m.h --- a/drivers/usb/misc/emi62_fw_m.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/misc/emi62_fw_m.h Wed May 12 20:33:39 2004 @@ -3,7 +3,7 @@ */ /* generated Tue Jun 3 21:36:11 EEST 2003 */ -INTEL_HEX_RECORD g_emi62bs[]={ +static INTEL_HEX_RECORD g_emi62bs[]={ 16, 0x8010, 0, {0xff,0xff,0xff,0xff,0xaa,0x99,0x55,0x66,0x30,0x00,0x80,0x01,0x00,0x00,0x00,0x07 }, 16, 0x8020, 0, {0x30,0x01,0x60,0x01,0x00,0x00,0x00,0x0d,0x30,0x01,0x20,0x01,0x00,0x80,0x3f,0x2d }, 16, 0x8030, 0, {0x30,0x00,0xc0,0x01,0x00,0x00,0x00,0x00,0x30,0x00,0x80,0x01,0x00,0x00,0x00,0x09 }, @@ -6112,7 +6112,7 @@ }; // VERSION= 1.0.0.191 // DATE= 2002oct28 -INTEL_HEX_RECORD g_HexMidiFw62[] = { +static INTEL_HEX_RECORD g_HexMidiFw62[] = { 3,0x0000,0,{0x02,0x46,0xb9}, 3,0x0003,0,{0x02,0x0f,0xfd}, 3,0x000b,0,{0x02,0x4e,0x0f}, @@ -8640,7 +8640,8 @@ /* VERSION=1.04.062 DATE=16.10.2002 -*/INTEL_HEX_RECORD g_emi62_loader[] = { +*/ +static INTEL_HEX_RECORD g_emi62_loader[] = { 3,0x0000,0,{0x02,0x02,0x87}, 3,0x0043,0,{0x02,0x04,0x00}, 16,0x0100,0,{0xe4,0xff,0xfe,0xc2,0x20,0xd2,0xe8,0x43,0xd8,0x20,0x90,0x7f,0xab,0x74,0xff,0xf0}, diff -Nru a/drivers/usb/misc/emi62_fw_s.h b/drivers/usb/misc/emi62_fw_s.h --- a/drivers/usb/misc/emi62_fw_s.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/misc/emi62_fw_s.h Wed May 12 20:33:39 2004 @@ -3,7 +3,7 @@ */ /* generated Tue Apr 15 21:59:42 EEST 2003 */ -INTEL_HEX_RECORD g_emi62bs[]={ +static INTEL_HEX_RECORD g_emi62bs[]={ {16, 0x8010, 0, {0xff,0xff,0xff,0xff,0xaa,0x99,0x55,0x66,0x30,0x00,0x80,0x01,0x00,0x00,0x00,0x07 }}, {16, 0x8020, 0, {0x30,0x01,0x60,0x01,0x00,0x00,0x00,0x0d,0x30,0x01,0x20,0x01,0x00,0x80,0x3f,0x2d }}, {16, 0x8030, 0, {0x30,0x00,0xc0,0x01,0x00,0x00,0x00,0x00,0x30,0x00,0x80,0x01,0x00,0x00,0x00,0x09 }}, @@ -6112,7 +6112,7 @@ }; // VERSION= 1.0.0.191 // DATE= 2002oct28 -INTEL_HEX_RECORD g_HexSpdifFw62[] = { +static INTEL_HEX_RECORD g_HexSpdifFw62[] = { {3,0x0000,0,{0x02,0x45,0xf9}}, { 3,0x0003,0,{0x02,0x0f,0xfd}}, { 3,0x000b,0,{0x02,0x4d,0x9b}}, @@ -8623,7 +8623,8 @@ /* VERSION=1.04.062 DATE=16.10.2002 -*/INTEL_HEX_RECORD g_emi62_loader[] = { +*/ +static INTEL_HEX_RECORD g_emi62_loader[] = { { 3,0x0000,0,{0x02,0x02,0x87}}, { 3,0x0043,0,{0x02,0x04,0x00}}, {16,0x0100,0,{0xe4,0xff,0xfe,0xc2,0x20,0xd2,0xe8,0x43,0xd8,0x20,0x90,0x7f,0xab,0x74,0xff,0xf0}}, diff -Nru a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c --- a/drivers/usb/misc/legousbtower.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/misc/legousbtower.c Wed May 12 20:33:39 2004 @@ -1,8 +1,8 @@ /* * LEGO USB Tower driver * - * Copyright (C) 2003 David Glance - * 2001 Juergen Stuber + * Copyright (C) 2003 David Glance + * 2001-2004 Juergen Stuber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -33,14 +33,44 @@ * - changed to use lego0 rather than tower0 * - changed dbg() to use __func__ rather than deprecated __FUNCTION__ * 2003-01-12 - 0.53 david (david@csse.uwa.edu.au) - * - changed read and write to write everything or timeout (from a patch by Chris Riesen and - * Brett Thaeler driver) + * - changed read and write to write everything or + * timeout (from a patch by Chris Riesen and Brett Thaeler driver) * - added ioctl functionality to set timeouts - * 2003-07-18 - 0.54 davidgsf (david@csse.uwa.edu.au) + * 2003-07-18 - 0.54 davidgsf (david@csse.uwa.edu.au) * - initial import into LegoUSB project * - merge of existing LegoUSB.c driver - * 2003-07-18 - 0.56 davidgsf (david@csse.uwa.edu.au) + * 2003-07-18 - 0.56 davidgsf (david@csse.uwa.edu.au) * - port to 2.6 style driver + * 2004-02-29 - 0.6 Juergen Stuber + * - fix locking + * - unlink read URBs which are no longer needed + * - allow increased buffer size, eliminates need for timeout on write + * - have read URB running continuously + * - added poll + * - forbid seeking + * - added nonblocking I/O + * - changed back __func__ to __FUNCTION__ + * - read and log tower firmware version + * - reset tower on probe, avoids failure of first write + * 2004-03-09 - 0.7 Juergen Stuber + * - timeout read now only after inactivity, shorten default accordingly + * 2004-03-11 - 0.8 Juergen Stuber + * - log major, minor instead of possibly confusing device filename + * - whitespace cleanup + * 2004-03-12 - 0.9 Juergen Stuber + * - normalize whitespace in debug messages + * - take care about endianness in control message responses + * 2004-03-13 - 0.91 Juergen Stuber + * - make default intervals longer to accommodate current EHCI driver + * 2004-03-19 - 0.92 Juergen Stuber + * - replaced atomic_t by memory barriers + * 2004-04-21 - 0.93 Juergen Stuber + * - wait for completion of write urb in release (needed for remotecontrol) + * - corrected poll for write direction (missing negation) + * 2004-04-22 - 0.94 Juergen Stuber + * - make device locking interruptible + * 2004-04-30 - 0.95 Juergen Stuber + * - check for valid udev on resubmitting and unlinking urbs */ #include @@ -53,33 +83,113 @@ #include #include #include +#include #ifdef CONFIG_USB_DEBUG static int debug = 4; #else - static int debug = 1; + static int debug = 0; #endif /* Use our own dbg macro */ #undef dbg -#define dbg(lvl, format, arg...) do { if (debug >= lvl) printk(KERN_DEBUG __FILE__ " : " format " \n", ## arg); } while (0) +#define dbg(lvl, format, arg...) do { if (debug >= lvl) printk(KERN_DEBUG __FILE__ ": " format "\n", ## arg); } while (0) /* Version Information */ -#define DRIVER_VERSION "v0.56" -#define DRIVER_AUTHOR "David Glance, davidgsf@sourceforge.net" +#define DRIVER_VERSION "v0.95" +#define DRIVER_AUTHOR "Juergen Stuber " #define DRIVER_DESC "LEGO USB Tower Driver" -/* Module paramaters */ +/* Module parameters */ MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Debug enabled or not"); +/* The defaults are chosen to work with the latest versions of leJOS and NQC. + */ + +/* Some legacy software likes to receive packets in one piece. + * In this case read_buffer_size should exceed the maximal packet length + * (417 for datalog uploads), and packet_timeout should be set. + */ +static size_t read_buffer_size = 480; +MODULE_PARM(read_buffer_size, "i"); +MODULE_PARM_DESC(read_buffer_size, "Read buffer size"); + +/* Some legacy software likes to send packets in one piece. + * In this case write_buffer_size should exceed the maximal packet length + * (417 for firmware and program downloads). + * A problem with long writes is that the following read may time out + * if the software is not prepared to wait long enough. + */ +static size_t write_buffer_size = 480; +MODULE_PARM(write_buffer_size, "i"); +MODULE_PARM_DESC(write_buffer_size, "Write buffer size"); + +/* Some legacy software expects reads to contain whole LASM packets. + * To achieve this, characters which arrive before a packet timeout + * occurs will be returned in a single read operation. + * A problem with long reads is that the software may time out + * if it is not prepared to wait long enough. + * The packet timeout should be greater than the time between the + * reception of subsequent characters, which should arrive about + * every 5ms for the standard 2400 baud. + * Set it to 0 to disable. + */ +static int packet_timeout = 50; +MODULE_PARM(packet_timeout, "i"); +MODULE_PARM_DESC(packet_timeout, "Packet timeout in ms"); + +/* Some legacy software expects blocking reads to time out. + * Timeout occurs after the specified time of read and write inactivity. + * Set it to 0 to disable. + */ +static int read_timeout = 200; +MODULE_PARM(read_timeout, "i"); +MODULE_PARM_DESC(read_timeout, "Read timeout in ms"); + +/* As of kernel version 2.6.4 ehci-hcd uses an + * "only one interrupt transfer per frame" shortcut + * to simplify the scheduling of periodic transfers. + * This conflicts with our standard 1ms intervals for in and out URBs. + * We use default intervals of 2ms for in and 8ms for out transfers, + * which is fast enough for 2400 baud and allows a small additional load. + * Increase the interval to allow more devices that do interrupt transfers, + * or set to 0 to use the standard interval from the endpoint descriptors. + */ +static int interrupt_in_interval = 2; +MODULE_PARM(interrupt_in_interval, "i"); +MODULE_PARM_DESC(interrupt_in_interval, "Interrupt in interval in ms"); + +static int interrupt_out_interval = 8; +MODULE_PARM(interrupt_out_interval, "i"); +MODULE_PARM_DESC(interrupt_out_interval, "Interrupt out interval in ms"); /* Define these values to match your device */ #define LEGO_USB_TOWER_VENDOR_ID 0x0694 #define LEGO_USB_TOWER_PRODUCT_ID 0x0001 +/* Vendor requests */ +#define LEGO_USB_TOWER_REQUEST_RESET 0x04 +#define LEGO_USB_TOWER_REQUEST_GET_VERSION 0xFD + +struct tower_reset_reply { + __u16 size; /* little-endian */ + __u8 err_code; + __u8 spare; +} __attribute__ ((packed)); + +struct tower_get_version_reply { + __u16 size; /* little-endian */ + __u8 err_code; + __u8 spare; + __u8 major; + __u8 minor; + __u16 build_no; /* little-endian */ +} __attribute__ ((packed)); + + /* table of devices that work with this driver */ static struct usb_device_id tower_table [] = { { USB_DEVICE(LEGO_USB_TOWER_VENDOR_ID, LEGO_USB_TOWER_PRODUCT_ID) }, @@ -90,22 +200,21 @@ #define LEGO_USB_TOWER_MINOR_BASE 160 -/* we can have up to this number of device plugged in at once */ -#define MAX_DEVICES 16 - -#define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */ /* Structure to hold all of our device specific stuff */ struct lego_usb_tower { struct semaphore sem; /* locks this structure */ - struct usb_device* udev; /* save off the usb device pointer */ - struct usb_interface* interface; + struct usb_device* udev; /* save off the usb device pointer */ unsigned char minor; /* the starting minor number for this device */ int open_count; /* number of times this port has been opened */ char* read_buffer; - int read_buffer_length; + size_t read_buffer_length; /* this much came in */ + size_t read_packet_length; /* this much will be returned on read */ + spinlock_t read_buffer_lock; + int packet_timeout_jiffies; + unsigned long read_last_arrival; wait_queue_head_t read_wait; wait_queue_head_t write_wait; @@ -113,18 +222,18 @@ char* interrupt_in_buffer; struct usb_endpoint_descriptor* interrupt_in_endpoint; struct urb* interrupt_in_urb; + int interrupt_in_interval; + int interrupt_in_running; + int interrupt_in_done; char* interrupt_out_buffer; struct usb_endpoint_descriptor* interrupt_out_endpoint; struct urb* interrupt_out_urb; + int interrupt_out_interval; + int interrupt_out_busy; }; -/* Note that no locking is needed: - * read_buffer is arbitrated by read_buffer_length == 0 - * interrupt_out_buffer is arbitrated by interrupt_out_urb->status == -EINPROGRESS - * interrupt_in_buffer belongs to urb alone and is overwritten on overflow - */ /* local function prototypes */ static ssize_t tower_read (struct file *file, char *buffer, size_t count, loff_t *ppos); @@ -132,8 +241,11 @@ static inline void tower_delete (struct lego_usb_tower *dev); static int tower_open (struct inode *inode, struct file *file); static int tower_release (struct inode *inode, struct file *file); -static int tower_release_internal (struct lego_usb_tower *dev); +static unsigned int tower_poll (struct file *file, poll_table *wait); +static loff_t tower_llseek (struct file *file, loff_t off, int whence); + static void tower_abort_transfers (struct lego_usb_tower *dev); +static void tower_check_for_read_packet (struct lego_usb_tower *dev); static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs); static void tower_interrupt_out_callback (struct urb *urb, struct pt_regs *regs); @@ -146,14 +258,16 @@ /* file operations needed when we register this driver */ static struct file_operations tower_fops = { - .owner = THIS_MODULE, - .read = tower_read, + .owner = THIS_MODULE, + .read = tower_read, .write = tower_write, .open = tower_open, - .release = tower_release, + .release = tower_release, + .poll = tower_poll, + .llseek = tower_llseek, }; -/* +/* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with devfs and the driver core */ @@ -167,11 +281,11 @@ /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver tower_driver = { - .owner = THIS_MODULE, - .name = "legousbtower", - .probe = tower_probe, - .disconnect = tower_disconnect, - .id_table = tower_table, + .owner = THIS_MODULE, + .name = "legousbtower", + .probe = tower_probe, + .disconnect = tower_disconnect, + .id_table = tower_table, }; @@ -183,8 +297,8 @@ int i; if (debug < level) - return; - + return; + printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", function, size); for (i = 0; i < size; ++i) { printk ("%.2x ", data[i]); @@ -198,7 +312,7 @@ */ static inline void tower_delete (struct lego_usb_tower *dev) { - dbg(2, "%s enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); tower_abort_transfers (dev); @@ -214,7 +328,7 @@ kfree (dev->interrupt_out_buffer); kfree (dev); - dbg(2, "%s : leave", __func__); + dbg(2, "%s: leave", __FUNCTION__); } @@ -228,7 +342,7 @@ int retval = 0; struct usb_interface *interface; - dbg(2,"%s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); subminor = iminor(inode); @@ -240,37 +354,63 @@ err ("%s - error, can't find device for minor %d", __FUNCTION__, subminor); retval = -ENODEV; - goto exit_no_device; + goto unlock_disconnect_exit; } dev = usb_get_intfdata(interface); if (!dev) { retval = -ENODEV; - goto exit_no_device; + goto unlock_disconnect_exit; } /* lock this device */ - down (&dev->sem); - - - /* increment our usage count for the device */ - ++dev->open_count; - - /* save device in the file's private structure */ - file->private_data = dev; + if (down_interruptible (&dev->sem)) { + retval = -ERESTARTSYS; + goto unlock_disconnect_exit; + } + /* allow opening only once */ + if (dev->open_count) { + retval = -EBUSY; + goto unlock_exit; + } + dev->open_count = 1; /* initialize in direction */ dev->read_buffer_length = 0; + dev->read_packet_length = 0; + usb_fill_int_urb (dev->interrupt_in_urb, + dev->udev, + usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), + dev->interrupt_in_buffer, + dev->interrupt_in_endpoint->wMaxPacketSize, + tower_interrupt_in_callback, + dev, + dev->interrupt_in_interval); + + dev->interrupt_in_running = 1; + dev->interrupt_in_done = 0; + mb(); - up (&dev->sem); + retval = usb_submit_urb (dev->interrupt_in_urb, GFP_KERNEL); + if (retval) { + err("Couldn't submit interrupt_in_urb %d", retval); + dev->interrupt_in_running = 0; + dev->open_count = 0; + goto unlock_exit; + } -exit_no_device: + /* save device in the file's private structure */ + file->private_data = dev; +unlock_exit: + up (&dev->sem); + +unlock_disconnect_exit: up (&disconnect_sem); - dbg(2,"%s : leave, return value %d ", __func__, retval); + dbg(2, "%s: leave, return value %d ", __FUNCTION__, retval); return retval; } @@ -283,87 +423,137 @@ struct lego_usb_tower *dev; int retval = 0; - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); dev = (struct lego_usb_tower *)file->private_data; if (dev == NULL) { - dbg(1," %s : object is NULL", __func__); + dbg(1, "%s: object is NULL", __FUNCTION__); retval = -ENODEV; goto exit; } + if (down_interruptible (&dev->sem)) { + retval = -ERESTARTSYS; + goto exit; + } - /* lock our device */ - down (&dev->sem); - - if (dev->open_count <= 0) { - dbg(1," %s : device not opened", __func__); + if (dev->open_count != 1) { + dbg(1, "%s: device not opened exactly once", __FUNCTION__); retval = -ENODEV; + goto unlock_exit; + } + if (dev->udev == NULL) { + /* the device was unplugged before the file was released */ + up (&dev->sem); /* unlock here as tower_delete frees dev */ + tower_delete (dev); goto exit; } - /* do the work */ - retval = tower_release_internal (dev); + /* wait until write transfer is finished */ + if (dev->interrupt_out_busy) { + wait_event_interruptible_timeout (dev->write_wait, !dev->interrupt_out_busy, 2 * HZ); + } + tower_abort_transfers (dev); + dev->open_count = 0; -exit: +unlock_exit: up (&dev->sem); - dbg(2," %s : leave, return value %d", __func__, retval); + +exit: + dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval; } /** - * tower_release_internal + * tower_abort_transfers + * aborts transfers and frees associated data structures */ -static int tower_release_internal (struct lego_usb_tower *dev) +static void tower_abort_transfers (struct lego_usb_tower *dev) { - int retval = 0; + dbg(2, "%s: enter", __FUNCTION__); - dbg(2," %s : enter", __func__); - - if (dev->udev == NULL) { - /* the device was unplugged before the file was released */ - tower_delete (dev); + if (dev == NULL) { + dbg(1, "%s: dev is null", __FUNCTION__); goto exit; } - /* decrement our usage count for the device */ - --dev->open_count; - if (dev->open_count <= 0) { - tower_abort_transfers (dev); - dev->open_count = 0; + /* shutdown transfer */ + if (dev->interrupt_in_running) { + dev->interrupt_in_running = 0; + mb(); + if (dev->interrupt_in_urb != NULL && dev->udev) { + usb_unlink_urb (dev->interrupt_in_urb); + } + } + if (dev->interrupt_out_busy) { + if (dev->interrupt_out_urb != NULL && dev->udev) { + usb_unlink_urb (dev->interrupt_out_urb); + } } exit: - dbg(2," %s : leave", __func__); - return retval; + dbg(2, "%s: leave", __FUNCTION__); } /** - * tower_abort_transfers - * aborts transfers and frees associated data structures + * tower_check_for_read_packet + * + * To get correct semantics for signals and non-blocking I/O + * with packetizing we pretend not to see any data in the read buffer + * until it has been there unchanged for at least + * dev->packet_timeout_jiffies, or until the buffer is full. */ -static void tower_abort_transfers (struct lego_usb_tower *dev) +static void tower_check_for_read_packet (struct lego_usb_tower *dev) { - dbg(2," %s : enter", __func__); - - if (dev == NULL) { - dbg(1," %s : dev is null", __func__); - goto exit; + spin_lock_irq (&dev->read_buffer_lock); + if (!packet_timeout + || time_after(jiffies, dev->read_last_arrival + dev->packet_timeout_jiffies) + || dev->read_buffer_length == read_buffer_size) { + dev->read_packet_length = dev->read_buffer_length; } + dev->interrupt_in_done = 0; + spin_unlock_irq (&dev->read_buffer_lock); +} - /* shutdown transfer */ - if (dev->interrupt_in_urb != NULL) { - usb_unlink_urb (dev->interrupt_in_urb); + +/** + * tower_poll + */ +static unsigned int tower_poll (struct file *file, poll_table *wait) +{ + struct lego_usb_tower *dev; + unsigned int mask = 0; + + dbg(2, "%s: enter", __FUNCTION__); + + dev = file->private_data; + + poll_wait(file, &dev->read_wait, wait); + poll_wait(file, &dev->write_wait, wait); + + tower_check_for_read_packet(dev); + if (dev->read_packet_length > 0) { + mask |= POLLIN | POLLRDNORM; } - if (dev->interrupt_out_urb != NULL) { - usb_unlink_urb (dev->interrupt_out_urb); + if (!dev->interrupt_out_busy) { + mask |= POLLOUT | POLLWRNORM; } -exit: - dbg(2," %s : leave", __func__); + dbg(2, "%s: leave, mask = %d", __FUNCTION__, mask); + + return mask; +} + + +/** + * tower_llseek + */ +static loff_t tower_llseek (struct file *file, loff_t off, int whence) +{ + return -ESPIPE; /* unseekable */ } @@ -373,96 +563,87 @@ static ssize_t tower_read (struct file *file, char *buffer, size_t count, loff_t *ppos) { struct lego_usb_tower *dev; - size_t bytes_read = 0; size_t bytes_to_read; int i; int retval = 0; - int timeout = 0; + unsigned long timeout = 0; - dbg(2," %s : enter, count = %Zd", __func__, count); + dbg(2, "%s: enter, count = %Zd", __FUNCTION__, count); dev = (struct lego_usb_tower *)file->private_data; - + /* lock this object */ - down (&dev->sem); + if (down_interruptible (&dev->sem)) { + retval = -ERESTARTSYS; + goto exit; + } /* verify that the device wasn't unplugged */ if (dev->udev == NULL) { retval = -ENODEV; err("No device or device unplugged %d", retval); - goto exit; + goto unlock_exit; } /* verify that we actually have some data to read */ if (count == 0) { - dbg(1," %s : read request of 0 bytes", __func__); - goto exit; + dbg(1, "%s: read request of 0 bytes", __FUNCTION__); + goto unlock_exit; } + if (read_timeout) { + timeout = jiffies + read_timeout * HZ / 1000; + } + + /* wait for data */ + tower_check_for_read_packet (dev); + while (dev->read_packet_length == 0) { + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto unlock_exit; + } + retval = wait_event_interruptible_timeout(dev->read_wait, dev->interrupt_in_done, dev->packet_timeout_jiffies); + if (retval < 0) { + goto unlock_exit; + } - timeout = COMMAND_TIMEOUT; + /* reset read timeout during read or write activity */ + if (read_timeout + && (dev->read_buffer_length || dev->interrupt_out_busy)) { + timeout = jiffies + read_timeout * HZ / 1000; + } + /* check for read timeout */ + if (read_timeout && time_after (jiffies, timeout)) { + retval = -ETIMEDOUT; + goto unlock_exit; + } + tower_check_for_read_packet (dev); + } - while (1) { - if (dev->read_buffer_length == 0) { + /* copy the data from read_buffer into userspace */ + bytes_to_read = min(count, dev->read_packet_length); - /* start reading */ - usb_fill_int_urb (dev->interrupt_in_urb,dev->udev, - usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), - dev->interrupt_in_buffer, - dev->interrupt_in_endpoint->wMaxPacketSize, - tower_interrupt_in_callback, - dev, - dev->interrupt_in_endpoint->bInterval); - - retval = usb_submit_urb (dev->interrupt_in_urb, GFP_KERNEL); - - if (retval < 0) { - err("Couldn't submit interrupt_in_urb"); - goto exit; - } - - if (timeout <= 0) { - retval = -ETIMEDOUT; - goto exit; - } - - if (signal_pending(current)) { - retval = -EINTR; - goto exit; - } - - up (&dev->sem); - timeout = interruptible_sleep_on_timeout (&dev->read_wait, timeout); - down (&dev->sem); + if (copy_to_user (buffer, dev->read_buffer, bytes_to_read)) { + retval = -EFAULT; + goto unlock_exit; + } - } else { - /* copy the data from read_buffer into userspace */ - bytes_to_read = count > dev->read_buffer_length ? dev->read_buffer_length : count; - if (copy_to_user (buffer, dev->read_buffer, bytes_to_read) != 0) { - retval = -EFAULT; - goto exit; - } - dev->read_buffer_length -= bytes_to_read; - for (i=0; iread_buffer_length; i++) { - dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read]; - } - - buffer += bytes_to_read; - count -= bytes_to_read; - bytes_read += bytes_to_read; - if (count == 0) { - break; - } - } + spin_lock_irq (&dev->read_buffer_lock); + dev->read_buffer_length -= bytes_to_read; + dev->read_packet_length -= bytes_to_read; + for (i=0; iread_buffer_length; i++) { + dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read]; } + spin_unlock_irq (&dev->read_buffer_lock); - retval = bytes_read; + retval = bytes_to_read; -exit: +unlock_exit: /* unlock the device */ up (&dev->sem); - dbg(2," %s : leave, return value %d", __func__, retval); +exit: + dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval; } @@ -473,107 +654,80 @@ static ssize_t tower_write (struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct lego_usb_tower *dev; - size_t bytes_written = 0; size_t bytes_to_write; - size_t buffer_size; int retval = 0; - int timeout = 0; - dbg(2," %s : enter, count = %Zd", __func__, count); + dbg(2, "%s: enter, count = %Zd", __FUNCTION__, count); dev = (struct lego_usb_tower *)file->private_data; /* lock this object */ - down (&dev->sem); + if (down_interruptible (&dev->sem)) { + retval = -ERESTARTSYS; + goto exit; + } /* verify that the device wasn't unplugged */ if (dev->udev == NULL) { retval = -ENODEV; err("No device or device unplugged %d", retval); - goto exit; + goto unlock_exit; } /* verify that we actually have some data to write */ if (count == 0) { - dbg(1," %s : write request of 0 bytes", __func__); - goto exit; + dbg(1, "%s: write request of 0 bytes", __FUNCTION__); + goto unlock_exit; } + /* wait until previous transfer is finished */ + while (dev->interrupt_out_busy) { + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto unlock_exit; + } + retval = wait_event_interruptible (dev->write_wait, !dev->interrupt_out_busy); + if (retval) { + goto unlock_exit; + } + } - while (count > 0) { - if (dev->interrupt_out_urb->status == -EINPROGRESS) { - timeout = COMMAND_TIMEOUT; - - while (timeout > 0) { - if (signal_pending(current)) { - dbg(1," %s : interrupted", __func__); - retval = -EINTR; - goto exit; - } - up (&dev->sem); - timeout = interruptible_sleep_on_timeout (&dev->write_wait, timeout); - down (&dev->sem); - if (timeout > 0) { - break; - } - dbg(1," %s : interrupted timeout: %d", __func__, timeout); - } - - - dbg(1," %s : final timeout: %d", __func__, timeout); - - if (timeout == 0) { - dbg(1, "%s - command timed out.", __func__); - retval = -ETIMEDOUT; - goto exit; - } - - dbg(4," %s : in progress, count = %Zd", __func__, count); - } else { - dbg(4," %s : sending, count = %Zd", __func__, count); - - /* write the data into interrupt_out_buffer from userspace */ - buffer_size = dev->interrupt_out_endpoint->wMaxPacketSize; - bytes_to_write = count > buffer_size ? buffer_size : count; - dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd", __func__, buffer_size, count, bytes_to_write); - - if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) { - retval = -EFAULT; - goto exit; - } - - /* send off the urb */ - usb_fill_int_urb(dev->interrupt_out_urb, - dev->udev, - usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress), - dev->interrupt_out_buffer, - bytes_to_write, - tower_interrupt_out_callback, - dev, - dev->interrupt_in_endpoint->bInterval); - - dev->interrupt_out_urb->actual_length = bytes_to_write; - retval = usb_submit_urb (dev->interrupt_out_urb, GFP_KERNEL); - - if (retval < 0) { - err("Couldn't submit interrupt_out_urb %d", retval); - goto exit; - } + /* write the data into interrupt_out_buffer from userspace */ + bytes_to_write = min(count, write_buffer_size); + dbg(4, "%s: count = %Zd, bytes_to_write = %Zd", __FUNCTION__, count, bytes_to_write); + + if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write)) { + retval = -EFAULT; + goto unlock_exit; + } + + /* send off the urb */ + usb_fill_int_urb(dev->interrupt_out_urb, + dev->udev, + usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress), + dev->interrupt_out_buffer, + bytes_to_write, + tower_interrupt_out_callback, + dev, + dev->interrupt_out_interval); - buffer += bytes_to_write; - count -= bytes_to_write; + dev->interrupt_out_busy = 1; + wmb(); - bytes_written += bytes_to_write; - } + retval = usb_submit_urb (dev->interrupt_out_urb, GFP_KERNEL); + if (retval) { + dev->interrupt_out_busy = 0; + err("Couldn't submit interrupt_out_urb %d", retval); + goto unlock_exit; } + retval = bytes_to_write; - retval = bytes_written; - -exit: +unlock_exit: /* unlock the device */ up (&dev->sem); - dbg(2," %s : leave, return value %d", __func__, retval); +exit: + dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval; } @@ -585,39 +739,53 @@ static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs) { struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context; + int retval; - dbg(4," %s : enter, status %d", __func__, urb->status); + dbg(4, "%s: enter, status %d", __FUNCTION__, urb->status); - lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); + lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); - if (urb->status != 0) { - if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) { - dbg(1," %s : nonzero status received: %d", __func__, urb->status); + if (urb->status) { + if (urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN) { + goto exit; + } else { + dbg(1, "%s: nonzero status received: %d", __FUNCTION__, urb->status); + goto resubmit; /* maybe we can recover */ } - goto exit; } - down (&dev->sem); - if (urb->actual_length > 0) { - if (dev->read_buffer_length < (4 * dev->interrupt_in_endpoint->wMaxPacketSize) - (urb->actual_length)) { - - memcpy (dev->read_buffer+dev->read_buffer_length, dev->interrupt_in_buffer, urb->actual_length); - + spin_lock (&dev->read_buffer_lock); + if (dev->read_buffer_length + urb->actual_length < read_buffer_size) { + memcpy (dev->read_buffer + dev->read_buffer_length, + dev->interrupt_in_buffer, + urb->actual_length); dev->read_buffer_length += urb->actual_length; - dbg(1," %s reading %d ", __func__, urb->actual_length); - wake_up_interruptible (&dev->read_wait); - + dev->read_last_arrival = jiffies; + dbg(3, "%s: received %d bytes", __FUNCTION__, urb->actual_length); } else { - dbg(1," %s : read_buffer overflow", __func__); + printk(KERN_WARNING "%s: read_buffer overflow, %d bytes dropped", __FUNCTION__, urb->actual_length); } + spin_unlock (&dev->read_buffer_lock); } - up (&dev->sem); +resubmit: + /* resubmit if we're still running */ + if (dev->interrupt_in_running && dev->udev) { + retval = usb_submit_urb (dev->interrupt_in_urb, GFP_ATOMIC); + if (retval) { + err("%s: usb_submit_urb failed (%d)", __FUNCTION__, retval); + } + } exit: - lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); - dbg(4," %s : leave, status %d", __func__, urb->status); + dev->interrupt_in_done = 1; + wake_up_interruptible (&dev->read_wait); + + lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); + dbg(4, "%s: leave, status %d", __FUNCTION__, urb->status); } @@ -628,22 +796,22 @@ { struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context; - dbg(4," %s : enter, status %d", __func__, urb->status); - lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); + dbg(4, "%s: enter, status %d", __FUNCTION__, urb->status); + lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); - if (urb->status != 0) { - if ((urb->status != -ENOENT) && - (urb->status != -ECONNRESET)) { - dbg(1, " %s :nonzero status received: %d", __func__, urb->status); - } - goto exit; - } + /* sync/async unlink faults aren't errors */ + if (urb->status && !(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) { + dbg(1, "%s - nonzero write bulk status received: %d", + __FUNCTION__, urb->status); + } + dev->interrupt_out_busy = 0; wake_up_interruptible(&dev->write_wait); -exit: - lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); - dbg(4," %s : leave, status %d", __func__, urb->status); + lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); + dbg(4, "%s: leave, status %d", __FUNCTION__, urb->status); } @@ -659,15 +827,18 @@ struct lego_usb_tower *dev = NULL; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor* endpoint; + struct tower_reset_reply reset_reply; + struct tower_get_version_reply get_version_reply; int i; int retval = -ENOMEM; + int result; - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); if (udev == NULL) { info ("udev is NULL."); } - + /* See if the device offered us matches what we can accept */ if ((udev->descriptor.idVendor != LEGO_USB_TOWER_VENDOR_ID) || (udev->descriptor.idProduct != LEGO_USB_TOWER_PRODUCT_ID)) { @@ -691,6 +862,10 @@ dev->read_buffer = NULL; dev->read_buffer_length = 0; + dev->read_packet_length = 0; + spin_lock_init (&dev->read_buffer_lock); + dev->packet_timeout_jiffies = packet_timeout * HZ / 1000; + dev->read_last_arrival = jiffies; init_waitqueue_head (&dev->read_wait); init_waitqueue_head (&dev->write_wait); @@ -698,13 +873,15 @@ dev->interrupt_in_buffer = NULL; dev->interrupt_in_endpoint = NULL; dev->interrupt_in_urb = NULL; + dev->interrupt_in_running = 0; + dev->interrupt_in_done = 0; dev->interrupt_out_buffer = NULL; dev->interrupt_out_endpoint = NULL; dev->interrupt_out_urb = NULL; + dev->interrupt_out_busy = 0; - - iface_desc = &interface->altsetting[0]; + iface_desc = interface->cur_altsetting; /* set up the endpoint information */ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { @@ -714,7 +891,7 @@ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) { dev->interrupt_in_endpoint = endpoint; } - + if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) { dev->interrupt_out_endpoint = endpoint; @@ -729,7 +906,7 @@ goto error; } - dev->read_buffer = kmalloc ((4*dev->interrupt_in_endpoint->wMaxPacketSize), GFP_KERNEL); + dev->read_buffer = kmalloc (read_buffer_size, GFP_KERNEL); if (!dev->read_buffer) { err("Couldn't allocate read_buffer"); goto error; @@ -744,7 +921,7 @@ err("Couldn't allocate interrupt_in_urb"); goto error; } - dev->interrupt_out_buffer = kmalloc (dev->interrupt_out_endpoint->wMaxPacketSize, GFP_KERNEL); + dev->interrupt_out_buffer = kmalloc (write_buffer_size, GFP_KERNEL); if (!dev->interrupt_out_buffer) { err("Couldn't allocate interrupt_out_buffer"); goto error; @@ -753,7 +930,9 @@ if (!dev->interrupt_out_urb) { err("Couldn't allocate interrupt_out_urb"); goto error; - } + } + dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval; + dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval; /* we can register the device now, as it is ready */ usb_set_intfdata (interface, dev); @@ -766,16 +945,50 @@ usb_set_intfdata (interface, NULL); goto error; } - dev->minor = interface->minor; /* let the user know what node this device is now attached to */ - info ("LEGO USB Tower device now attached to /dev/usb/lego%d", (dev->minor - LEGO_USB_TOWER_MINOR_BASE)); + info ("LEGO USB Tower #%d now attached to major %d minor %d", (dev->minor - LEGO_USB_TOWER_MINOR_BASE), USB_MAJOR, dev->minor); + /* reset the tower */ + result = usb_control_msg (udev, + usb_rcvctrlpipe(udev, 0), + LEGO_USB_TOWER_REQUEST_RESET, + USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, + 0, + 0, + &reset_reply, + sizeof(reset_reply), + HZ); + if (result < 0) { + err("LEGO USB Tower reset control request failed"); + retval = result; + goto error; + } + + /* get the firmware version and log it */ + result = usb_control_msg (udev, + usb_rcvctrlpipe(udev, 0), + LEGO_USB_TOWER_REQUEST_GET_VERSION, + USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, + 0, + 0, + &get_version_reply, + sizeof(get_version_reply), + HZ); + if (result < 0) { + err("LEGO USB Tower get version control request failed"); + retval = result; + goto error; + } + info("LEGO USB Tower firmware version is %d.%d build %d", + get_version_reply.major, + get_version_reply.minor, + le16_to_cpu(get_version_reply.build_no)); exit: - dbg(2," %s : leave, return value 0x%.8lx (dev)", __func__, (long) dev); + dbg(2, "%s: leave, return value 0x%.8lx (dev)", __FUNCTION__, (long) dev); return retval; @@ -795,7 +1008,7 @@ struct lego_usb_tower *dev; int minor; - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); down (&disconnect_sem); @@ -823,7 +1036,7 @@ info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE)); - dbg(2," %s : leave", __func__); + dbg(2, "%s: leave", __FUNCTION__); } @@ -836,7 +1049,7 @@ int result; int retval = 0; - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); /* register this driver with the USB subsystem */ result = usb_register(&tower_driver); @@ -849,7 +1062,7 @@ info(DRIVER_DESC " " DRIVER_VERSION); exit: - dbg(2," %s : leave, return value %d", __func__, retval); + dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval; } @@ -860,12 +1073,12 @@ */ static void __exit lego_usb_tower_exit(void) { - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); /* deregister this driver with the USB subsystem */ usb_deregister (&tower_driver); - dbg(2," %s : leave", __func__); + dbg(2, "%s: leave", __FUNCTION__); } module_init (lego_usb_tower_init); diff -Nru a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/misc/phidgetservo.c Wed May 12 20:33:40 2004 @@ -0,0 +1,327 @@ +/* + * USB PhidgetServo driver 1.0 + * + * Copyright (C) 2004 Sean Young + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is a driver for the USB PhidgetServo version 2.0 and 3.0 servo + * controllers available at: http://www.phidgets.com/ + * + * Note that the driver takes input as: degrees.minutes + * -23 < degrees < 203 + * 0 < minutes < 59 + * + * CAUTION: Generally you should use 0 < degrees < 180 as anything else + * is probably beyond the range of your servo and may damage it. + */ + +#include +#ifdef CONFIG_USB_DEBUG +#define DEBUG 1 +#endif +#include +#include +#include +#include +#include +#include + +#define DRIVER_AUTHOR "Sean Young " +#define DRIVER_DESC "USB PhidgetServo Driver" + +#define VENDOR_ID_GLAB 0x06c2 +#define DEVICE_ID_4MOTOR_SERVO_30 0x0038 +#define DEVICE_ID_1MOTOR_SERVO_30 0x0039 + +#define VENDOR_ID_WISEGROUP 0x0925 +#define DEVICE_ID_1MOTOR_SERVO_20 0x8101 +#define DEVICE_ID_4MOTOR_SERVO_20 0x8104 + +static struct usb_device_id id_table[] = { + {USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_4MOTOR_SERVO_30)}, + {USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_1MOTOR_SERVO_30)}, + {USB_DEVICE(VENDOR_ID_WISEGROUP, DEVICE_ID_4MOTOR_SERVO_20)}, + {USB_DEVICE(VENDOR_ID_WISEGROUP, DEVICE_ID_1MOTOR_SERVO_20)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, id_table); + +struct phidget_servo { + struct usb_device *udev; + int version; + int quad_servo; + int pulse[4]; + int degrees[4]; + int minutes[4]; +}; + +static void +change_position_v30(struct phidget_servo *servo, int servo_no, int degrees, + int minutes) +{ + int retval; + unsigned char *buffer; + + buffer = kmalloc(6, GFP_KERNEL); + if (!buffer) { + dev_err(&servo->udev->dev, "%s - out of memory\n", + __FUNCTION__); + return; + } + + /* + * pulse = 0 - 4095 + * angle = 0 - 180 degrees + * + * pulse = angle * 10.6 + 243.8 + */ + servo->pulse[servo_no] = ((degrees*60 + minutes)*106 + 2438*60)/600; + servo->degrees[servo_no]= degrees; + servo->minutes[servo_no]= minutes; + + /* + * The PhidgetServo v3.0 is controlled by sending 6 bytes, + * 4 * 12 bits for each servo. + * + * low = lower 8 bits pulse + * high = higher 4 bits pulse + * + * offset bits + * +---+-----------------+ + * | 0 | low 0 | + * +---+--------+--------+ + * | 1 | high 1 | high 0 | + * +---+--------+--------+ + * | 2 | low 1 | + * +---+-----------------+ + * | 3 | low 2 | + * +---+--------+--------+ + * | 4 | high 3 | high 2 | + * +---+--------+--------+ + * | 5 | low 3 | + * +---+-----------------+ + */ + + buffer[0] = servo->pulse[0] & 0xff; + buffer[1] = (servo->pulse[0] >> 8 & 0x0f) + | (servo->pulse[1] >> 4 & 0xf0); + buffer[2] = servo->pulse[1] & 0xff; + buffer[3] = servo->pulse[2] & 0xff; + buffer[4] = (servo->pulse[2] >> 8 & 0x0f) + | (servo->pulse[3] >> 4 & 0xf0); + buffer[5] = servo->pulse[3] & 0xff; + + dev_dbg(&servo->udev->dev, + "data: %02x %02x %02x %02x %02x %02x\n", + buffer[0], buffer[1], buffer[2], + buffer[3], buffer[4], buffer[5]); + + retval = usb_control_msg(servo->udev, + usb_sndctrlpipe(servo->udev, 0), + 0x09, 0x21, 0x0200, 0x0000, buffer, 6, 2 * HZ); + if (retval != 6) + dev_err(&servo->udev->dev, "retval = %d\n", retval); + kfree(buffer); +} + +static void +change_position_v20(struct phidget_servo *servo, int servo_no, int degrees, + int minutes) +{ + int retval; + unsigned char *buffer; + + buffer = kmalloc(2, GFP_KERNEL); + if (!buffer) { + dev_err(&servo->udev->dev, "%s - out of memory\n", + __FUNCTION__); + return; + } + + /* + * angle = 0 - 180 degrees + * pulse = angle + 23 + */ + servo->pulse[servo_no]= degrees + 23; + servo->degrees[servo_no]= degrees; + servo->minutes[servo_no]= 0; + + /* + * The PhidgetServo v2.0 is controlled by sending two bytes. The + * first byte is the servo number xor'ed with 2: + * + * servo 0 = 2 + * servo 1 = 3 + * servo 2 = 0 + * servo 3 = 1 + * + * The second byte is the position. + */ + + buffer[0] = servo_no ^ 2; + buffer[1] = servo->pulse[servo_no]; + + dev_dbg(&servo->udev->dev, "data: %02x %02x\n", buffer[0], buffer[1]); + + retval = usb_control_msg(servo->udev, + usb_sndctrlpipe(servo->udev, 0), + 0x09, 0x21, 0x0200, 0x0000, buffer, 2, 2 * HZ); + if (retval != 2) + dev_err(&servo->udev->dev, "retval = %d\n", retval); + kfree(buffer); +} + +#define show_set(value) \ +static ssize_t set_servo##value (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + int degrees, minutes; \ + struct usb_interface *intf = to_usb_interface (dev); \ + struct phidget_servo *servo = usb_get_intfdata (intf); \ + \ + minutes = 0; \ + /* must at least convert degrees */ \ + if (sscanf (buf, "%d.%d", °rees, &minutes) < 1) { \ + return -EINVAL; \ + } \ + \ + if (degrees < -23 || degrees > (180 + 23) || \ + minutes < 0 || minutes > 59) { \ + return -EINVAL; \ + } \ + \ + if (servo->version >= 3) \ + change_position_v30 (servo, value, degrees, minutes); \ + else \ + change_position_v20 (servo, value, degrees, minutes); \ + \ + return count; \ +} \ + \ +static ssize_t show_servo##value (struct device *dev, char *buf) \ +{ \ + struct usb_interface *intf = to_usb_interface (dev); \ + struct phidget_servo *servo = usb_get_intfdata (intf); \ + \ + return sprintf (buf, "%d.%02d\n", servo->degrees[value], \ + servo->minutes[value]); \ +} \ +static DEVICE_ATTR(servo##value, S_IWUGO | S_IRUGO, \ + show_servo##value, set_servo##value); + +show_set(0); +show_set(1); +show_set(2); +show_set(3); + +static int +servo_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct phidget_servo *dev = NULL; + + dev = kmalloc(sizeof (struct phidget_servo), GFP_KERNEL); + if (dev == NULL) { + dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__); + return -ENOMEM; + } + memset(dev, 0x00, sizeof (*dev)); + + dev->udev = usb_get_dev(udev); + switch (udev->descriptor.idVendor) { + case VENDOR_ID_WISEGROUP: + dev->version = 2; + break; + case VENDOR_ID_GLAB: + dev->version = 3; + break; + } + switch (udev->descriptor.idProduct) { + case DEVICE_ID_4MOTOR_SERVO_20: + case DEVICE_ID_4MOTOR_SERVO_30: + dev->quad_servo = 1; + break; + case DEVICE_ID_1MOTOR_SERVO_20: + case DEVICE_ID_1MOTOR_SERVO_30: + dev->quad_servo = 0; + break; + } + + usb_set_intfdata(interface, dev); + + device_create_file(&interface->dev, &dev_attr_servo0); + if (dev->quad_servo) { + device_create_file(&interface->dev, &dev_attr_servo1); + device_create_file(&interface->dev, &dev_attr_servo2); + device_create_file(&interface->dev, &dev_attr_servo3); + } + + dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 attached\n", + dev->quad_servo ? 4 : 1, dev->version); + if (dev->version == 2) + dev_info(&interface->dev, + "WARNING: v2.0 not tested! Please report if it works.\n"); + + return 0; +} + +static void +servo_disconnect(struct usb_interface *interface) +{ + struct phidget_servo *dev; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + device_remove_file(&interface->dev, &dev_attr_servo0); + if (dev->quad_servo) { + device_remove_file(&interface->dev, &dev_attr_servo1); + device_remove_file(&interface->dev, &dev_attr_servo2); + device_remove_file(&interface->dev, &dev_attr_servo3); + } + + usb_put_dev(dev->udev); + + kfree(dev); + + dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 detached\n", + dev->quad_servo ? 4 : 1, dev->version); +} + +static struct usb_driver servo_driver = { + .owner = THIS_MODULE, + .name = "phidgetservo", + .probe = servo_probe, + .disconnect = servo_disconnect, + .id_table = id_table +}; + +static int __init +phidget_servo_init(void) +{ + int retval = 0; + + retval = usb_register(&servo_driver); + if (retval) + err("usb_register failed. Error number %d", retval); + + return retval; +} + +static void __exit +phidget_servo_exit(void) +{ + usb_deregister(&servo_driver); +} + +module_init(phidget_servo_init); +module_exit(phidget_servo_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/usb/misc/tiglusb.c b/drivers/usb/misc/tiglusb.c --- a/drivers/usb/misc/tiglusb.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/misc/tiglusb.c Wed May 12 20:33:39 2004 @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "tiglusb.h" @@ -49,6 +50,7 @@ static tiglusb_t tiglusb[MAXTIGL]; static int timeout = TIMAXTIME; /* timeout in tenth of seconds */ +static struct class_simple *tiglusb_class; /*---------- misc functions ------------------------------------------- */ @@ -336,7 +338,7 @@ { struct usb_device *dev = interface_to_usbdev(intf); int minor = -1; - int i; + int i, err = 0; ptiglusb_t s; dbg ("probing vendor id 0x%x, device id 0x%x", @@ -347,18 +349,23 @@ * the TIGL hardware, there's only 1 configuration. */ - if (dev->descriptor.bNumConfigurations != 1) - return -ENODEV; + if (dev->descriptor.bNumConfigurations != 1) { + err = -ENODEV; + goto out; + } if ((dev->descriptor.idProduct != 0xe001) - && (dev->descriptor.idVendor != 0x451)) - return -ENODEV; + && (dev->descriptor.idVendor != 0x451)) { + err = -ENODEV; + goto out; + } // NOTE: it's already in this config, this shouldn't be needed. // is this working around some hardware bug? if (usb_reset_configuration (dev) < 0) { err ("tiglusb_probe: reset_configuration failed"); - return -ENODEV; + err = -ENODEV; + goto out; } /* @@ -372,8 +379,10 @@ } } - if (minor == -1) - return -ENODEV; + if (minor == -1) { + err = -ENODEV; + goto out; + } s = &tiglusb[minor]; @@ -383,17 +392,28 @@ up (&s->mutex); dbg ("bound to interface"); - devfs_mk_cdev(MKDEV(TIUSB_MAJOR, TIUSB_MINOR) + s->minor, + class_simple_device_add(tiglusb_class, MKDEV(TIUSB_MAJOR, TIUSB_MINOR + s->minor), + NULL, "usb%d", s->minor); + err = devfs_mk_cdev(MKDEV(TIUSB_MAJOR, TIUSB_MINOR) + s->minor, S_IFCHR | S_IRUGO | S_IWUGO, "ticables/usb/%d", s->minor); + if (err) + goto out_class; + /* Display firmware version */ info ("firmware revision %i.%02x", dev->descriptor.bcdDevice >> 8, dev->descriptor.bcdDevice & 0xff); usb_set_intfdata (intf, s); - return 0; + err = 0; + goto out; + +out_class: + class_simple_device_remove(MKDEV(TIUSB_MAJOR, TIUSB_MINOR + s->minor)); +out: + return err; } static void @@ -423,6 +443,7 @@ s->dev = NULL; s->opened = 0; + class_simple_device_remove(MKDEV(TIUSB_MAJOR, TIUSB_MINOR + s->minor)); devfs_remove("ticables/usb/%d", s->minor); info ("device %d removed", s->minor); @@ -473,7 +494,7 @@ tiglusb_init (void) { unsigned u; - int result; + int result, err = 0; /* initialize struct */ for (u = 0; u < MAXTIGL; u++) { @@ -490,28 +511,41 @@ /* register device */ if (register_chrdev (TIUSB_MAJOR, "tiglusb", &tiglusb_fops)) { err ("unable to get major %d", TIUSB_MAJOR); - return -EIO; + err = -EIO; + goto out; } /* Use devfs, tree: /dev/ticables/usb/[0..3] */ devfs_mk_dir ("ticables/usb"); + tiglusb_class = class_simple_create(THIS_MODULE, "tiglusb"); + if (IS_ERR(tiglusb_class)) { + err = PTR_ERR(tiglusb_class); + goto out_chrdev; + } /* register USB module */ result = usb_register (&tiglusb_driver); if (result < 0) { - unregister_chrdev (TIUSB_MAJOR, "tiglusb"); - return -1; + err = -1; + goto out_chrdev; } info (DRIVER_DESC ", version " DRIVER_VERSION); - return 0; + err = 0; + goto out; + +out_chrdev: + unregister_chrdev (TIUSB_MAJOR, "tiglusb"); +out: + return err; } static void __exit tiglusb_cleanup (void) { usb_deregister (&tiglusb_driver); + class_simple_destroy(tiglusb_class); devfs_remove("ticables/usb"); unregister_chrdev (TIUSB_MAJOR, "tiglusb"); } diff -Nru a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c --- a/drivers/usb/misc/usbtest.c Wed May 12 20:33:40 2004 +++ b/drivers/usb/misc/usbtest.c Wed May 12 20:33:40 2004 @@ -802,6 +802,7 @@ switch (status) { case -EINPROGRESS: case -EBUSY: + case -EIDRM: continue; default: dbg ("urb unlink --> %d", status); @@ -1038,8 +1039,6 @@ if (!status) status = usb_submit_urb (urb, SLAB_ATOMIC); if (status) { - if (status == -ECONNRESET || status == -ENOENT) - status = 0; urb->status = status; complete ((struct completion *) urb->context); } @@ -1077,8 +1076,9 @@ wait_ms (jiffies % (2 * INTERRUPT_RATE)); retry: retval = usb_unlink_urb (urb); - if (retval == -EBUSY) { + if (retval == -EBUSY || retval == -EIDRM) { /* we can't unlink urbs while they're completing. + * or if they've completed, and we haven't resubmitted. * "normal" drivers would prevent resubmission, but * since we're testing unlink paths, we can't. */ diff -Nru a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c --- a/drivers/usb/misc/uss720.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/misc/uss720.c Wed May 12 20:33:39 2004 @@ -553,7 +553,7 @@ i = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2); printk(KERN_DEBUG "uss720: set inteface result %d\n", i); - interface = &intf->altsetting[2]; + interface = intf->cur_altsetting; /* * Allocate parport interface diff -Nru a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c --- a/drivers/usb/net/kaweth.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/net/kaweth.c Wed May 12 20:33:39 2004 @@ -123,9 +123,10 @@ const struct usb_device_id *id /* from id_table */ ); static void kaweth_disconnect(struct usb_interface *intf); -int kaweth_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, - struct usb_ctrlrequest *cmd, void *data, - int len, int timeout); +static int kaweth_internal_control_msg(struct usb_device *usb_dev, + unsigned int pipe, + struct usb_ctrlrequest *cmd, void *data, + int len, int timeout); /**************************************************************** * usb_device_id @@ -1277,9 +1278,10 @@ /*-------------------------------------------------------------------*/ // returns status (negative) or length (positive) -int kaweth_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, - struct usb_ctrlrequest *cmd, void *data, int len, - int timeout) +static int kaweth_internal_control_msg(struct usb_device *usb_dev, + unsigned int pipe, + struct usb_ctrlrequest *cmd, void *data, + int len, int timeout) { struct urb *urb; int retv; @@ -1305,7 +1307,7 @@ /**************************************************************** * kaweth_init ****************************************************************/ -int __init kaweth_init(void) +static int __init kaweth_init(void) { kaweth_dbg("Driver loading"); return usb_register(&kaweth_driver); @@ -1314,7 +1316,7 @@ /**************************************************************** * kaweth_exit ****************************************************************/ -void __exit kaweth_exit(void) +static void __exit kaweth_exit(void) { usb_deregister(&kaweth_driver); } diff -Nru a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c --- a/drivers/usb/net/pegasus.c Wed May 12 20:33:40 2004 +++ b/drivers/usb/net/pegasus.c Wed May 12 20:33:40 2004 @@ -1321,13 +1321,13 @@ .id_table = pegasus_ids, }; -int __init pegasus_init(void) +static int __init pegasus_init(void) { info(DRIVER_VERSION ":" DRIVER_DESC); return usb_register(&pegasus_driver); } -void __exit pegasus_exit(void) +static void __exit pegasus_exit(void) { usb_deregister(&pegasus_driver); } diff -Nru a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c --- a/drivers/usb/net/rtl8150.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/net/rtl8150.c Wed May 12 20:33:39 2004 @@ -167,7 +167,7 @@ typedef struct rtl8150 rtl8150_t; -unsigned long multicast_filter_limit = 32; +static unsigned long multicast_filter_limit = 32; static void fill_skb_pool(rtl8150_t *); static void free_skb_pool(rtl8150_t *); @@ -516,7 +516,7 @@ netif_wake_queue(dev->netdev); } -void intr_callback(struct urb *urb, struct pt_regs *regs) +static void intr_callback(struct urb *urb, struct pt_regs *regs) { rtl8150_t *dev; __u8 *d; @@ -977,13 +977,13 @@ } } -int __init usb_rtl8150_init(void) +static int __init usb_rtl8150_init(void) { info(DRIVER_DESC " " DRIVER_VERSION); return usb_register(&rtl8150_driver); } -void __exit usb_rtl8150_exit(void) +static void __exit usb_rtl8150_exit(void) { usb_deregister(&rtl8150_driver); } diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c --- a/drivers/usb/net/usbnet.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/net/usbnet.c Wed May 12 20:33:39 2004 @@ -606,7 +606,7 @@ ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf); } -void ax8817x_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) +static void ax8817x_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) { struct usbnet *dev = (struct usbnet *)net->priv; u8 opt; @@ -626,7 +626,7 @@ } } -int ax8817x_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) +static int ax8817x_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) { struct usbnet *dev = (struct usbnet *)net->priv; u8 opt = 0; @@ -646,8 +646,8 @@ return 0; } -int ax8817x_get_eeprom(struct net_device *net, - struct ethtool_eeprom *eeprom, u8 *data) +static int ax8817x_get_eeprom(struct net_device *net, + struct ethtool_eeprom *eeprom, u8 *data) { struct usbnet *dev = (struct usbnet *)net->priv; u16 *ebuf = (u16 *)data; @@ -928,8 +928,8 @@ */ static int generic_cdc_bind (struct usbnet *dev, struct usb_interface *intf) { - u8 *buf = intf->altsetting->extra; - int len = intf->altsetting->extralen; + u8 *buf = intf->cur_altsetting->extra; + int len = intf->cur_altsetting->extralen; struct usb_interface_descriptor *d; struct cdc_state *info = (void *) &dev->data; int status; @@ -955,7 +955,7 @@ /* this assumes that if there's a non-RNDIS vendor variant * of cdc-acm, it'll fail RNDIS requests cleanly. */ - rndis = (intf->altsetting->desc.bInterfaceProtocol == 0xff); + rndis = (intf->cur_altsetting->desc.bInterfaceProtocol == 0xff); memset (info, 0, sizeof *info); info->control = intf; @@ -1025,7 +1025,7 @@ } /* a data interface altsetting does the real i/o */ - d = &info->data->altsetting->desc; + d = &info->data->cur_altsetting->desc; if (d->bInterfaceClass != USB_CLASS_CDC_DATA) { dev_dbg (&intf->dev, "slave class %u\n", d->bInterfaceClass); @@ -3014,7 +3014,7 @@ // precondition: never called in_interrupt -int +static int usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) { struct usbnet *dev; @@ -3204,6 +3204,10 @@ // Hawking UF200, TrendNet TU2-ET100 USB_DEVICE (0x07b8, 0x420a), .driver_info = (unsigned long) &hawking_uf200_info, +}, { + // Billionton Systems, USB2AR + USB_DEVICE (0x08dd, 0x90ff), + .driver_info = (unsigned long) &ax8817x_info, }, { // ATEN UC210T USB_DEVICE (0x0557, 0x2009), diff -Nru a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c --- a/drivers/usb/serial/io_ti.c Wed May 12 20:33:40 2004 +++ b/drivers/usb/serial/io_ti.c Wed May 12 20:33:40 2004 @@ -279,8 +279,8 @@ * @address_type: Can read both XDATA and I2C * @buffer: pointer to input data buffer */ -int TIReadDownloadMemory (struct usb_device *dev, int start_address, int length, - __u8 address_type, __u8 *buffer) +static int TIReadDownloadMemory(struct usb_device *dev, int start_address, + int length, __u8 address_type, __u8 *buffer) { int status = 0; __u8 read_length; @@ -328,7 +328,7 @@ return status; } -int TIReadRam (struct usb_device *dev, int start_address, int length, __u8 *buffer) +static int TIReadRam (struct usb_device *dev, int start_address, int length, __u8 *buffer) { return TIReadDownloadMemory (dev, start_address, @@ -612,7 +612,7 @@ return 0; } -int TIReadRom (struct edgeport_serial *serial, int start_address, int length, __u8 *buffer) +static int TIReadRom (struct edgeport_serial *serial, int start_address, int length, __u8 *buffer) { int status; @@ -632,7 +632,7 @@ return status; } -int TIWriteRom (struct edgeport_serial *serial, int start_address, int length, __u8 *buffer) +static int TIWriteRom (struct edgeport_serial *serial, int start_address, int length, __u8 *buffer) { if (serial->product_info.TiMode == TI_MODE_BOOT) return TIWriteBootMemory (serial, @@ -995,7 +995,7 @@ if (status) return status; - interface = &serial->serial->dev->config->interface[0]->altsetting->desc; + interface = &serial->serial->interface->cur_altsetting->desc; if (!interface) { dev_err (&serial->serial->dev->dev, "%s - no interface set, error!", __FUNCTION__); return -ENODEV; diff -Nru a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c --- a/drivers/usb/serial/ipaq.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/serial/ipaq.c Wed May 12 20:33:39 2004 @@ -150,7 +150,7 @@ /* All of the device info needed for the Compaq iPAQ */ -struct usb_serial_device_type ipaq_device = { +static struct usb_serial_device_type ipaq_device = { .owner = THIS_MODULE, .name = "PocketPC PDA", .id_table = ipaq_id_table, diff -Nru a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c --- a/drivers/usb/serial/ir-usb.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/serial/ir-usb.c Wed May 12 20:33:39 2004 @@ -138,7 +138,7 @@ }; -struct usb_serial_device_type ir_device = { +static struct usb_serial_device_type ir_device = { .owner = THIS_MODULE, .name = "IR Dongle", .id_table = id_table, diff -Nru a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c --- a/drivers/usb/serial/keyspan.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/serial/keyspan.c Wed May 12 20:33:39 2004 @@ -133,9 +133,6 @@ /* Per device and per port private data */ struct keyspan_serial_private { - /* number of active ports */ - atomic_t active_count; - const struct keyspan_device_details *device_details; struct urb *instat_urb; diff -Nru a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c --- a/drivers/usb/serial/kobil_sct.c Wed May 12 20:33:40 2004 +++ b/drivers/usb/serial/kobil_sct.c Wed May 12 20:33:40 2004 @@ -110,7 +110,7 @@ }; -struct usb_serial_device_type kobil_device = { +static struct usb_serial_device_type kobil_device = { .owner = THIS_MODULE, .name = "KOBIL USB smart card terminal", .id_table = id_table, @@ -183,7 +183,7 @@ pdev = serial->dev; actconfig = pdev->actconfig; interface = actconfig->interface[0]; - altsetting = interface->altsetting; + altsetting = interface->cur_altsetting; endpoint = altsetting->endpoint; for (i = 0; i < altsetting->desc.bNumEndpoints; i++) { diff -Nru a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c --- a/drivers/usb/serial/pl2303.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/serial/pl2303.c Wed May 12 20:33:39 2004 @@ -80,6 +80,7 @@ { USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) }, { USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) }, { USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) }, + { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) }, { } /* Terminating entry */ }; diff -Nru a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h --- a/drivers/usb/serial/pl2303.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/serial/pl2303.h Wed May 12 20:33:39 2004 @@ -41,3 +41,6 @@ #define SITECOM_VENDOR_ID 0x6189 #define SITECOM_PRODUCT_ID 0x2068 + +#define ALCATEL_VENDOR_ID 0x11f7 +#define ALCATEL_PRODUCT_ID 0x02df diff -Nru a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c --- a/drivers/usb/serial/safe_serial.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/serial/safe_serial.c Wed May 12 20:33:39 2004 @@ -395,7 +395,7 @@ static int safe_startup (struct usb_serial *serial) { - switch (serial->interface->altsetting->desc.bInterfaceProtocol) { + switch (serial->interface->cur_altsetting->desc.bInterfaceProtocol) { case LINEO_SAFESERIAL_CRC: break; case LINEO_SAFESERIAL_CRC_PADDED: diff -Nru a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c --- a/drivers/usb/serial/usb-serial.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/serial/usb-serial.c Wed May 12 20:33:39 2004 @@ -1037,7 +1037,7 @@ (dev->descriptor.idProduct == ATEN_PRODUCT_ID))) { if (interface != dev->actconfig->interface[0]) { /* check out the endpoints of the other interface*/ - iface_desc = &dev->actconfig->interface[0]->altsetting[0]; + iface_desc = dev->actconfig->interface[0]->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; if ((endpoint->bEndpointAddress & 0x80) && diff -Nru a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c --- a/drivers/usb/serial/visor.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/serial/visor.c Wed May 12 20:33:39 2004 @@ -222,6 +222,8 @@ .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE31_ID), + .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID), @@ -273,6 +275,7 @@ { USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID) }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE31_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) }, diff -Nru a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h --- a/drivers/usb/serial/visor.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/serial/visor.h Wed May 12 20:33:39 2004 @@ -31,6 +31,7 @@ #define PALM_M130_ID 0x0050 #define PALM_TUNGSTEN_T_ID 0x0060 #define PALM_TUNGSTEN_Z_ID 0x0031 +#define PALM_ZIRE31_ID 0x0061 #define PALM_ZIRE_ID 0x0070 #define PALM_M100_ID 0x0080 diff -Nru a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c --- a/drivers/usb/storage/datafab.c Wed May 12 20:33:40 2004 +++ b/drivers/usb/storage/datafab.c Wed May 12 20:33:40 2004 @@ -489,7 +489,7 @@ return USB_STOR_TRANSPORT_GOOD; } -void datafab_info_destructor(void *extra) +static void datafab_info_destructor(void *extra) { // this routine is a placeholder... // currently, we don't allocate any extra memory so we're okay diff -Nru a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c --- a/drivers/usb/storage/isd200.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/storage/isd200.c Wed May 12 20:33:39 2004 @@ -349,7 +349,7 @@ * RETURNS: * void */ -void isd200_build_sense(struct us_data *us, Scsi_Cmnd *srb) +static void isd200_build_sense(struct us_data *us, Scsi_Cmnd *srb) { struct isd200_info *info = (struct isd200_info *)us->extra; struct sense_data *buf = (struct sense_data *) &srb->sense_buffer[0]; @@ -505,7 +505,7 @@ * RETURNS: * ISD status code */ -int isd200_read_regs( struct us_data *us ) +static int isd200_read_regs( struct us_data *us ) { struct isd200_info *info = (struct isd200_info *)us->extra; int retStatus = ISD200_GOOD; @@ -534,7 +534,7 @@ * This is used by the protocol layers to actually send the message to * the device and receive the response. */ -void isd200_invoke_transport( struct us_data *us, +static void isd200_invoke_transport( struct us_data *us, Scsi_Cmnd *srb, union ata_cdb *ataCdb ) { @@ -677,7 +677,7 @@ * RETURNS: * ISD status code */ -int isd200_write_config( struct us_data *us ) +static int isd200_write_config( struct us_data *us ) { struct isd200_info *info = (struct isd200_info *)us->extra; int retStatus = ISD200_GOOD; @@ -720,7 +720,7 @@ * RETURNS: * ISD status code */ -int isd200_read_config( struct us_data *us ) +static int isd200_read_config( struct us_data *us ) { struct isd200_info *info = (struct isd200_info *)us->extra; int retStatus = ISD200_GOOD; @@ -765,7 +765,7 @@ * RETURNS: * NT status code */ -int isd200_atapi_soft_reset( struct us_data *us ) +static int isd200_atapi_soft_reset( struct us_data *us ) { int retStatus = ISD200_GOOD; int transferStatus; @@ -791,7 +791,7 @@ * RETURNS: * ISD status code */ -int isd200_srst( struct us_data *us ) +static int isd200_srst( struct us_data *us ) { int retStatus = ISD200_GOOD; int transferStatus; @@ -937,7 +937,7 @@ * RETURNS: * ISD status code */ -int isd200_manual_enum(struct us_data *us) +static int isd200_manual_enum(struct us_data *us) { struct isd200_info *info = (struct isd200_info *)us->extra; int retStatus = ISD200_GOOD; @@ -981,7 +981,7 @@ * RETURNS: * ISD status code */ -int isd200_get_inquiry_data( struct us_data *us ) +static int isd200_get_inquiry_data( struct us_data *us ) { struct isd200_info *info = (struct isd200_info *)us->extra; int retStatus = ISD200_GOOD; @@ -1124,8 +1124,8 @@ * TRUE if the command needs to be sent to the transport layer * FALSE otherwise */ -int isd200_scsi_to_ata(Scsi_Cmnd *srb, struct us_data *us, - union ata_cdb * ataCdb) +static int isd200_scsi_to_ata(Scsi_Cmnd *srb, struct us_data *us, + union ata_cdb * ataCdb) { struct isd200_info *info = (struct isd200_info *)us->extra; struct hd_driveid *id = info->id; @@ -1339,7 +1339,7 @@ * * Frees the driver structure. */ -void isd200_free_info_ptrs(void *info_) +static void isd200_free_info_ptrs(void *info_) { struct isd200_info *info = (struct isd200_info *) info_; @@ -1357,7 +1357,7 @@ * RETURNS: * ISD status code */ -int isd200_init_info(struct us_data *us) +static int isd200_init_info(struct us_data *us) { int retStatus = ISD200_GOOD; struct isd200_info *info; diff -Nru a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c --- a/drivers/usb/storage/jumpshot.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/storage/jumpshot.c Wed May 12 20:33:39 2004 @@ -416,7 +416,7 @@ } -void jumpshot_info_destructor(void *extra) +static void jumpshot_info_destructor(void *extra) { // this routine is a placeholder... // currently, we don't allocate any extra blocks so we're okay diff -Nru a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c --- a/drivers/usb/storage/scsiglue.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/storage/scsiglue.c Wed May 12 20:33:39 2004 @@ -159,14 +159,18 @@ return FAILED; } - /* Set state to ABORTING, set the ABORTING bit, and release the lock */ + /* Set state to ABORTING and set the ABORTING bit, but only if + * a device reset isn't already in progress (to avoid interfering + * with the reset). To prevent races with auto-reset, we must + * stop any ongoing USB transfers while still holding the host + * lock. */ us->sm_state = US_STATE_ABORTING; - set_bit(US_FLIDX_ABORTING, &us->flags); + if (!test_bit(US_FLIDX_RESETTING, &us->flags)) { + set_bit(US_FLIDX_ABORTING, &us->flags); + usb_stor_stop_transport(us); + } scsi_unlock(host); - /* Stop an ongoing USB transfer */ - usb_stor_stop_transport(us); - /* Wait for the aborted command to finish */ wait_for_completion(&us->notify); @@ -254,18 +258,17 @@ } /* Report a driver-initiated device reset to the SCSI layer. - * Calling this for a SCSI-initiated reset is unnecessary but harmless. */ + * Calling this for a SCSI-initiated reset is unnecessary but harmless. + * The caller must own the SCSI host lock. */ void usb_stor_report_device_reset(struct us_data *us) { int i; - scsi_lock(us->host); scsi_report_device_reset(us->host, 0, 0); if (us->flags & US_FL_SCM_MULT_TARG) { for (i = 1; i < us->host->max_id; ++i) scsi_report_device_reset(us->host, 0, i); } - scsi_unlock(us->host); } /*********************************************************************** diff -Nru a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c --- a/drivers/usb/storage/shuttle_usbat.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/storage/shuttle_usbat.c Wed May 12 20:33:39 2004 @@ -55,11 +55,11 @@ int transferred = 0; -int usbat_read(struct us_data *us, - unsigned char access, - unsigned char reg, - unsigned char *content) { - +static int usbat_read(struct us_data *us, + unsigned char access, + unsigned char reg, + unsigned char *content) +{ int result; result = usb_stor_ctrl_transfer(us, @@ -74,11 +74,11 @@ return result; } -int usbat_write(struct us_data *us, - unsigned char access, - unsigned char reg, - unsigned char content) { - +static int usbat_write(struct us_data *us, + unsigned char access, + unsigned char reg, + unsigned char content) +{ int result; result = usb_stor_ctrl_transfer(us, @@ -93,14 +93,14 @@ return result; } -int usbat_set_shuttle_features(struct us_data *us, - unsigned char external_trigger, - unsigned char epp_control, - unsigned char mask_byte, - unsigned char test_pattern, - unsigned char subcountH, - unsigned char subcountL) { - +static int usbat_set_shuttle_features(struct us_data *us, + unsigned char external_trigger, + unsigned char epp_control, + unsigned char mask_byte, + unsigned char test_pattern, + unsigned char subcountH, + unsigned char subcountL) +{ int result; unsigned char *command = us->iobuf; @@ -125,13 +125,13 @@ return result; } -int usbat_read_block(struct us_data *us, - unsigned char access, - unsigned char reg, - unsigned char *content, - unsigned short len, - int use_sg) { - +static int usbat_read_block(struct us_data *us, + unsigned char access, + unsigned char reg, + unsigned char *content, + unsigned short len, + int use_sg) +{ int result; unsigned char *command = us->iobuf; @@ -171,8 +171,8 @@ * an error condition. */ -int usbat_wait_not_busy(struct us_data *us, int minutes) { - +static int usbat_wait_not_busy(struct us_data *us, int minutes) +{ int i; int result; unsigned char *status = us->iobuf; @@ -216,14 +216,13 @@ return USB_STOR_TRANSPORT_FAILED; } -int usbat_write_block(struct us_data *us, - unsigned char access, - unsigned char reg, - unsigned char *content, - unsigned short len, - int use_sg, - int minutes) { - +static int usbat_write_block(struct us_data *us, + unsigned char access, + unsigned char reg, + unsigned char *content, + unsigned short len, + int use_sg, int minutes) +{ int result; unsigned char *command = us->iobuf; @@ -260,21 +259,21 @@ return usbat_wait_not_busy(us, minutes); } -int usbat_rw_block_test(struct us_data *us, - unsigned char access, - unsigned char *registers, - unsigned char *data_out, - unsigned short num_registers, - unsigned char data_reg, - unsigned char status_reg, - unsigned char timeout, - unsigned char qualifier, - int direction, - unsigned char *content, - unsigned short len, - int use_sg, - int minutes) { - +static int usbat_rw_block_test(struct us_data *us, + unsigned char access, + unsigned char *registers, + unsigned char *data_out, + unsigned short num_registers, + unsigned char data_reg, + unsigned char status_reg, + unsigned char timeout, + unsigned char qualifier, + int direction, + unsigned char *content, + unsigned short len, + int use_sg, + int minutes) +{ int result; unsigned int pipe = (direction == SCSI_DATA_READ) ? us->recv_bulk_pipe : us->send_bulk_pipe; @@ -431,12 +430,12 @@ * transfers of data! */ -int usbat_multiple_write(struct us_data *us, - unsigned char access, - unsigned char *registers, - unsigned char *data_out, - unsigned short num_registers) { - +static int usbat_multiple_write(struct us_data *us, + unsigned char access, + unsigned char *registers, + unsigned char *data_out, + unsigned short num_registers) +{ int result; unsigned char *data = us->iobuf; int i; @@ -479,9 +478,8 @@ return usbat_wait_not_busy(us, 0); } -int usbat_read_user_io(struct us_data *us, - unsigned char *data_flags) { - +static int usbat_read_user_io(struct us_data *us, unsigned char *data_flags) +{ int result; result = usb_stor_ctrl_transfer(us, @@ -496,10 +494,10 @@ return result; } -int usbat_write_user_io(struct us_data *us, - unsigned char enable_flags, - unsigned char data_flags) { - +static int usbat_write_user_io(struct us_data *us, + unsigned char enable_flags, + unsigned char data_flags) +{ int result; result = usb_stor_ctrl_transfer(us, @@ -519,11 +517,11 @@ * a little ( <= 65535 byte) ATAPI pipe */ -int usbat_handle_read10(struct us_data *us, - unsigned char *registers, - unsigned char *data, - Scsi_Cmnd *srb) { - +static int usbat_handle_read10(struct us_data *us, + unsigned char *registers, + unsigned char *data, + Scsi_Cmnd *srb) +{ int result = USB_STOR_TRANSPORT_GOOD; unsigned char *buffer; unsigned int len; @@ -630,8 +628,8 @@ return result; } -static int hp_8200e_select_and_test_registers(struct us_data *us) { - +static int hp_8200e_select_and_test_registers(struct us_data *us) +{ int selector; unsigned char *status = us->iobuf; @@ -679,8 +677,8 @@ return USB_STOR_TRANSPORT_GOOD; } -int init_8200e(struct us_data *us) { - +int init_8200e(struct us_data *us) +{ int result; unsigned char *status = us->iobuf; diff -Nru a/drivers/usb/storage/shuttle_usbat.h b/drivers/usb/storage/shuttle_usbat.h --- a/drivers/usb/storage/shuttle_usbat.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/storage/shuttle_usbat.h Wed May 12 20:33:39 2004 @@ -52,27 +52,7 @@ #define USBAT_UIO_OE0 0x10 // I/O 0 set=output/clr=input #define USBAT_UIO_ADPRST 0x01 // Reset SCM chip -/* USBAT-specific commands */ - -extern int usbat_read(struct us_data *us, unsigned char access, - unsigned char reg, unsigned char *content); -extern int usbat_write(struct us_data *us, unsigned char access, - unsigned char reg, unsigned char content); -extern int usbat_read_block(struct us_data *us, unsigned char access, - unsigned char reg, unsigned char *content, unsigned short len, - int use_sg); -extern int usbat_write_block(struct us_data *us, unsigned char access, - unsigned char reg, unsigned char *content, unsigned short len, - int use_sg, int minutes); -extern int usbat_multiple_write(struct us_data *us, unsigned char access, - unsigned char *registers, unsigned char *data_out, - unsigned short num_registers); -extern int usbat_read_user_io(struct us_data *us, unsigned char *data_flags); -extern int usbat_write_user_io(struct us_data *us, - unsigned char enable_flags, unsigned char data_flags); - /* HP 8200e stuff */ - extern int hp8200e_transport(Scsi_Cmnd *srb, struct us_data *us); extern int init_8200e(struct us_data *us); diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c --- a/drivers/usb/storage/transport.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/storage/transport.c Wed May 12 20:33:39 2004 @@ -137,7 +137,7 @@ int status; /* don't submit URBs during abort/disconnect processing */ - if (us->flags & DONT_SUBMIT) + if (us->flags & ABORTING_OR_DISCONNECTING) return -EIO; /* set up data structures for the wakeup system */ @@ -172,7 +172,7 @@ set_bit(US_FLIDX_URB_ACTIVE, &us->flags); /* did an abort/disconnect occur during the submission? */ - if (us->flags & DONT_SUBMIT) { + if (us->flags & ABORTING_OR_DISCONNECTING) { /* cancel the URB, if it hasn't been cancelled already */ if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) { @@ -440,7 +440,7 @@ int result; /* don't submit s-g requests during abort/disconnect processing */ - if (us->flags & DONT_SUBMIT) + if (us->flags & ABORTING_OR_DISCONNECTING) return USB_STOR_XFER_ERROR; /* initialize the scatter-gather request block */ @@ -458,7 +458,7 @@ set_bit(US_FLIDX_SG_ACTIVE, &us->flags); /* did an abort/disconnect occur during the submission? */ - if (us->flags & DONT_SUBMIT) { + if (us->flags & ABORTING_OR_DISCONNECTING) { /* cancel the request, if it hasn't been cancelled already */ if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->flags)) { @@ -712,14 +712,10 @@ /* abort processing: the bulk-only transport requires a reset * following an abort */ - Handle_Abort: + Handle_Abort: srb->result = DID_ABORT << 16; - if (us->protocol == US_PR_BULK) { - - /* permit the reset transfer to take place */ - clear_bit(US_FLIDX_ABORTING, &us->flags); + if (us->protocol == US_PR_BULK) us->transport_reset(us); - } } /* Stop the current URB transfer */ @@ -912,6 +908,17 @@ USB_RECIP_INTERFACE, 0, us->ifnum, us->iobuf, 1, HZ); + /* + * Some devices (i.e. Iomega Zip100) need this -- apparently + * the bulk pipes get STALLed when the GetMaxLUN request is + * processed. This is, in theory, harmless to all other devices + * (regardless of if they stall or not). + */ + if (result < 0) { + usb_stor_clear_halt(us, us->recv_bulk_pipe); + usb_stor_clear_halt(us, us->send_bulk_pipe); + } + US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", result, us->iobuf[0]); @@ -1079,20 +1086,28 @@ { int result; int result2; + int rc = FAILED; - /* Let the SCSI layer know we are doing a reset */ + /* Let the SCSI layer know we are doing a reset, set the + * RESETTING bit, and clear the ABORTING bit so that the reset + * may proceed. + */ + scsi_lock(us->host); usb_stor_report_device_reset(us); + set_bit(US_FLIDX_RESETTING, &us->flags); + clear_bit(US_FLIDX_ABORTING, &us->flags); + scsi_unlock(us->host); /* A 20-second timeout may seem rather long, but a LaCie - * StudioDrive USB2 device takes 16+ seconds to get going - * following a powerup or USB attach event. */ - + * StudioDrive USB2 device takes 16+ seconds to get going + * following a powerup or USB attach event. + */ result = usb_stor_control_msg(us, us->send_ctrl_pipe, request, requesttype, value, index, data, size, 20*HZ); if (result < 0) { US_DEBUGP("Soft reset failed: %d\n", result); - return FAILED; + goto Done; } /* long wait for reset, so unlock to allow disconnects */ @@ -1102,12 +1117,9 @@ down(&us->dev_semaphore); if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { US_DEBUGP("Reset interrupted by disconnect\n"); - return FAILED; + goto Done; } - /* permit the clear-halt transfers to take place */ - clear_bit(US_FLIDX_ABORTING, &us->flags); - US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n"); result = usb_stor_clear_halt(us, us->recv_bulk_pipe); @@ -1117,10 +1129,14 @@ /* return a result code based on the result of the control message */ if (result < 0 || result2 < 0) { US_DEBUGP("Soft reset failed\n"); - return FAILED; + goto Done; } US_DEBUGP("Soft reset done\n"); - return SUCCESS; + rc = SUCCESS; + + Done: + clear_bit(US_FLIDX_RESETTING, &us->flags); + return rc; } /* This issues a CB[I] Reset to the device in question diff -Nru a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h --- a/drivers/usb/storage/unusual_devs.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/storage/unusual_devs.h Wed May 12 20:33:39 2004 @@ -90,6 +90,12 @@ US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ), #endif +/* Patch submitted by Alessandro Fracchetti */ +UNUSUAL_DEV( 0x0482, 0x0105, 0x0100, 0x0100, + "Kyocera", + "Finecam L3", + US_SC_SCSI, US_PR_BULK, NULL, US_FL_FIX_INQUIRY), + /* Patch submitted by Philipp Friedrich */ UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100, "Kyocera", @@ -297,6 +303,13 @@ US_SC_SCSI, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN | US_FL_MODE_XLATE), +/* Submitted by Rajesh Kumble Nayak */ +UNUSUAL_DEV( 0x054c, 0x002e, 0x0500, 0x0500, + "Sony", + "Handycam HC-85", + US_SC_UFI, US_PR_DEVICE, NULL, + US_FL_SINGLE_LUN | US_FL_MODE_XLATE), + UNUSUAL_DEV( 0x054c, 0x0032, 0x0000, 0x9999, "Sony", "Memorystick MSC-U01N", @@ -322,6 +335,13 @@ "PEG Mass Storage", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), + +/* Submitted by Mike Alborn */ +UNUSUAL_DEV( 0x054c, 0x016a, 0x0000, 0x9999, + "Sony", + "PEG Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), UNUSUAL_DEV( 0x057b, 0x0000, 0x0000, 0x0299, "Y-E Data", @@ -438,22 +458,6 @@ US_FL_SINGLE_LUN ), #endif -/* Following three Minolta cameras reported by Martin Pool - * . Originally discovered by Kedar Petankar, - * Matthew Geier, Mikael Lofj"ard, Marcel de Boer. - */ -UNUSUAL_DEV( 0x0686, 0x4006, 0x0001, 0x0001, - "Minolta", - "DiMAGE 7", - US_SC_SCSI, US_PR_DEVICE, NULL, - 0 ), - -UNUSUAL_DEV( 0x0686, 0x400f, 0x0001, 0x0001, - "Minolta", - "DiMAGE 7Hi", - US_SC_SCSI, US_PR_DEVICE, NULL, - 0 ), - /* Submitted by Benny Sjostrand */ UNUSUAL_DEV( 0x0686, 0x4011, 0x0001, 0x0001, "Minolta", @@ -629,7 +633,7 @@ "Casio", "QV DigitalCamera", US_SC_DEVICE, US_PR_CB, NULL, - US_FL_FIX_INQUIRY ), + US_FL_NEED_OVERRIDE | US_FL_FIX_INQUIRY ), /* Later Casio cameras apparently tell the truth */ UNUSUAL_DEV( 0x07cf, 0x1001, 0x9010, 0x9999, @@ -688,7 +692,7 @@ UNUSUAL_DEV( 0x0a16, 0x8888, 0x0100, 0x0100, "IBM", "IBM USB Memory Key", - US_SC_SCSI, US_PR_BULK, NULL, + US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), /* This Pentax still camera is not conformant diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c --- a/drivers/usb/storage/usb.c Wed May 12 20:33:39 2004 +++ b/drivers/usb/storage/usb.c Wed May 12 20:33:39 2004 @@ -84,8 +84,6 @@ #include -#include -#include #include #include @@ -490,7 +488,7 @@ if (unusual_dev->useTransport != US_PR_DEVICE && us->protocol == idesc->bInterfaceProtocol) msg += 2; - if (msg >= 0) + if (msg >= 0 && !(unusual_dev->flags & US_FL_NEED_OVERRIDE)) printk(KERN_NOTICE USB_STORAGE "This device " "(%04x,%04x,%04x S %02x P %02x)" " has %s in unusual_devs.h\n" diff -Nru a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h --- a/drivers/usb/storage/usb.h Wed May 12 20:33:39 2004 +++ b/drivers/usb/storage/usb.h Wed May 12 20:33:39 2004 @@ -69,6 +69,7 @@ /* Flag definitions: these entries are static */ #define US_FL_SINGLE_LUN 0x00000001 /* allow access to only LUN 0 */ #define US_FL_MODE_XLATE 0 /* [no longer used] */ +#define US_FL_NEED_OVERRIDE 0x00000004 /* unusual_devs entry is necessary */ #define US_FL_IGNORE_SER 0 /* [no longer used] */ #define US_FL_SCM_MULT_TARG 0x00000020 /* supports multiple targets */ #define US_FL_FIX_INQUIRY 0x00000040 /* INQUIRY response needs faking */ @@ -79,8 +80,9 @@ #define US_FLIDX_SG_ACTIVE 19 /* 0x00080000 current_sg is in use */ #define US_FLIDX_ABORTING 20 /* 0x00100000 abort is in progress */ #define US_FLIDX_DISCONNECTING 21 /* 0x00200000 disconnect in progress */ -#define DONT_SUBMIT ((1UL << US_FLIDX_ABORTING) | \ - (1UL << US_FLIDX_DISCONNECTING)) +#define ABORTING_OR_DISCONNECTING ((1UL << US_FLIDX_ABORTING) | \ + (1UL << US_FLIDX_DISCONNECTING)) +#define US_FLIDX_RESETTING 22 /* 0x00400000 device reset in progress */ /* processing state machine states */ diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h Wed May 12 20:33:39 2004 +++ b/include/linux/usb.h Wed May 12 20:33:39 2004 @@ -14,6 +14,7 @@ #include /* for mdelay() */ #include /* for in_interrupt() */ #include /* for struct list_head */ +#include /* for struct kref */ #include /* for struct device */ #include /* for struct file_operations */ #include /* for struct completion */ @@ -147,11 +148,42 @@ #define USB_MAXINTERFACES 32 /** + * struct usb_interface_cache - long-term representation of a device interface + * @num_altsetting: number of altsettings defined. + * @ref: reference counter. + * @altsetting: variable-length array of interface structures, one for + * each alternate setting that may be selected. Each one includes a + * set of endpoint configurations. They will be in no particular order. + * + * These structures persist for the lifetime of a usb_device, unlike + * struct usb_interface (which persists only as long as its configuration + * is installed). The altsetting arrays can be accessed through these + * structures at any time, permitting comparison of configurations and + * providing support for the /proc/bus/usb/devices pseudo-file. + */ +struct usb_interface_cache { + unsigned num_altsetting; /* number of alternate settings */ + struct kref ref; /* reference counter */ + + /* variable-length array of alternate settings for this interface, + * stored in no particular order */ + struct usb_host_interface altsetting[0]; +}; +#define ref_to_usb_interface_cache(r) \ + container_of(r, struct usb_interface_cache, ref) +#define altsetting_to_usb_interface_cache(a) \ + container_of(a, struct usb_interface_cache, altsetting[0]) + +/** * struct usb_host_config - representation of a device's configuration * @desc: the device's configuration descriptor. - * @interface: array of usb_interface structures, one for each interface - * in the configuration. The number of interfaces is stored in - * desc.bNumInterfaces. + * @interface: array of pointers to usb_interface structures, one for each + * interface in the configuration. The number of interfaces is stored + * in desc.bNumInterfaces. These pointers are valid only while the + * the configuration is active. + * @intf_cache: array of pointers to usb_interface_cache structures, one + * for each interface in the configuration. These structures exist + * for the entire life of the device. * @extra: pointer to buffer containing all extra descriptors associated * with this configuration (those preceding the first interface * descriptor). @@ -185,6 +217,10 @@ * stored in no particular order */ struct usb_interface *interface[USB_MAXINTERFACES]; + /* Interface information available even when this is not the + * active configuration */ + struct usb_interface_cache *intf_cache[USB_MAXINTERFACES]; + unsigned char *extra; /* Extra descriptors */ int extralen; }; @@ -676,7 +712,7 @@ * URB_NO_SETUP_DMA_MAP indicate which buffers have already been mapped. * URB_NO_SETUP_DMA_MAP is ignored for non-control URBs. * - * Interrupt UBS must provide an interval, saying how often (in milliseconds + * Interrupt URBs must provide an interval, saying how often (in milliseconds * or, for highspeed devices, 125 microsecond units) * to poll for transfers. After the URB has been submitted, the interval * field reflects how the transfer was actually scheduled. @@ -731,8 +767,8 @@ struct urb { /* private, usb core and host controller only fields in the urb */ + struct kref kref; /* reference count of the URB */ spinlock_t lock; /* lock for the URB */ - atomic_t count; /* reference count of the URB */ void *hcpriv; /* private data for host controller */ struct list_head urb_list; /* list pointer to all active urbs */ int bandwidth; /* bandwidth for INT/ISO request */ diff -Nru a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h --- a/include/linux/usbdevice_fs.h Wed May 12 20:33:39 2004 +++ b/include/linux/usbdevice_fs.h Wed May 12 20:33:39 2004 @@ -154,7 +154,6 @@ struct dev_state { struct list_head list; /* state list */ - struct rw_semaphore devsem; /* protects modifications to dev (dev == NULL indicating disconnect) */ struct usb_device *dev; struct file *file; spinlock_t lock; /* protects the async urb lists */