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 <adridg@cs.kun.nl>
# 
# 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 <adridg@cs.kun.nl>
# 
# 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 <adridg@cs.kun.nl>
# 
# 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
#   <http://www.phidgets.com/>, 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 <linux-usb-devel@lists.sourceforge.net>
#   
#   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; i<dev->descriptor.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 <linux/usb.h>
 #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,
  *	<sojkam@centrum.cz>, 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, &regs->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, &regs->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 <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#if !defined(DEBUG) && defined(CONFIG_USB_DEBUG)
+#define DEBUG
+#endif
+#include <linux/usb.h>
+
+
+#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 <daniel.ritz@gmx.ch>"
+#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 <msdemlei@tucana.harvard.edu>"
 #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 <http://www.phidgets.com/>.
+
+	  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 <davidgsf@sourceforge.net> 
- *               2001 Juergen Stuber <stuber@loria.fr>
+ * Copyright (C) 2003 David Glance <davidgsf@sourceforge.net>
+ *               2001-2004 Juergen Stuber <starblue@users.sourceforge.net>
  *
  *	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 <starblue@users.sourceforge.net>
+ *   - 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 <starblue@users.sourceforge.net>
+ *   - timeout read now only after inactivity, shorten default accordingly
+ * 2004-03-11 - 0.8 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - log major, minor instead of possibly confusing device filename
+ *   - whitespace cleanup
+ * 2004-03-12 - 0.9 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - normalize whitespace in debug messages
+ *   - take care about endianness in control message responses
+ * 2004-03-13 - 0.91 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - make default intervals longer to accommodate current EHCI driver
+ * 2004-03-19 - 0.92 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - replaced atomic_t by memory barriers
+ * 2004-04-21 - 0.93 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - 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 <starblue@users.sourceforge.net>
+ *   - make device locking interruptible
+ * 2004-04-30 - 0.95 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - check for valid udev on resubmitting and unlinking urbs
  */
 
 #include <linux/config.h>
@@ -53,33 +83,113 @@
 #include <linux/completion.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
+#include <linux/poll.h>
 
 
 #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 <starblue@sourceforge.net>"
 #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; i<dev->read_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; i<dev->read_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 <sean@mess.org>
+ *
+ * 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 <linux/config.h>
+#ifdef CONFIG_USB_DEBUG
+#define DEBUG	1
+#endif
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
+#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", &degrees, &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 <linux/usb.h>
 #include <linux/smp_lock.h>
 #include <linux/devfs_fs_kernel.h>
+#include <linux/device.h>
 
 #include <linux/ticable.h>
 #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 <al.fracchetti@tin.it> */
+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 <philipp@void.at> */
 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 <nayak@obs-nice.fr> */
+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 <malborn@deandra.homeip.net> */
+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
- * <mbp@sourcefrog.net>.  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 <benny@hostmobility.com> */
 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 <linux/module.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 
@@ -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 <linux/delay.h>	/* for mdelay() */
 #include <linux/interrupt.h>	/* for in_interrupt() */
 #include <linux/list.h>		/* for struct list_head */
+#include <linux/kref.h>		/* for struct kref */
 #include <linux/device.h>	/* for struct device */
 #include <linux/fs.h>		/* for struct file_operations */
 #include <linux/completion.h>	/* 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 */
