bk://kernel.bkbits.net/gregkh/linux/usb-2.6 herbert@gondor.apana.org.au|ChangeSet|20040505213249|27873 herbert # This is a BitKeeper generated diff -Nru style patch. # # ChangeSet # 2004/05/05 14:52:58-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/core/message.c # 2004/05/05 14:52:55-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/05/05 14:52:11-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # CREDITS # 2004/05/05 14:52:08-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/05/05 14:32:49-07:00 herbert@gondor.apana.org.au # [PATCH] USB Storage: Sony Clie # # I've received the following report which indicates that the Sony Clie needs # the US_FL_FIX_INQUIRY flag set. # # http://bugs.debian.org/243650 # # drivers/usb/storage/unusual_devs.h # 2004/04/16 17:47:39-07:00 herbert@gondor.apana.org.au +7 -0 # USB Storage: Sony Clie # # ChangeSet # 2004/05/05 14:10:42-07:00 stern@rowland.harvard.edu # [PATCH] PATCH: (as268) Import device-reset changes from gadget-2.6 tree # # This patch imports the changes that David Brownell has made to the # device-reset functions in his gadget-2.6 tree. Once these ongoing # troubling questions about locking are settled, I'll add support for the # "descriptors changed" case. # # drivers/usb/core/hub.c # 2004/05/03 08:02:43-07:00 stern@rowland.harvard.edu +99 -97 # PATCH: (as268) Import device-reset changes from gadget-2.6 tree # # ChangeSet # 2004/05/05 13:44:44-07:00 al.fracchetti@tin.it # [PATCH] USB Storage: Kyocera Finecsm 3L -unusual_devs.h # # drivers/usb/storage/unusual_devs.h # 2004/03/27 10:48:01-08:00 al.fracchetti@tin.it +6 -0 # USB Storage: Kyocera Finecsm 3L -unusual_devs.h # # ChangeSet # 2004/05/05 13:36:39-07:00 stern@rowland.harvard.edu # [PATCH] USB: Small change to CPiA USB driver # # Only one aspect of it is notable: The CPiA USB # driver calls usb_driver_release_interface() during its disconnect() # routine. That doesn't appear to be necessary, since it didn't call # usb_driver_claim_interface() beforehand and since the interface will be # released automatically when disconnect() returns. # # drivers/media/video/cpia_usb.c # 2004/04/27 06:54:33-07:00 stern@rowland.harvard.edu +1 -3 # USB: Small change to CPiA USB driver # # ChangeSet # 2004/05/05 13:36:11-07:00 stern@rowland.harvard.edu # [PATCH] USB Storage: unusual_devs.h update # # On 4 May 2004, Rajesh Kumble Nayak wrote: # # > The Above patch work fine for Sony Hc-85 # > I shall post the dmesg entry soon. # > # > With many thanks # > Rajesh # # Greg and Pete, here's the patch. It's possible that this entry could be # combined with the previous one, but until we know definitely they should # be kept separate. # # drivers/usb/storage/unusual_devs.h # 2004/04/29 07:42:22-07:00 stern@rowland.harvard.edu +7 -0 # USB Storage: unusual_devs.h update # # ChangeSet # 2004/05/05 13:35:43-07:00 stern@rowland.harvard.edu # [PATCH] USB: Reduce kernel stack usage # # This patch allocates a temporary array from the heap instead of from the # kernel's stack in usb_set_configuration(). It also updates a few # comments. Please apply. # # drivers/usb/core/message.c # 2004/05/03 03:26:40-07:00 stern@rowland.harvard.edu +23 -12 # USB: Reduce kernel stack usage # # ChangeSet # 2004/05/05 13:35:14-07:00 daniel.ritz@gmx.ch # [PATCH] USB: add support for eGalax Touchscreen USB # # this is the second version of the patch to add support for eGalax Touchkit USB # touchscreen. changes since last patch: # - fixed the bug in open, found by oliver neukum # - renamed driver from touchkit.c to touchkitusb.c (since the thing also exists # as RS232, PS/2 and I2C) # - some minor coding style updates # # drivers/usb/input/touchkitusb.c # 2004/05/02 07:32:32-07:00 daniel.ritz@gmx.ch +310 -0 # USB: add support for eGalax Touchscreen USB # # drivers/usb/input/Makefile # 2004/04/25 09:18:58-07:00 daniel.ritz@gmx.ch +1 -0 # USB: add support for eGalax Touchscreen USB # # drivers/usb/input/Kconfig # 2004/05/01 10:40:50-07:00 daniel.ritz@gmx.ch +13 -0 # USB: add support for eGalax Touchscreen USB # # drivers/usb/input/touchkitusb.c # 2004/05/02 07:32:32-07:00 daniel.ritz@gmx.ch +0 -0 # BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/input/touchkitusb.c # # ChangeSet # 2004/05/05 13:34:46-07:00 david-b@pacbell.net # [PATCH] USB: usbnet handles Billionton Systems USB2AR # # This adds another ax8817x device to "usbnet". # # drivers/usb/net/usbnet.c # 2004/04/26 14:32:52-07:00 david-b@pacbell.net +4 -0 # USB: usbnet handles Billionton Systems USB2AR # # ChangeSet # 2004/05/05 13:34:16-07:00 stefan.eletzhofer@eletztrick.de # [PATCH] USB Gadget: fix g_serial debug module parm # # g_serial.ko can't be load as module because "debug" is only # defined if G_SERIAL_DEBUG is defined, but "debug" is referenced # in MODULE_PARM(). # # drivers/usb/gadget/serial.c # 2004/05/04 17:00:00-07:00 stefan.eletzhofer@eletztrick.de +5 -1 # USB Gadget: fix g_serial debug module parm # # ChangeSet # 2004/05/05 13:33:47-07:00 stefan.eletzhofer@eletztrick.de # [PATCH] USB Gadget: fix pxa define in gadget_chips.h # # below is a trivial patch which fixes the PXA gadget define # in drivers/linux/usb/gadget/gadget_chips.h # # Everywhere CONFIG_USB_GADGET_PXA2XX is used, except in that file, which # bites obviously ... # # # Fix define for PXA UDC. # # drivers/usb/gadget/gadget_chips.h # 2004/05/04 17:00:00-07:00 stefan.eletzhofer@eletztrick.de +1 -1 # USB Gadget: fix pxa define in gadget_chips.h # # ChangeSet # 2004/05/05 12:13:23-07:00 greg@kroah.com # USB: add support for Zire 31 devices. # # Info was from Adriaan de Groot # # drivers/usb/serial/visor.h # 2004/05/05 05:12:42-07:00 greg@kroah.com +1 -0 # USB: add support for Zire 31 devices. # # Info was from Adriaan de Groot # # drivers/usb/serial/visor.c # 2004/05/05 05:12:42-07:00 greg@kroah.com +3 -0 # USB: add support for Zire 31 devices. # # Info was from Adriaan de Groot # # ChangeSet # 2004/05/04 14:13:11-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb.h # 2004/05/04 14:13:08-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/05/03 14:18:33-07:00 tejohnson@yahoo.com # [PATCH] USB: update for mtouchusb # # The attached patch for the 3M Touch Systems Capacitive controller. (again) # # Quick list of changes: # # * decrease mtouch->open counter in the event of a urb submission # failure # # The changes are due to comments Oliver Neukum's comments on the # touchkit.c driver. Good catch! Sorry I missed it. # # http://marc.theaimsgroup.com/?l=linux-usb-devel&m=108343028201159&w=2 # # drivers/usb/input/mtouchusb.c # 2004/05/02 09:37:18-07:00 tejohnson@yahoo.com +3 -1 # USB: update for mtouchusb # # ChangeSet # 2004/05/03 14:17:38-07:00 stern@rowland.harvard.edu # [PATCH] USB: Altsetting update for USB IrDA driver # # This patch updates the USB IrDA driver to take into account that the # kernel may no longer store altsetting entries in numerical order. # The driver only needed one change; this was a simple matter of using the # entry corresponding to the altsetting that was just installed. # # drivers/net/irda/irda-usb.c # 2004/05/03 03:37:59-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting update for USB IrDA driver # # ChangeSet # 2004/05/03 14:16:51-07:00 c.lucas@ifrance.com # [PATCH] USB: esthetic and trivial patch. # # include/linux/usb.h # 2004/05/02 23:42:51-07:00 c.lucas@ifrance.com +1 -1 # USB: esthetic and trivial patch. # # ChangeSet # 2004/05/01 16:24:00-07:00 msdemlei@cl.uni-heidelberg.de # [PATCH] USB: DSBR-100 tiny patch # # On Fri, Feb 06, 2004 at 10:17:32AM -0800, Greg KH wrote: # > On Fri, Feb 06, 2004 at 05:06:01PM +0100, Markus Demleitner wrote: # > > Since I finally switched over to 2.6 I noticed that my dsbr100 driver # > > produces a warning to the effect that I should provide a release # > > callback. After a quick google on the issue I came to the conclusion # > # > No, you will have to fix up your driver to work properly, sorry. It's # > due to the changes to the v4l layer to handle removable devices much # > better (and to tie it into the driver model.) # # I didn't get around to doing real work on this until now, but finally # in the attachment there's my stab at bringing dsbr100 up to kernel 2.6. # I'm not really comfortable with the release callback issues (I've yet # to find some HOWTO-like documentation on this...) on the v4l side, # so I'd be grateful if you could have a look at it. I've basically # tried to copy what stv680 does, which may or may not have been a # good idea (in particular see the comment above the disconnect # function). # # I've used the opportunity for some code beautyfing, which of course # makes the patch a bit of a mess. I hope you won't mind too much # -- as you can see, it would have been pretty messy anyway. # # drivers/usb/media/dsbr100.c # 2004/04/21 12:32:35-07:00 msdemlei@cl.uni-heidelberg.de +133 -94 # USB: DSBR-100 tiny patch # # ChangeSet # 2004/05/01 16:18:03-07:00 david-b@pacbell.net # [PATCH] USB: dummy_hcd, root port wakeup/suspend # # Here's what's in my tree to make dummy_hcd do suspend and # wakeup correctly ... that is, making its emulated root hub # and gadget work more like real ones. # # It's easier to do this for fake hardware than the real stuff. # But real drivers tend to need very similar changes ... :) # # - Dave # # p.s. This does not depend on the suspend/resume patch. # And it doesn't do "global" suspend (of root hub). # # drivers/usb/gadget/dummy_hcd.c # 2004/04/29 11:16:12-07:00 david-b@pacbell.net +133 -33 # USB: dummy_hcd, root port wakeup/suspend # # ChangeSet # 2004/05/01 16:02:37-07:00 baldrick@free.fr # [PATCH] USB: fix WARN_ON in usbfs # # On Tuesday 27 April 2004 10:58, Oliver Neukum wrote: # > Am Dienstag, 27. April 2004 00:14 schrieb Greg KH: # > > On Mon, Apr 26, 2004 at 04:05:17PM +0200, Duncan Sands wrote: # > > > diff -Nru a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c # > > > --- a/drivers/usb/core/devio.c Mon Apr 26 13:48:28 2004 # > > > +++ b/drivers/usb/core/devio.c Mon Apr 26 13:48:28 2004 # > > > @@ -350,8 +350,8 @@ # > > > * all pending I/O requests; 2.6 does that. # > > > */ # > > > # > > > - if (ifnum < 8*sizeof(ps->ifclaimed)) # > > > - clear_bit(ifnum, &ps->ifclaimed); # > > > + BUG_ON(ifnum >= 8*sizeof(ps->ifclaimed)); # > > # > > I've changed that to a WARN_ON(). Yeah, writing over memory is bad, but # > > oopsing is worse. Let's be a bit nicer than that. # > # > You aren't nice that way. An oops has localised consequences. Scribbling # > over memory can cause anything. # # Hi Greg, if won't accept a BUG_ON, how about the following? # # drivers/usb/core/devio.c # 2004/04/30 03:01:37-07:00 baldrick@free.fr +5 -2 # USB: fix WARN_ON in usbfs # # ChangeSet # 2004/05/01 16:02:00-07:00 baldrick@free.fr # [PATCH] USB: usbfs: change extern inline to static inline # # And change __inline__ to inline and get rid of an unused function # while at it. # # drivers/usb/core/devio.c # 2004/04/30 16:36:25-07:00 baldrick@free.fr +5 -30 # USB: usbfs: change extern inline to static inline # # ChangeSet # 2004/05/01 15:23:25-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # MAINTAINERS # 2004/05/01 15:23:22-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/30 22:19:36-07:00 stuber@loria.fr # [PATCH] USB: LEGO USB Tower driver v0.95 # # here is the latest version 0.95 of the LEGO USB Tower driver against 2.6.6-rc3 # which corrects a lot of problems in the version currently in the kernel, # most notably sleeping in interrupt context and improper locking. # Please apply. # # It has been thoroughly tested with UHCI, OHCI and EHCI host controllers # using Lejos and NQC. Firmware and program download, and with proper # modifications all communication protocols supported by Lejos work, # as well as firmware and program download and datalog upload in NQC. # # Notes to application maintainers/protocol designers: # # - Small modifications are needed in communication protocols because # the tower tends to discard the first byte of transmissions. # So for example LNP needs to send an extra byte like 0xff before # the packet, and F7 handlers needs to cope with a lost 0x55. # # - I suggest /dev/usb/legousbtower0 etc. as the standard device names. # This puts it in the same place as the other USB devices and makes # clear which driver is responsible for these devices. # # drivers/usb/misc/legousbtower.c # 2004/04/30 12:44:31-07:00 stuber@loria.fr +489 -276 # USB: LEGO USB Tower driver v0.95 # # MAINTAINERS # 2004/04/30 08:30:25-07:00 stuber@loria.fr +7 -0 # USB: LEGO USB Tower driver v0.95 # # ChangeSet # 2004/04/30 22:19:00-07:00 david-b@pacbell.net # [PATCH] USB: reject urb submissions to suspended devices # # This patch rejects URB submissions to suspended devices, so # that they don't get hardware-specific fault reports. Instead, # they get the same code (-EHOSTUNREACH) for all HCDs. # # It also fixes a minor problem with colliding declarations of # the symbol USB_STATE_SUSPENDED. # # drivers/usb/core/urb.c # 2004/04/28 07:00:02-07:00 david-b@pacbell.net +2 -0 # USB: reject urb submissions to suspended devices # # drivers/usb/core/hcd.h # 2004/04/28 07:01:03-07:00 david-b@pacbell.net +1 -1 # USB: reject urb submissions to suspended devices # # drivers/usb/core/hcd-pci.c # 2004/04/28 06:57:24-07:00 david-b@pacbell.net +3 -3 # USB: reject urb submissions to suspended devices # # ChangeSet # 2004/04/30 22:17:59-07:00 david-b@pacbell.net # [PATCH] USB Gadget: gadget zero and USB suspend/resume # # This patch lets gadget zero be more useful in testing usb suspend # and resume. It prints messages on suspend() and resume(), and # supports an "autoresume=N" mode to wake the host after N seconds. # # drivers/usb/gadget/zero.c # 2004/04/27 13:26:18-07:00 david-b@pacbell.net +63 -2 # USB Gadget: gadget zero and USB suspend/resume # # ChangeSet # 2004/04/30 22:16:57-07:00 linux-usb@nerds-incorporated.org # [PATCH] USB: Alcatel TD10 Serial to USB converter cable support # # The Alcatel TD10 USB to Serial converter cable (for use with a Alcatel # OT 535 or 735(i) mobile phone) seems to be a repackaged Alcatel # version of the Prolific 2303 adapter. # # And as such, simply adding its product/vendor id (0x11f7/0x02df) to # drivers/usb/serial/pl2303.c seems to be enough to make it work. # # drivers/usb/serial/pl2303.h # 2004/04/29 09:35:06-07:00 linux-usb@nerds-incorporated.org +3 -0 # USB: Alcatel TD10 Serial to USB converter cable support # # drivers/usb/serial/pl2303.c # 2004/04/29 09:35:28-07:00 linux-usb@nerds-incorporated.org +1 -0 # USB: Alcatel TD10 Serial to USB converter cable support # # ChangeSet # 2004/04/30 22:15:51-07:00 stern@rowland.harvard.edu # [PATCH] USB: USB altsetting updates for IDSN Hisax driver # # The USB core is changing the way interfaces and altsettings are stored. # They are no longer required to be in numerical order, and as a result, # simply indexing the interface and altsetting arrays won't work as # expected. # # This patch for the st5481 takes these changes into account. A simpler # approach would be to store a pointer to the struct usb_host_interface # rather than look it up repeatedly, but I'm not very familiar with this # driver and didn't want to attempt such an alteration. # # drivers/isdn/hisax/st5481_usb.c # 2004/04/26 07:43:53-07:00 stern@rowland.harvard.edu +7 -3 # USB: USB altsetting updates for IDSN Hisax driver # # drivers/isdn/hisax/st5481_d.c # 2004/04/26 07:43:53-07:00 stern@rowland.harvard.edu +7 -2 # USB: USB altsetting updates for IDSN Hisax driver # # drivers/isdn/hisax/st5481_b.c # 2004/04/26 07:43:53-07:00 stern@rowland.harvard.edu +7 -2 # USB: USB altsetting updates for IDSN Hisax driver # # ChangeSet # 2004/04/30 22:06:45-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # drivers/usb/core/inode.c # 2004/04/30 22:06:42-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/29 16:00:47-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # include/linux/pci_ids.h # 2004/04/29 16:00:44-07:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2004/04/29 16:00:44-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/29 10:47:50-07:00 greg@kroah.com # USB: fix build error in hci_usb driver due to urb reference count change. # # This really needs to get fixed the proper way, by making the urb allocation # dynamic in the driver, instead of the hack it is currently doing... # # drivers/bluetooth/hci_usb.c # 2004/04/29 03:47:26-07:00 greg@kroah.com +1 -1 # USB: fix build error in hci_usb driver due to urb reference count change. # # This really needs to get fixed the proper way, by making the urb allocation # dynamic in the driver, instead of the hack it is currently doing... # # ChangeSet # 2004/04/29 10:40:57-07:00 greg@kroah.com # USB: remove the wait_for_urb function from bfusb driver as it's no longer needed. # # drivers/bluetooth/bfusb.c # 2004/04/29 03:40:30-07:00 greg@kroah.com +0 -9 # USB: remove the wait_for_urb function from bfusb driver as it's no longer needed. # # ChangeSet # 2004/04/28 21:43:38-07:00 sean@mess.org # [PATCH] USB: fix PhidgetServo driver # # Somehow I managed to send the wrong version. Here is a patch which fixes # that. (Remove a dev_info() which wasn't supposed to be there, and make sure # that everything is still consistent in the unlikely event that kmalloc() # fails). Just minor cleanups. # # drivers/usb/misc/phidgetservo.c # 2004/04/28 17:35:09-07:00 sean@mess.org +14 -17 # USB: fix PhidgetServo driver # # ChangeSet # 2004/04/28 13:38:52-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/input/hid-core.c # 2004/04/28 13:38:49-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/28 13:25:14-07:00 david-b@pacbell.net # [PATCH] USB: fix sparc64 2.6.6-rc2-mm2 build busted: usb/core/hub.c hubstatus # # > 2) An undefined 'hubstatus' variable in drivers/usb/core/hub.c: # > # > CC drivers/usb/core/hub.o # > drivers/usb/core/hub.c: In function `hub_port_connect_change': # > drivers/usb/core/hub.c:1343: error: `hubstatus' undeclared (first use in this function) # > drivers/usb/core/hub.c:1343: error: (Each undeclared identifier is reported only once # > drivers/usb/core/hub.c:1343: error: for each function it appears in.) # > make[3]: *** [drivers/usb/core/hub.o] Error 1 # > # > As a total shot in the dark, the following fixes the build (I've no clue # > if it is the right fix): # # Yes, it's the right fix. Greg, please merge the attached patch, # which will be needed on any big-endian system. # # drivers/usb/core/hub.c # 2004/04/27 01:19:29-07:00 david-b@pacbell.net +1 -1 # USB: fix sparc64 2.6.6-rc2-mm2 build busted: usb/core/hub.c hubstatus # # ChangeSet # 2004/04/28 11:52:21-07:00 stern@rowland.harvard.edu # [PATCH] USB: Lock devices during tree traversal # # On Tue, 27 Apr 2004, Greg KH wrote: # # > So, what's next in this patch series? :) # # Funny you should ask... # # While writing those patches I noted a problem, that the USB device tree # can change while a process reading /proc/bus/usb/devices is traversing it, # leading to an oops when a pointer to a no-longer-existing child device is # dereferenced. The ensuing discussion led to the conclusion that the # devices' ->serialize locks should be acquired, top-down, while going # through the tree. # # That means changing the code that populates the devices file and changing # the code that adds and removes USB device structures. This patch takes # care of the first part. I'm delaying the second part because that section # of usbcore is still under change -- David Brownell's revisions have not # yet been fully integrated. # # A similar change should be made to usb_find_device() and match_device() in # usb.c. You may want to add that yourself. # # drivers/usb/core/devices.c # 2004/04/28 06:48:48-07:00 stern@rowland.harvard.edu +10 -2 # USB: Lock devices during tree traversal # # ChangeSet # 2004/04/28 11:51:52-07:00 sean@mess.org # [PATCH] USB: add new USB PhidgetServo driver # # Here is a driver for the usb servo controllers from Phidgets # , using sysfs. # # Note that the devices claim to be hid devices, so I've added them to the # hid_blacklist (HID_QUIRK_IGNORE). A servo controller isn't really an hid # device (or is it?). # # drivers/usb/misc/Makefile # 2004/04/21 09:02:00-07:00 sean@mess.org +1 -0 # USB: add new USB PhidgetServo driver # # drivers/usb/misc/Kconfig # 2004/04/21 09:02:00-07:00 sean@mess.org +12 -0 # USB: add new USB PhidgetServo driver # # drivers/usb/input/hid-core.c # 2004/04/22 02:54:25-07:00 sean@mess.org +12 -0 # USB: add new USB PhidgetServo driver # # drivers/usb/Makefile # 2004/04/21 09:02:00-07:00 sean@mess.org +1 -0 # USB: add new USB PhidgetServo driver # # drivers/usb/misc/phidgetservo.c # 2004/04/27 04:03:07-07:00 sean@mess.org +330 -0 # USB: add new USB PhidgetServo driver # # drivers/usb/misc/phidgetservo.c # 2004/04/27 04:03:07-07:00 sean@mess.org +0 -0 # BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/misc/phidgetservo.c # # ChangeSet # 2004/04/27 19:39:13-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb.h # 2004/04/27 19:39:10-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/04/27 19:39:10-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/message.c # 2004/04/27 19:39:10-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/27 17:33:52-07:00 greg@kroah.com # USB: fix compiler warnings in devices.c file. # # drivers/usb/core/devices.c # 2004/04/27 10:33:12-07:00 greg@kroah.com +2 -2 # USB: fix compiler warnings in devices.c file. # # ChangeSet # 2004/04/27 16:02:32-07:00 stern@rowland.harvard.edu # [PATCH] USB: Allocate interface structures dynamically # # This is a revised version of an earlier patch; I feel a lot better about # this one. Basically it does the same thing as before: allocate # interfaces dynamically to avoid the problems with reusing them. # # The difference is that this patch adds a struct kref to the array of # usb_interface_cache's, so the array can persist if needed after the # device has been disconnected. Each interface takes a reference to it # (along with the configuration itself), so as long as the interfaces # remain pinned in memory the altsettings will also remain. # # Here is a slight revision of patch as246b. This one allocates all the new # interfaces before changing any other state; otherwise it's the same. # # include/linux/usb.h # 2004/04/15 07:43:58-07:00 stern@rowland.harvard.edu +38 -3 # USB: Allocate interface structures dynamically # # drivers/usb/core/usb.c # 2004/04/15 05:19:20-07:00 stern@rowland.harvard.edu +1 -1 # USB: Allocate interface structures dynamically # # drivers/usb/core/message.c # 2004/04/15 05:28:25-07:00 stern@rowland.harvard.edu +46 -13 # USB: Allocate interface structures dynamically # # drivers/usb/core/devices.c # 2004/04/15 05:21:07-07:00 stern@rowland.harvard.edu +19 -12 # USB: Allocate interface structures dynamically # # drivers/usb/core/config.c # 2004/04/15 07:45:41-07:00 stern@rowland.harvard.edu +31 -44 # USB: Allocate interface structures dynamically # # ChangeSet # 2004/04/27 15:22:05-07:00 greg@kroah.com # USB: fix incorrect usb-serial conversion for cur_altsetting from previous patch. # # drivers/usb/serial/usb-serial.c # 2004/04/27 08:21:23-07:00 greg@kroah.com +1 -1 # USB: fix incorrect usb-serial conversion for cur_altsetting from previous patch. # # ChangeSet # 2004/04/27 14:45:49-07:00 greg@kroah.com # USB: make ehci driver use a kref instead of an atomic_t # # drivers/usb/host/ehci.h # 2004/04/27 14:45:32-07:00 greg@kroah.com +2 -1 # USB: make ehci driver use a kref instead of an atomic_t # # drivers/usb/host/ehci-sched.c # 2004/04/27 14:45:32-07:00 greg@kroah.com +3 -3 # USB: make ehci driver use a kref instead of an atomic_t # # drivers/usb/host/ehci-q.c # 2004/04/27 14:45:32-07:00 greg@kroah.com +5 -5 # USB: make ehci driver use a kref instead of an atomic_t # # drivers/usb/host/ehci-mem.c # 2004/04/27 14:45:32-07:00 greg@kroah.com +23 -16 # USB: make ehci driver use a kref instead of an atomic_t # # drivers/usb/host/ehci-hcd.c # 2004/04/27 14:45:32-07:00 greg@kroah.com +1 -1 # USB: make ehci driver use a kref instead of an atomic_t # # ChangeSet # 2004/04/27 14:21:13-07:00 greg@kroah.com # USB: removed unused atomic_t in keyspan driver structure. # # drivers/usb/serial/keyspan.c # 2004/04/27 07:20:12-07:00 greg@kroah.com +0 -3 # USB: removed unused atomic_t in keyspan driver structure. # # ChangeSet # 2004/04/26 18:31:48-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb.h # 2004/04/26 18:31:45-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/04/26 18:31:45-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/26 17:07:28-07:00 greg@kroah.com # USB: switch struct urb to use a kref instead of it's own atomic_t # # include/linux/usb.h # 2004/04/26 10:06:52-07:00 greg@kroah.com +2 -1 # USB: switch struct urb to use a kref instead of it's own atomic_t # # drivers/usb/core/urb.c # 2004/04/26 10:06:52-07:00 greg@kroah.com +13 -8 # USB: switch struct urb to use a kref instead of it's own atomic_t # # ChangeSet # 2004/04/26 16:11:11-07:00 mdharm-usb@one-eyed-alien.net # [PATCH] USB: usb-storage driver changes for 2.6.x [4/4] # # This is a trivial patch to remove some duplicate includes. sched.h and # errno.h are already included in this file about a dozen lines or so above # this point. # # drivers/usb/storage/usb.c # 2004/04/24 19:25:23-07:00 mdharm-usb@one-eyed-alien.net +0 -2 # USB: usb-storage driver changes for 2.6.x [4/4] # # ChangeSet # 2004/04/26 16:10:49-07:00 mdharm-usb@one-eyed-alien.net # [PATCH] USB: usb-storage driver changes for 2.6.x [3/4] # # This patch adds some clear-halt calls if a GetMaxLUN fails. Apparently, # some devices (like certain early-rev Zip100s) stall their bulk pipes if # they receive a GetMaxLUN. # # drivers/usb/storage/transport.c # 2004/04/24 19:25:32-07:00 mdharm-usb@one-eyed-alien.net +11 -0 # USB: usb-storage driver changes for 2.6.x [3/4] # # ChangeSet # 2004/04/26 16:10:25-07:00 mdharm-usb@one-eyed-alien.net # [PATCH] USB: usb-storage driver changes for 2.6.x [2/4] # # This is patch as248b from Alan Stern, modified by myself: This adds a flag # which allows us to supress the "unneeded unusual_devs.h entry" message. # This is useful for times when idiotic device manufacturers break the rules # and release two different devices with the same VID, PID, and revision # number. # # drivers/usb/storage/usb.h # 2004/04/24 19:25:41-07:00 mdharm-usb@one-eyed-alien.net +1 -0 # USB: usb-storage driver changes for 2.6.x [2/4] # # drivers/usb/storage/usb.c # 2004/04/24 19:25:41-07:00 mdharm-usb@one-eyed-alien.net +1 -1 # USB: usb-storage driver changes for 2.6.x [2/4] # # drivers/usb/storage/unusual_devs.h # 2004/04/24 19:25:41-07:00 mdharm-usb@one-eyed-alien.net +1 -1 # USB: usb-storage driver changes for 2.6.x [2/4] # # ChangeSet # 2004/04/26 16:10:02-07:00 mdharm-usb@one-eyed-alien.net # [PATCH] USB: usb-storage driver changes for 2.6.x [1/4] # # Patch as239b from Alan Stern: This patch improves the interaction between # a SCSI reset, an internally generated reset, and an abort. This improves # our error-recovery in cases where the device is hung (or almost hung) while # we're trying to auto-reset. # # drivers/usb/storage/usb.h # 2004/04/24 19:25:50-07:00 mdharm-usb@one-eyed-alien.net +3 -2 # USB: usb-storage driver changes for 2.6.x [1/4] # # drivers/usb/storage/transport.c # 2004/04/24 19:25:50-07:00 mdharm-usb@one-eyed-alien.net +26 -21 # USB: usb-storage driver changes for 2.6.x [1/4] # # drivers/usb/storage/scsiglue.c # 2004/04/24 19:25:50-07:00 mdharm-usb@one-eyed-alien.net +11 -8 # USB: usb-storage driver changes for 2.6.x [1/4] # # ChangeSet # 2004/04/26 16:09:34-07:00 stern@rowland.harvard.edu # [PATCH] USB: Altsetting updates for usb/serial # # The updates needed for proper altsetting handling among the USB serial # drivers turned out to be a lot easier than I expected, thanks to the # organization of the drivers. Only a handful of changes were needed. # # drivers/usb/serial/usb-serial.c # 2004/04/26 07:38:00-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting updates for usb/serial # # drivers/usb/serial/safe_serial.c # 2004/04/26 07:38:00-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting updates for usb/serial # # drivers/usb/serial/kobil_sct.c # 2004/04/26 07:38:00-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting updates for usb/serial # # drivers/usb/serial/io_ti.c # 2004/04/26 07:38:00-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting updates for usb/serial # # ChangeSet # 2004/04/26 16:09:06-07:00 baldrick@free.fr # [PATCH] USB: be assertive in usbfs # # Be assertive. # # drivers/usb/core/devio.c # 2004/04/26 06:48:28-07:00 baldrick@free.fr +2 -2 # USB: be assertive in usbfs # # ChangeSet # 2004/04/26 16:08:38-07:00 bellucda@tiscali.it # [PATCH] USB: audits in usb_init() # # there were some missing audits in usb_init() # # drivers/usb/core/usb.c # 2004/04/26 14:55:56-07:00 bellucda@tiscali.it +26 -6 # USB: audits in usb_init() # # ChangeSet # 2004/04/26 16:08:12-07:00 tejohnson@yahoo.com # [PATCH] USB: mtouchusb update for 2.6.6-rc2 # # The attached patch for the 3M Touch Systems Capacitive controller. # # Quick list of changes: # # * Changed reset from standard USB dev reset to vendor reset # * Changed data sent to host from compensated to raw coordinates # * Eliminated vendor/product module params # * Performed multiple successfull tests with an EXII-5010UC # # The changes are primarily due to comments from Vojtech Pavlik, as well # as making the newer EXII-50XXUC controllers work. # # Thanks to 3M Touch Systems for sending me some new controllers to test with! # # An updated HOWTO is also available at: # # # http://groomlakelabs.com/grandamp/code/microtouch/Linux-Input-USB-Touchscreen-HowTo.txt # # drivers/usb/input/mtouchusb.c # 2004/04/25 12:16:01-07:00 tejohnson@yahoo.com +35 -72 # USB: mtouchusb update for 2.6.6-rc2 # # Documentation/usb/mtouchusb.txt # 2004/04/25 12:16:49-07:00 tejohnson@yahoo.com +42 -51 # USB: mtouchusb update for 2.6.6-rc2 # # ChangeSet # 2004/04/25 23:06:05-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # include/linux/pci_ids.h # 2004/04/25 23:06:02-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/24 23:51:05-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/core/usb.c # 2004/04/24 23:51:02-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/23 16:41:30-07:00 stern@rowland.harvard.edu # [PATCH] USB: Altsetting update for USB net drivers # # The only driver under usb/net that needed any altsetting changes was # usbnet. I'm not looking forward to going through all the source files # under usb/serial. :-( # # drivers/usb/net/usbnet.c # 2004/04/22 09:56:28-07:00 stern@rowland.harvard.edu +4 -4 # USB: Altsetting update for USB net drivers # # ChangeSet # 2004/04/23 16:41:04-07:00 stern@rowland.harvard.edu # [PATCH] USB: Altsetting update for USB misc drivers # # This is the altsetting update for the drivers under usb/misc. As you can, # not much was needed at all. # # drivers/usb/misc/uss720.c # 2004/04/22 09:08:37-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting update for USB misc drivers # # drivers/usb/misc/legousbtower.c # 2004/04/22 09:01:26-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting update for USB misc drivers # # ChangeSet # 2004/04/23 16:40:35-07:00 stern@rowland.harvard.edu # [PATCH] USB: Altsetting updates for USB media drivers # # This patch implements the new altsetting regime for the drivers under # usb/media. Not much needed to be changed. I'm unable to test any of the # changes, but at least they compile all right (except that I didn't even # try to compile the pwc driver since it's marked BROKEN). # # The stv680 and w9968cf drivers still include an assumption that they are # bound to interface number 0. Since that the drivers are fairly tightly # linked to a specific kind of device I didn't try to change those # assumptions, but maybe they should be changed. # # drivers/usb/media/vicam.c # 2004/04/22 04:41:49-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting updates for USB media drivers # # drivers/usb/media/ultracam.c # 2004/04/21 09:26:45-07:00 stern@rowland.harvard.edu +4 -3 # USB: Altsetting updates for USB media drivers # # drivers/usb/media/se401.c # 2004/04/21 09:21:19-07:00 stern@rowland.harvard.edu +1 -1 # USB: Altsetting updates for USB media drivers # # drivers/usb/media/pwc-if.c # 2004/04/21 09:18:47-07:00 stern@rowland.harvard.edu +5 -2 # USB: Altsetting updates for USB media drivers # # drivers/usb/media/ov511.c # 2004/04/22 04:40:56-07:00 stern@rowland.harvard.edu +11 -3 # USB: Altsetting updates for USB media drivers # # drivers/usb/media/konicawc.c # 2004/04/21 09:41:57-07:00 stern@rowland.harvard.edu +12 -5 # USB: Altsetting updates for USB media drivers # # drivers/usb/media/ibmcam.c # 2004/04/21 09:32:00-07:00 stern@rowland.harvard.edu +5 -4 # USB: Altsetting updates for USB media drivers # # ChangeSet # 2004/04/23 16:40:06-07:00 stern@rowland.harvard.edu # [PATCH] USB: Cosmetic improvements for the UHCI driver # # This patch makes a few minor improvements to the appearance of the UHCI # driver. Please apply. # # drivers/usb/host/uhci-hcd.h # 2004/04/19 04:05:56-07:00 stern@rowland.harvard.edu +7 -7 # USB: Cosmetic improvements for the UHCI driver # # drivers/usb/host/uhci-hcd.c # 2004/04/14 03:19:54-07:00 stern@rowland.harvard.edu +10 -10 # USB: Cosmetic improvements for the UHCI driver # # ChangeSet # 2004/04/23 16:39:38-07:00 stern@rowland.harvard.edu # [PATCH] USB: Ignore URB_NO_INTERRUPT flag in UHCI # # Following a suggestion of David Brownell's I have decided to remove # support for the URB_NO_INTERRUPT flag in the UHCI driver. The overall # effect of the flag is to reduce the number of interrupts, thereby # improving throughput somewhat while increasing the duration of the # remaining IRQ handlers quite a lot (i.e., increasing interrupt variance). # So I think we're better off without it. Mind you, this is all in the # absence of any firm measurements. # # A common case where this will come up is during usb-storage bulk # transfers. Such transfers are generally divided into scatter-gather # components each corresponding to a single URB and transferring one memory # page (4 KB). While generating an interrupt for each one is a little # faster than ideal -- about every 3 ms -- it's better than waiting until 64 # KB has been transferred and there are 1024 individual TDs to clean up # during the IRQ. # # drivers/usb/host/uhci-hcd.c # 2004/04/16 04:02:37-07:00 stern@rowland.harvard.edu +7 -3 # USB: Ignore URB_NO_INTERRUPT flag in UHCI # # ChangeSet # 2004/04/23 16:20:26-07:00 baldrick@free.fr # [PATCH] USB usbfs: drop pointless racy check # # The check of interface->dev.driver requires a lock to be taken # to protect against driver binding changes. But in fact I think it # is better just to drop the test. The result is that the caller is # required to claim an interface before changing the altsetting, # which is consistent with the other routines that operate on # interfaces. # # devio.c | 6 ++---- # 1 files changed, 2 insertions(+), 4 deletions(-) # # drivers/usb/core/devio.c # 2004/04/14 05:18:37-07:00 baldrick@free.fr +2 -4 # USB usbfs: drop pointless racy check # # ChangeSet # 2004/04/23 16:20:07-07:00 baldrick@free.fr # [PATCH] USB usbfs: missing lock in proc_getdriver # # Hi Oliver, # # > I expect it to rarely matter, but it might matter now and then. It's # > just a question of hygiene. If you are using a temporary buffer I'd # > like to see it used to full advantage. So either drop the lock or do # > a direct copy. I'd prefer the first option your patch implemented. # # I agree. Greg, please consider applying the updated patch: # # # # Protect against driver binding changes while reading the driver name. # # drivers/usb/core/devio.c # 2004/04/14 07:03:12-07:00 baldrick@free.fr +6 -4 # USB usbfs: missing lock in proc_getdriver # # ChangeSet # 2004/04/23 16:19:43-07:00 baldrick@free.fr # [PATCH] USB usbfs: destroy submitted urbs only on the disconnected interface # # The remaining three patches contain miscellaneous fixes to usbfs. # This one fixes up the disconnect callback to only shoot down urbs # on the disconnected interface, and not on all interfaces. It also adds # a sanity check (this check is pointless because the interface could # never have been claimed in the first place if it failed, but I feel better # having it there). # # devio.c | 6 ++++-- # 1 files changed, 4 insertions(+), 2 deletions(-) # # drivers/usb/core/devio.c # 2004/04/14 05:18:20-07:00 baldrick@free.fr +4 -2 # USB usbfs: destroy submitted urbs only on the disconnected interface # # ChangeSet # 2004/04/23 16:19:14-07:00 baldrick@free.fr # [PATCH] USB usbfs: fix up releaseintf # # The semaphore is now taken in the callers. # # devio.c | 2 -- # 1 files changed, 2 deletions(-) # # drivers/usb/core/devio.c # 2004/04/14 05:18:08-07:00 baldrick@free.fr +0 -2 # USB usbfs: fix up releaseintf # # ChangeSet # 2004/04/23 16:18:50-07:00 baldrick@free.fr # [PATCH] USB usbfs: fix up proc_ioctl # # The semaphore is now taken in the caller. # # devio.c | 2 -- # 1 files changed, 2 deletions(-) # # drivers/usb/core/devio.c # 2004/04/14 05:17:56-07:00 baldrick@free.fr +0 -2 # USB usbfs: fix up proc_ioctl # # ChangeSet # 2004/04/23 16:18:24-07:00 baldrick@free.fr # [PATCH] USB usbfs: fix up proc_setconfig # # The semaphore is now taken in the caller. # # devio.c | 2 -- # 1 files changed, 2 deletions(-) # # drivers/usb/core/devio.c # 2004/04/14 05:17:46-07:00 baldrick@free.fr +0 -2 # USB usbfs: fix up proc_setconfig # # ChangeSet # 2004/04/23 16:17:59-07:00 baldrick@free.fr # [PATCH] USB usbfs: remove obsolete comment from proc_resetdevice # # devio.c | 3 --- # 1 files changed, 3 deletions(-) # # drivers/usb/core/devio.c # 2004/04/14 05:17:37-07:00 baldrick@free.fr +0 -3 # USB usbfs: remove obsolete comment from proc_resetdevice # # ChangeSet # 2004/04/23 16:17:35-07:00 baldrick@free.fr # [PATCH] USB usbfs: replace the per-file semaphore with the per-device semaphore # # devio.c | 43 +++++++++++++++++++++++-------------------- # usbdevice_fs.h | 1 - # 2 files changed, 23 insertions(+), 21 deletions(-) # # include/linux/usbdevice_fs.h # 2004/04/14 05:34:00-07:00 baldrick@free.fr +0 -1 # USB usbfs: replace the per-file semaphore with the per-device semaphore # # drivers/usb/core/devio.c # 2004/04/14 05:17:29-07:00 baldrick@free.fr +23 -20 # USB usbfs: replace the per-file semaphore with the per-device semaphore # # ChangeSet # 2004/04/23 16:17:09-07:00 baldrick@free.fr # [PATCH] USB usbfs: take a reference to the usb device # # Hi Greg, this is the first of a series of patches that replace the # per-file semaphore ps->devsem with the per-device semaphore # ps->dev->serialize. The role of devsem was to protect against # device disconnection. This can be done equally well using # ps->dev->serialize. On the other hand, ps->dev->serialize # protects against configuration and other changes, and has # already been introduced into usbfs in several places. Using # just one semaphore simplifies the code and removes some # remaining race conditions. It should also fix the oopses some # people have been seeing. In this first patch, a reference is # taken to the usb device as long as the usbfs file is open. That # way we can use ps->dev->serialize for as long as ps exists. # # devio.c | 27 ++++++++++++++++----------- # inode.c | 3 --- # 2 files changed, 16 insertions(+), 14 deletions(-) # # drivers/usb/core/inode.c # 2004/04/14 05:15:29-07:00 baldrick@free.fr +0 -3 # USB usbfs: take a reference to the usb device # # drivers/usb/core/devio.c # 2004/04/14 05:15:29-07:00 baldrick@free.fr +16 -11 # USB usbfs: take a reference to the usb device # # ChangeSet # 2004/04/23 15:45:24-07:00 david-b@pacbell.net # [PATCH] USB: khubd fixes # # This goes on top of the other enumeration patch I just sent, # to handle some dubious and/or broken hub configurations better. # # # Make khubd handle some cases better: # # - Track power budget for bus-powered hubs. This version only warns # when the budgets are exceeded. Eventually, the budgets should help # prevent such errors. # # - Rejects illegal USB setup: two consecutive bus powered hubs # would exceed the voltage drop budget, causing much flakiness. # # - For hosts with high speed hubs, warn when devices are hooked up # to full speed hubs if they'd be faster on a high speed one. # # - For hubs that don't do power switching, don't try to use it # # - For hubs that aren't self-powered, don't report local power status # # drivers/usb/core/hub.h # 2004/04/19 08:29:02-07:00 david-b@pacbell.net +2 -0 # USB: khubd fixes # # drivers/usb/core/hub.c # 2004/04/20 20:44:45-07:00 david-b@pacbell.net +143 -15 # USB: khubd fixes # # ChangeSet # 2004/04/23 15:44:57-07:00 david-b@pacbell.net # [PATCH] USB: re-factor enumeration logic # # This is an update to some patches from the December/January # timeframe, which will help sort out some of the mess for # drivers that need to use the reset logic. It's one of the # last significant patches in my gadget-2.6 tree that haven't # yet been merged into the main kernel tree. # # # More refactoring of the enumeration code paths: # # * The first half of usb_new_device() becomes the second half of a new # hub_port_init() routine (resets, sets address, gets descriptor) # # * The middle chunk of hub_port_connect_change() becomes the first half # of that new hub_port_init() routine. # # * Khubd uses that new routine in hub_port_connect_change(). # # * Now usb_new_device() cleans up better after faults, and has # a more useful locking policy (caller owns dev->serialize). # # * Has related minor cleanups including commenting some of # the curious request sequences coming from khubd. # # Refactoring means a lot of the current usb_reset_device() logic won't # need to stay an imperfect clone of the enumeration code ... soon, it # can just call hub_port_init(). # # Even without touching usb_reset_device(), this eliminates a deadlock. # Previously, address0_sem was used both during probe and during reset, # so probe routines can't implement DFU firmware download (involves a # reset; DFU also uncovers other problems) or safely recover from probe # faults by resetting (usb-storage can try that). Now that lock is no # longer held during probe(); so those deadlocks are gone. (And some # drivers, like at76c503, can start to remove ugly workarounds.) # # drivers/usb/core/usb.c # 2004/04/21 03:46:29-07:00 david-b@pacbell.net +13 -79 # USB: re-factor enumeration logic # # drivers/usb/core/hub.c # 2004/04/21 03:46:29-07:00 david-b@pacbell.net +215 -72 # USB: re-factor enumeration logic # # drivers/usb/core/hcd.c # 2004/04/21 03:46:29-07:00 david-b@pacbell.net +12 -0 # USB: re-factor enumeration logic # # ChangeSet # 2004/04/23 15:44:32-07:00 david-b@pacbell.net # [PATCH] USB: usbtest, smp unlink modes # # Handle some SMP-visible unlink states better. # # drivers/usb/misc/usbtest.c # 2004/04/14 20:23:11-07:00 david-b@pacbell.net +3 -3 # USB: usbtest, smp unlink modes # # ChangeSet # 2004/04/23 14:50:19-07:00 greg@kroah.com # [PATCH] USB: fix devio compiler warnings created by previous patch. # # drivers/usb/core/devio.c # 2004/04/23 07:33:30-07:00 greg@kroah.com +2 -2 # USB: fix devio compiler warnings created by previous patch. # # ChangeSet # 2004/04/23 14:49:56-07:00 stern@rowland.harvard.edu # [PATCH] USB: Eliminate dead code from the UHCI driver # # I'm not sure what this piece of code is doing in the UHCI driver. It # looks like someone envisioned queuing several URBs for the same endpoint # simultaneously. Anyway, the driver can't do that and this code can never # run. # # drivers/usb/host/uhci-hcd.c # 2004/04/16 04:02:37-07:00 stern@rowland.harvard.edu +1 -11 # USB: Eliminate dead code from the UHCI driver # # ChangeSet # 2004/04/23 14:49:33-07:00 stern@rowland.harvard.edu # [PATCH] USB: Implement endpoint_disable() for UHCI # # This patch implements the endpoint_disable method for the UHCI driver, as # you requested a while back. It guarantees that during unbinding events # (disconnect, configuration change, rmmod) the UHCI driver will have # finished using every URB for the interface being unbound. It doesn't # quite guarantee that the completion handlers will have finished running, # but it would take a pretty unlikely race to violate that assumption. (I # think it's the same with the OHCI and EHCI drivers.) # # Despite the patch numbering this one applies _after_ as249, which is a # more important bugfix. # # drivers/usb/host/uhci-hcd.h # 2004/04/19 04:05:56-07:00 stern@rowland.harvard.edu +2 -0 # USB: Implement endpoint_disable() for UHCI # # drivers/usb/host/uhci-hcd.c # 2004/04/14 03:19:54-07:00 stern@rowland.harvard.edu +49 -0 # USB: Implement endpoint_disable() for UHCI # # ChangeSet # 2004/04/23 14:49:10-07:00 stern@rowland.harvard.edu # [PATCH] USB: unusual_devs.h update # # On Tue, 20 Apr 2004, Damian Ivereigh wrote: # # > Here is the output of dmesg when plugging in an IBM USB MemKey # > # > usb-storage: This device (0a16,8888,0100 S 06 P 50) has unneeded SubClass and Protocol entries in unusual_devs.h # > Please send a copy of this message to # # Thank you for sending this in. Greg and Pete, here's the patch. # # drivers/usb/storage/unusual_devs.h # 2004/04/19 05:11:29-07:00 stern@rowland.harvard.edu +1 -1 # USB: unusual_devs.h update # # ChangeSet # 2004/04/23 14:48:47-07:00 stern@rowland.harvard.edu # [PATCH] USB: Remove unusual_devs entries for Minolta DiMAGE 7, 7Hi # # It looks safe to conclude that the unusual_devs.h entries for the Minolta # DiMAGE 7x cameras aren't needed. (Michael has tested the 7Hi and it's # definitely unnecessary.) The two other DiMAGE entries probably aren't # needed either, but we don't have any evidence of that so I'm leaving them. # # drivers/usb/storage/unusual_devs.h # 2004/04/16 04:37:06-07:00 stern@rowland.harvard.edu +0 -16 # USB: Remove unusual_devs entries for Minolta DiMAGE 7, 7Hi # # ChangeSet # 2004/04/23 14:48:28-07:00 david-b@pacbell.net # [PATCH] USB: root hubs can report remote wakeup feature # # The patch lets HCDs report the root hub remote wakeup feature to usbcore # through config descriptors, and lets usbcore say whether or not remote # wakeup (of host from sleep, by devices) should be enabled. # # Both OHCI and UHCI HCDs have some remote wakeup support already; I'm not # too sure how well it works. Given (separate) patches, their root hubs # can start to act more like other hubs in this area too. That'll make # it easier to start using USB suspend mode. # # drivers/usb/core/hcd.h # 2004/04/13 11:48:39-07:00 david-b@pacbell.net +10 -1 # USB: root hubs can report remote wakeup feature # # drivers/usb/core/hcd.c # 2004/04/13 11:33:31-07:00 david-b@pacbell.net +28 -11 # USB: root hubs can report remote wakeup feature # # ChangeSet # 2004/04/23 14:48:02-07:00 david-b@pacbell.net # [PATCH] USB: fix usbfs iso interval problem # # In 2.6, ISO transfers on USB require a value for urb->interval ... which # usbfs didn't provide (until this patch), or let user mode drivers specify. # # This patch initializes the urb->interval from the endpoint's descriptor, # so ISO transfers should now work from userspace. It also fixes a related # problem for interrupt transfers. # # drivers/usb/core/devio.c # 2004/04/14 13:36:53-07:00 david-b@pacbell.net +7 -1 # USB: fix usbfs iso interval problem # # ChangeSet # 2004/04/23 12:58:38-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # CREDITS # 2004/04/23 12:58:35-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/19 19:45:10-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # include/linux/pci_ids.h # 2004/04/19 19:45:07-07:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2004/04/19 19:45:07-07:00 akpm@bix.(none) +0 -0 # Auto merged # # CREDITS # 2004/04/19 19:45:06-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/07 20:17:13-07:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/core/message.c # 2004/04/07 20:17:11-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/02 11:35:28-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/pci_ids.h # 2004/04/02 11:35:25-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/04/01 15:16:14-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # CREDITS # 2004/04/01 15:16:11-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/31 19:24:39-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb.h # 2004/03/31 19:24:37-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/03/31 19:24:37-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/message.c # 2004/03/31 19:24:37-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc64/defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc64/configs/pSeries_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc/defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc/configs/pmac_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc/configs/common_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/parisc/configs/c3000_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ia64/defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ia64/configs/zx1_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ia64/configs/generic_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/arm/configs/neponset_defconfig # 2004/03/31 19:24:36-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/30 20:18:36-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # arch/ppc64/defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc64/configs/pSeries_defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc/defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc/configs/pmac_defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ppc/configs/common_defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/parisc/configs/c3000_defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ia64/defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ia64/configs/zx1_defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/ia64/configs/generic_defconfig # 2004/03/30 20:18:33-08:00 akpm@bix.(none) +0 -0 # Auto merged # # arch/arm/configs/neponset_defconfig # 2004/03/30 20:18:32-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/30 12:09:32-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb.h # 2004/03/30 12:09:29-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/03/30 12:09:29-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/message.c # 2004/03/30 12:09:29-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/29 18:05:43-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # MAINTAINERS # 2004/03/29 18:05:41-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/29 13:51:58-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # CREDITS # 2004/03/29 13:51:56-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/28 12:29:41-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # drivers/usb/core/message.c # 2004/03/28 12:29:38-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/27 02:28:18-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/input/wacom.c # 2004/03/27 02:28:16-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/26 12:24:49-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb_gadget.h # 2004/03/26 12:24:46-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/03/26 12:24:46-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/20 13:26:55-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # CREDITS # 2004/03/20 13:26:53-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/16 21:53:42-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # drivers/usb/input/wacom.c # 2004/03/16 21:53:39-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/input/hid-core.c # 2004/03/16 21:53:39-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/16 12:59:58-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # include/linux/usb_gadget.h # 2004/03/16 12:59:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # include/linux/usb.h # 2004/03/16 12:59:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/03/16 12:59:46-08:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2004/03/16 12:59:46-08:00 akpm@bix.(none) +0 -0 # Auto merged # # CREDITS # 2004/03/16 12:59:46-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/16 12:58:57-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # drivers/usb/input/wacom.c # 2004/03/16 12:58:47-08:00 akpm@bix.(none) +0 -4 # Auto merged # # drivers/usb/input/hid-core.c # 2004/03/16 12:58:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # CREDITS # 2004/03/16 12:58:46-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/14 11:03:00-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # include/linux/usb_gadget.h # 2004/03/14 11:02:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # include/linux/usb.h # 2004/03/14 11:02:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/usb.c # 2004/03/14 11:02:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2004/03/14 11:02:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/03/12 10:57:17-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-usb # # MAINTAINERS # 2004/03/12 10:57:02-08:00 akpm@bix.(none) +0 -0 # Auto merged # # CREDITS # 2004/03/12 10:57:01-08:00 akpm@bix.(none) +0 -0 # Auto merged # diff -Nru a/Documentation/usb/mtouchusb.txt b/Documentation/usb/mtouchusb.txt --- a/Documentation/usb/mtouchusb.txt Sun May 9 21:32:55 2004 +++ b/Documentation/usb/mtouchusb.txt Sun May 9 21:32:55 2004 @@ -1,26 +1,38 @@ CHANGES -- Created based off of scanner & INSTALL from the original touchscreen +- 0.3 - Created based off of scanner & INSTALL from the original touchscreen driver on freshmeat (http://freshmeat.net/projects/3mtouchscreendriver) - Amended for linux-2.4.18, then 2.4.19 -- Complete rewrite using Linux Input in 2.6.3 +- 0.5 - Complete rewrite using Linux Input in 2.6.3 Unfortunately no calibration support at this time +- 1.4 - Multiple changes to support the EXII 5000UC and house cleaning + 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 -DRIVER NOTES: +SUPPORTED HARDWARE: + + All controllers have the Vendor: 0x0596 & Product: 0x0001 -Installation is simple, you only need to add Linux Input, Linux USB, and the -driver to the kernel. The driver can also be optionally built as a module. -If you have another MicroTouch device that you wish to experiment with -or try using this driver with, but the Vendor and Product ID's are not -coded in, don't despair. If the driver was compiled as a module, you can -pass options to the driver. Simply try: + Controller Description Part Number + ------------------------------------------------------ - /sbin/modprobe mtouchusb vendor=0x#### product=0x**** + USB Capacitive - Pearl Case 14-205 (Discontinued) + USB Capacitive - Black Case 14-124 (Discontinued) + USB Capacitive - No Case 14-206 (Discontinued) + + USB Capacitive - Pearl Case EXII-5010UC + USB Capacitive - Black Case EXII-5030UC + USB Capacitive - No Case EXII-5050UC + +DRIVER NOTES: -If it works, send me the iVendor & iProduct (or a patch) and I will add... +Installation is simple, you only need to add Linux Input, Linux USB, and the +driver to the kernel. The driver can also be optionally built as a module. This driver appears to be one of possible 2 Linux USB Input Touchscreen drivers. Although 3M produces a binary only driver available for @@ -28,53 +40,28 @@ touchscreen for embedded apps using QTEmbedded, DirectFB, etc. So I feel the logical choice is to use Linux Imput. -A little info about the MicroTouch USB controller (14-206): - -Y is inverted, and the device has a total possible resolution of 0 - 65535. - -Y is inverted by the driver by: +Currently there is no way to calibrate the device via this driver. Even if +the device could be calibrated, the driver pulls to raw coordinate data from +the controller. This means calibration must be performed within the +userspace. + +The controller screen resolution is now 0 to 16384 for both X and Y reporting +the raw touch data. This is the same for the old and new capacitive USB +controllers. + +Perhaps at some point an abstract function will be placed into evdev so +generic functions like calibrations, resets, and vendor information can be +requested from the userspace (And the drivers would handle the vendor specific +tasks). - input.absmin[ABS_Y] = MTOUCHUSB_MAX_YC; - input.absmax[ABS_Y] = MTOUCHUSB_MIN_YC; - -absmin & absmax are also used to scale the data, sine it is rather high -resolution. - - ---------------touch screen area----------------- - I MicroTouch (xmax,ymax) @I - I X I - I ########visible monitor area############## I - I #@ (xmin,ymin) # I - I # # I - I # # I - I # # I - I # # I - I # # I - I Y # # I - I # # I - I # # I - I # # I - I # # I - I # # I - I # (xmax,ymax) @# I - I ########################################## I - I I - I@ MicroTouch (xmin,ymin) I - ------------------------------------------------- - -Currently there is no way to calibrate the device via this driver. Perhaps -at some point an abstract function will be placed into evdev so generic -functions like calibrations, resets, and vendor information can be requested -(And the drivers would handle the vendor specific tasks). - -ADDITIONAL INFORMATION/UPDATES: +ADDITIONAL INFORMATION/UPDATES/X CONFIGURATION EXAMPLE: http://groomlakelabs.com/grandamp/code/microtouch/ TODO: Implement a control urb again to handle requests to and from the device -such as calibration, etc. +such as calibration, etc once/if it becomes available. DISCLAMER: @@ -83,3 +70,7 @@ http://www.3m.com/3MTouchSystems/downloads/ +THANKS: + +A huge thank you to 3M Touch Systems for the EXII-5010UC controllers for +testing! diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Sun May 9 21:32:55 2004 +++ b/MAINTAINERS Sun May 9 21:32:55 2004 @@ -1228,6 +1228,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 Sun May 9 21:32:55 2004 +++ b/drivers/bluetooth/bfusb.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/bluetooth/hci_usb.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/isdn/hisax/st5481_b.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/isdn/hisax/st5481_d.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/isdn/hisax/st5481_usb.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/media/video/cpia_usb.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/net/irda/irda-usb.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/Makefile Sun May 9 21:32:55 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/core/config.c b/drivers/usb/core/config.c --- a/drivers/usb/core/config.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/core/config.c Sun May 9 21:32:55 2004 @@ -96,19 +96,14 @@ 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, @@ -117,7 +112,7 @@ 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; @@ -137,16 +132,16 @@ if (inum >= config->desc.bNumInterfaces) goto skip_to_next_interface_descriptor; - interface = config->interface[inum]; + intfc = config->intf_cache[inum]; asnum = d->bAlternateSetting; - if (asnum >= interface->num_altsetting) { + if (asnum >= intfc->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); + cfgno, inum, asnum, intfc->num_altsetting - 1); return -EINVAL; } - alt = &interface->altsetting[asnum]; + alt = &intfc->altsetting[asnum]; if (alt->desc.bLength) { dev_err(ddev, "Duplicate descriptor for config %d " "interface %d altsetting %d\n", cfgno, inum, asnum); @@ -210,11 +205,12 @@ 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 nalts[USB_MAXINTERFACES]; memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); if (config->desc.bDescriptorType != USB_DT_CONFIG || @@ -237,14 +233,7 @@ 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)); - } + memset(nalts, 0, nintf); /* Go through the descriptors, checking their length and counting the * number of altsettings for each interface */ @@ -277,8 +266,8 @@ cfgno, i, nintf_orig - 1); return -EINVAL; } - if (i < nintf) - ++config->interface[i]->num_altsetting; + if (i < nintf && nalts[i] < 255) + ++nalts[i]; } else if (header->bDescriptorType == USB_DT_DEVICE || header->bDescriptorType == USB_DT_CONFIG) { @@ -290,29 +279,29 @@ } /* for ((buffer2 = buffer, size2 = size); ...) */ - /* Allocate the altsetting arrays */ + /* Allocate the usb_interface_caches and altsetting arrays */ for (i = 0; i < nintf; ++i) { - interface = config->interface[i]; - if (interface->num_altsetting > USB_MAXALTSETTING) { + j = nalts[i]; + if (j > 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); + cfgno, i, j, USB_MAXALTSETTING); return -EINVAL; } - if (interface->num_altsetting == 0) { + if (j == 0) { dev_err(ddev, "config %d has no interface number " "%d\n", cfgno, i); return -EINVAL; } - 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); + intfc->num_altsetting = j; + kref_init(&intfc->ref, usb_release_interface_cache); } /* Skip over any Class Specific or Vendor Specific descriptors; @@ -340,9 +329,9 @@ /* 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) { + intfc = config->intf_cache[i]; + for (j = 0; j < intfc->num_altsetting; ++j) { + if (!intfc->altsetting[j].desc.bLength) { dev_err(ddev, "config %d interface %d has no " "altsetting %d\n", cfgno, i, j); return -EINVAL; @@ -374,10 +363,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); diff -Nru a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c --- a/drivers/usb/core/devices.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/core/devices.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/core/devio.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/core/hcd-pci.c Sun May 9 21:32:55 2004 @@ -293,7 +293,7 @@ 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: @@ -310,7 +310,7 @@ "suspend fail, retval %d\n", retval); else - hcd->state = USB_STATE_SUSPENDED; + hcd->state = HCD_STATE_SUSPENDED; } pci_set_power_state (dev, state); @@ -333,7 +333,7 @@ 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; diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c --- a/drivers/usb/core/hcd.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/core/hcd.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/core/hcd.h Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/core/hub.c Sun May 9 21:32:55 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; - - dev_dbg (&hubstate->intf->dev, - "port %d, status %x, change %x, %s\n", - port + 1, portstatus, portchange, portspeed (portstatus)); + static DECLARE_MUTEX(usb_address0_sem); - /* 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,258 @@ 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); +} + +static unsigned +hub_power_remaining (struct usb_hub *hubstate, struct usb_device *hub) +{ + int remaining; + unsigned i; - /* Free the configuration if there was an error */ - usb_put_dev(dev); + remaining = hubstate->power_budget; + if (!remaining) /* self-powered */ + return 0; - /* Switch to a long reset time */ - delay = HUB_LONG_RESET_TIME; + 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)) { + 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 +1448,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 +1606,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 +1680,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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/core/hub.h Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/core/inode.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/core/message.c Sun May 9 21:32:55 2004 @@ -796,10 +796,6 @@ } } -static void release_interface(struct device *dev) -{ -} - /* * usb_disable_device - Disable all the endpoints for a USB device * @dev: the device whose endpoints are being disabled @@ -835,6 +831,7 @@ dev_dbg (&dev->dev, "unregistering interface %s\n", interface->dev.bus_id); device_unregister (&interface->dev); + dev->actconfig->interface[i] = NULL; } dev->actconfig = 0; if (dev->state == USB_STATE_CONFIGURED) @@ -1071,6 +1068,16 @@ return 0; } +static void release_interface(struct device *dev) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_interface_cache *intfc = + altsetting_to_usb_interface_cache(intf->altsetting); + + kref_put(&intfc->ref); + kfree(intf); +} + /* * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated @@ -1109,19 +1116,19 @@ { int i, ret; struct usb_host_config *cp = NULL; - + struct usb_interface **new_interfaces = NULL; + int n, nintf; + /* dev->serialize guards all config changes */ - for (i=0; idescriptor.bNumConfigurations; i++) { + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { if (dev->config[i].desc.bConfigurationValue == configuration) { cp = &dev->config[i]; break; } } - if ((!cp && configuration != 0)) { - ret = -EINVAL; - goto out; - } + if ((!cp && configuration != 0)) + return -EINVAL; /* The USB spec says configuration 0 means unconfigured. * But if a device includes a configuration numbered 0, @@ -1130,6 +1137,34 @@ if (cp && configuration == 0) dev_warn(&dev->dev, "config 0 descriptor??\n"); + /* Allocate memory for new interfaces before doing anything else, + * so that if we run out then nothing will have changed. */ + n = nintf = 0; + if (cp) { + nintf = cp->desc.bNumInterfaces; + new_interfaces = kmalloc(nintf * sizeof(*new_interfaces), + GFP_KERNEL); + if (!new_interfaces) { + dev_err(&dev->dev, "Out of memory"); + return -ENOMEM; + } + + for (; n < nintf; ++n) { + new_interfaces[n] = kmalloc( + sizeof(struct usb_interface), + GFP_KERNEL); + if (!new_interfaces[n]) { + dev_err(&dev->dev, "Out of memory"); + ret = -ENOMEM; +free_interfaces: + while (--n >= 0) + kfree(new_interfaces[n]); + kfree(new_interfaces); + return ret; + } + } + } + /* if it's already configured, clear out old state first. * getting rid of old interfaces means unbinding their drivers. */ @@ -1139,7 +1174,7 @@ if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 0) - goto out; + goto free_interfaces; dev->actconfig = cp; if (!cp) @@ -1147,14 +1182,21 @@ else { dev->state = USB_STATE_CONFIGURED; - /* re-initialize hc/hcd/usbcore interface/endpoint state. - * this triggers binding of drivers to interfaces; and - * maybe probe() calls will choose different altsettings. + /* Initialize the new interface structures and the + * hc/hcd/usbcore interface/endpoint state. */ - for (i = 0; i < cp->desc.bNumInterfaces; ++i) { - struct usb_interface *intf = cp->interface[i]; + for (i = 0; i < nintf; ++i) { + struct usb_interface_cache *intfc; + struct usb_interface *intf; struct usb_host_interface *alt; + cp->interface[i] = intf = new_interfaces[i]; + memset(intf, 0, sizeof(*intf)); + intfc = cp->intf_cache[i]; + intf->altsetting = intfc->altsetting; + intf->num_altsetting = intfc->num_altsetting; + kref_get(&intfc->ref); + alt = usb_altnum_to_altsetting(intf, 0); /* No altsetting 0? We'll assume the first altsetting. @@ -1178,12 +1220,15 @@ configuration, alt->desc.bInterfaceNumber); } + kfree(new_interfaces); - /* Now that all interfaces are setup, probe() calls - * may claim() any interface that's not yet bound. - * Many class drivers need that: CDC, audio, video, etc. + /* Now that all the interfaces are set up, register them + * to trigger binding of drivers to interfaces. probe() + * routines may install different altsettings and may + * claim() any interfaces not yet bound. Many class drivers + * need that: CDC, audio, video, etc. */ - for (i = 0; i < cp->desc.bNumInterfaces; ++i) { + for (i = 0; i < nintf; ++i) { struct usb_interface *intf = cp->interface[i]; struct usb_interface_descriptor *desc; @@ -1204,7 +1249,6 @@ } } -out: return ret; } diff -Nru a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c --- a/drivers/usb/core/urb.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/core/urb.c Sun May 9 21:32:55 2004 @@ -13,6 +13,14 @@ #include #include "hcd.h" +#define to_urb(d) container_of(d, struct urb, kref) + +static void urb_destroy(struct kref *kref) +{ + struct urb *urb = to_urb(kref); + kfree(urb); +} + /** * usb_init_urb - initializes a urb so that it can be used by a USB driver * @urb: pointer to the urb to initialize @@ -31,7 +39,7 @@ { if (urb) { memset(urb, 0, sizeof(*urb)); - urb->count = (atomic_t)ATOMIC_INIT(1); + kref_init(&urb->kref, urb_destroy); spin_lock_init(&urb->lock); } } @@ -80,8 +88,7 @@ void usb_free_urb(struct urb *urb) { if (urb) - if (atomic_dec_and_test(&urb->count)) - kfree(urb); + kref_put(&urb->kref); } /** @@ -96,11 +103,9 @@ */ struct urb * usb_get_urb(struct urb *urb) { - if (urb) { - atomic_inc(&urb->count); - return urb; - } else - return NULL; + if (urb) + kref_get(&urb->kref); + return urb; } @@ -232,6 +237,8 @@ (dev->state < USB_STATE_DEFAULT) || (!dev->bus) || (dev->devnum <= 0)) return -ENODEV; + if (dev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; if (!(op = dev->bus->op) || !op->submit_urb) return -ENODEV; diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c --- a/drivers/usb/core/usb.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/core/usb.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/gadget/dummy_hcd.c Sun May 9 21:32:55 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/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h --- a/drivers/usb/gadget/gadget_chips.h Sun May 9 21:32:55 2004 +++ b/drivers/usb/gadget/gadget_chips.h Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/gadget/serial.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/gadget/zero.c Sun May 9 21:32:55 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-hcd.c b/drivers/usb/host/ehci-hcd.c --- a/drivers/usb/host/ehci-hcd.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/host/ehci-hcd.c Sun May 9 21:32:55 2004 @@ -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-mem.c b/drivers/usb/host/ehci-mem.c --- a/drivers/usb/host/ehci-mem.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/host/ehci-mem.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/host/ehci-q.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/host/ehci-sched.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/host/ehci.h Sun May 9 21:32:55 2004 @@ -366,7 +366,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; diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c --- a/drivers/usb/host/uhci-hcd.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/host/uhci-hcd.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/host/uhci-hcd.h Sun May 9 21:32:55 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/input/Kconfig b/drivers/usb/input/Kconfig --- a/drivers/usb/input/Kconfig Sun May 9 21:32:55 2004 +++ b/drivers/usb/input/Kconfig Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/input/Makefile Sun May 9 21:32:55 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/hid-core.c b/drivers/usb/input/hid-core.c --- a/drivers/usb/input/hid-core.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/input/hid-core.c Sun May 9 21:32:55 2004 @@ -1412,6 +1412,14 @@ #define USB_VENDOR_ID_CHIC 0x05fe #define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014 +#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 + struct hid_blacklist { __u16 idVendor; __u16 idProduct; @@ -1459,6 +1467,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/mtouchusb.c b/drivers/usb/input/mtouchusb.c --- a/drivers/usb/input/mtouchusb.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/input/mtouchusb.c Sun May 9 21:32:55 2004 @@ -28,6 +28,12 @@ * Complete rewrite using Linux Input in 2.6.3 * Unfortunately no calibration support at this time * + * 1.4 04/25/2004 (TEJ) tejohnson@yahoo.com + * 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 + * *****************************************************************************/ #include @@ -45,25 +51,28 @@ #include #include -#define MTOUCHUSB_MIN_XC 0xc8 -#define MTOUCHUSB_MAX_XC 0xff78 +#define MTOUCHUSB_MIN_XC 0x0 +#define MTOUCHUSB_MAX_XC 0x4000 #define MTOUCHUSB_XC_FUZZ 0x0 #define MTOUCHUSB_XC_FLAT 0x0 #define MTOUCHUSB_MIN_YC 0x0 -#define MTOUCHUSB_MAX_YC 0xff78 +#define MTOUCHUSB_MAX_YC 0x4000 #define MTOUCHUSB_YC_FUZZ 0x0 #define MTOUCHUSB_YC_FLAT 0x0 -#define MTOUCHUSB_ASYC_REPORT 1 -#define MTOUCHUSB_REPORT_SIZE_DATA 11 + +#define MTOUCHUSB_ASYNC_REPORT 1 +#define MTOUCHUSB_RESET 7 +#define MTOUCHUSB_REPORT_DATA_SIZE 11 #define MTOUCHUSB_REQ_CTRLLR_ID 10 -#define MTOUCHUSB_GET_XC(data) (data[4]<<8 | data[3]) -#define MTOUCHUSB_GET_YC(data) (data[6]<<8 | data[5]) +#define MTOUCHUSB_GET_XC(data) (data[8]<<8 | data[7]) +#define MTOUCHUSB_GET_YC(data) (data[10]<<8 | data[9]) #define MTOUCHUSB_GET_TOUCHED(data) ((data[2] & 0x40) ? 1:0) -#define DRIVER_VERSION "v0.1" +#define DRIVER_VERSION "v1.4" #define DRIVER_AUTHOR "Todd E. Johnson, tejohnson@yahoo.com" -#define DRIVER_DESC "Microtouch USB HID Touchscreen Driver" +#define DRIVER_DESC "3M USB Touchscreen Driver" +#define DRIVER_LICENSE "GPL" struct mtouch_usb { unsigned char *data; @@ -76,11 +85,9 @@ char phys[64]; }; -static __s32 vendor=-1, product=-1; - static struct usb_device_id mtouchusb_devices [] = { - { USB_DEVICE(0x0596, 0x0001) }, /* 3M (Formerly MicroTouch) 14-206 */ - { } /* Terminating entry */ + { USB_DEVICE(0x0596, 0x0001) }, + { } }; static void mtouchusb_irq(struct urb *urb, struct pt_regs *regs) @@ -135,8 +142,10 @@ mtouch->irq->dev = mtouch->udev; - if (usb_submit_urb (mtouch->irq, GFP_ATOMIC)) + if (usb_submit_urb (mtouch->irq, GFP_ATOMIC)) { + mtouch->open--; return -EIO; + } return 0; } @@ -153,7 +162,7 @@ { dbg("%s - called", __FUNCTION__); - mtouch->data = usb_buffer_alloc(udev, MTOUCHUSB_REPORT_SIZE_DATA, + mtouch->data = usb_buffer_alloc(udev, MTOUCHUSB_REPORT_DATA_SIZE, SLAB_ATOMIC, &mtouch->data_dma); if (!mtouch->data) @@ -167,7 +176,7 @@ dbg("%s - called", __FUNCTION__); if (mtouch->data) - usb_buffer_free(udev, MTOUCHUSB_REPORT_SIZE_DATA, + usb_buffer_free(udev, MTOUCHUSB_REPORT_DATA_SIZE, mtouch->data, mtouch->data_dma); } @@ -180,41 +189,8 @@ char path[64]; char *buf; int nRet; - int ix; - char valid_device = 0; dbg("%s - called", __FUNCTION__); - if (vendor != -1 && product != -1) { - info("%s - User specified USB Touch -- Vend:Prod - %x:%x", - __FUNCTION__, vendor, product); - } - - for (ix = 0; ix < sizeof (mtouchusb_devices) / - sizeof (struct usb_device_id); ix++) { - if ((udev->descriptor.idVendor == - mtouchusb_devices [ix].idVendor) && - (udev->descriptor.idProduct == - mtouchusb_devices [ix].idProduct)) { - valid_device = 1; - break; - } - } - - if (udev->descriptor.idVendor == vendor && - udev->descriptor.idProduct == product) { /* User specified */ - valid_device = 1; - } - - if (!valid_device) { - err("%s - No valid device!", __FUNCTION__); - return -EIO; - } - - if (udev->descriptor.bNumConfigurations != 1) { - err("%s - Only one device configuration is supported.", - __FUNCTION__); - return -EIO; - } dbg("%s - setting interface", __FUNCTION__); interface = intf->cur_altsetting; @@ -222,11 +198,6 @@ dbg("%s - setting endpoint", __FUNCTION__); endpoint = &interface->endpoint[0].desc; - if (interface->desc.bNumEndpoints != 1) { - err("%s - Only one endpoint is supported.", __FUNCTION__); - return -EIO; - } - if (!(mtouch = kmalloc (sizeof (struct mtouch_usb), GFP_KERNEL))) { err("%s - Out of memory.", __FUNCTION__); return -ENOMEM; @@ -266,8 +237,8 @@ mtouch->input.absmax[ABS_X] = MTOUCHUSB_MAX_XC; mtouch->input.absfuzz[ABS_X] = MTOUCHUSB_XC_FUZZ; mtouch->input.absflat[ABS_X] = MTOUCHUSB_XC_FLAT; - mtouch->input.absmin[ABS_Y] = MTOUCHUSB_MAX_YC; - mtouch->input.absmax[ABS_Y] = MTOUCHUSB_MIN_YC; + mtouch->input.absmin[ABS_Y] = MTOUCHUSB_MIN_YC; + mtouch->input.absmax[ABS_Y] = MTOUCHUSB_MAX_YC; mtouch->input.absfuzz[ABS_Y] = MTOUCHUSB_YC_FUZZ; mtouch->input.absflat[ABS_Y] = MTOUCHUSB_YC_FLAT; @@ -290,15 +261,15 @@ kfree(buf); nRet = usb_control_msg(mtouch->udev, - usb_rcvctrlpipe(udev, 0x80), - USB_REQ_GET_CONFIGURATION, - USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + usb_rcvctrlpipe(udev, 0), + MTOUCHUSB_RESET, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, 0, - 0x81, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT); - dbg("%s - usb_control_msg - USB_REQ_GET_CONFIGURATION - bytes|err: %d", + dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d", __FUNCTION__, nRet); dbg("%s - usb_alloc_urb: mtouch->irq", __FUNCTION__); @@ -315,7 +286,7 @@ mtouch->udev, usb_rcvintpipe(mtouch->udev, 0x81), mtouch->data, - MTOUCHUSB_REPORT_SIZE_DATA, + MTOUCHUSB_REPORT_DATA_SIZE, mtouchusb_irq, mtouch, endpoint->bInterval); @@ -324,15 +295,15 @@ input_register_device(&mtouch->input); nRet = usb_control_msg(mtouch->udev, - usb_rcvctrlpipe(udev, 0x80), - MTOUCHUSB_ASYC_REPORT, + usb_rcvctrlpipe(udev, 0), + MTOUCHUSB_ASYNC_REPORT, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - MTOUCHUSB_ASYC_REPORT, - MTOUCHUSB_ASYC_REPORT, + 1, + 1, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT); - dbg("%s - usb_control_msg - MTOUCHUSB_ASYC_REPORT - bytes|err: %d", + dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d", __FUNCTION__, nRet); printk(KERN_INFO "input: %s on %s\n", mtouch->name, path); @@ -383,9 +354,3 @@ MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); MODULE_LICENSE("GPL"); -MODULE_PARM(vendor, "i"); -MODULE_PARM_DESC(vendor, "User specified USB idVendor"); -MODULE_PARM(product, "i"); -MODULE_PARM_DESC(product, "User specified USB idProduct"); - - 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 Sun May 9 21:32:55 2004 @@ -0,0 +1,310 @@ +/****************************************************************************** + * touchkitusb.c -- Driver for eGalax TouchKit USB Touchscreens + * + * Copyright (C) 2004 by Daniel Ritz + * Copyright (C) by Todd E. Johnson (mtouchusb.c) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Based upon mtouchusb.c + * + *****************************************************************************/ + +//#define DEBUG + +#include +#include +#include +#include +#include +#include + +#if !defined(DEBUG) && defined(CONFIG_USB_DEBUG) +#define DEBUG +#endif +#include + + +#define TOUCHKIT_MIN_XC 0x0 +#define TOUCHKIT_MAX_XC 0x07ff +#define TOUCHKIT_XC_FUZZ 0x0 +#define TOUCHKIT_XC_FLAT 0x0 +#define TOUCHKIT_MIN_YC 0x0 +#define TOUCHKIT_MAX_YC 0x07ff +#define TOUCHKIT_YC_FUZZ 0x0 +#define TOUCHKIT_YC_FLAT 0x0 +#define TOUCHKIT_REPORT_DATA_SIZE 8 + +#define TOUCHKIT_DOWN 0x01 +#define TOUCHKIT_POINT_TOUCH 0x81 +#define TOUCHKIT_POINT_NOTOUCH 0x80 + +#define TOUCHKIT_GET_TOUCHED(dat) ((((dat)[0]) & TOUCHKIT_DOWN) ? 1 : 0) +#define TOUCHKIT_GET_X(dat) (((dat)[3] << 7) | (dat)[4]) +#define TOUCHKIT_GET_Y(dat) (((dat)[1] << 7) | (dat)[2]) + +#define DRIVER_VERSION "v0.1" +#define DRIVER_AUTHOR "Daniel Ritz " +#define DRIVER_DESC "eGalax TouchKit USB HID Touchscreen Driver" + +struct touchkit_usb { + unsigned char *data; + dma_addr_t data_dma; + struct urb *irq; + struct usb_device *udev; + struct input_dev input; + int open; + char name[128]; + char phys[64]; +}; + +static struct usb_device_id touchkit_devices[] = { + {USB_DEVICE(0x3823, 0x0001)}, + {USB_DEVICE(0x0eef, 0x0001)}, + {} +}; + +static void touchkit_irq(struct urb *urb, struct pt_regs *regs) +{ + struct touchkit_usb *touchkit = urb->context; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ETIMEDOUT: + /* this urb is timing out */ + dbg("%s - urb timed out - was the device unplugged?", + __FUNCTION__); + return; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + + input_regs(&touchkit->input, regs); + input_report_key(&touchkit->input, BTN_TOUCH, + TOUCHKIT_GET_TOUCHED(touchkit->data)); + input_report_abs(&touchkit->input, ABS_X, + TOUCHKIT_GET_X(touchkit->data)); + input_report_abs(&touchkit->input, ABS_Y, + TOUCHKIT_GET_Y(touchkit->data)); + input_sync(&touchkit->input); + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err("%s - usb_submit_urb failed with result: %d", + __FUNCTION__, retval); +} + +static int touchkit_open(struct input_dev *input) +{ + struct touchkit_usb *touchkit = input->private; + + if (touchkit->open++) + return 0; + + touchkit->irq->dev = touchkit->udev; + + if (usb_submit_urb(touchkit->irq, GFP_ATOMIC)) { + touchkit->open--; + return -EIO; + } + + return 0; +} + +static void touchkit_close(struct input_dev *input) +{ + struct touchkit_usb *touchkit = input->private; + + if (!--touchkit->open) + usb_unlink_urb(touchkit->irq); +} + +static int touchkit_alloc_buffers(struct usb_device *udev, + struct touchkit_usb *touchkit) +{ + touchkit->data = usb_buffer_alloc(udev, TOUCHKIT_REPORT_DATA_SIZE, + SLAB_ATOMIC, &touchkit->data_dma); + + if (!touchkit->data) + return -1; + + return 0; +} + +static void touchkit_free_buffers(struct usb_device *udev, + struct touchkit_usb *touchkit) +{ + if (touchkit->data) + usb_buffer_free(udev, TOUCHKIT_REPORT_DATA_SIZE, + touchkit->data, touchkit->data_dma); +} + +static int touchkit_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret; + struct touchkit_usb *touchkit; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_device *udev = interface_to_usbdev(intf); + char path[64]; + char *buf; + + interface = intf->cur_altsetting; + endpoint = &interface->endpoint[0].desc; + + touchkit = kmalloc(sizeof(struct touchkit_usb), GFP_KERNEL); + if (!touchkit) + return -ENOMEM; + + memset(touchkit, 0, sizeof(struct touchkit_usb)); + touchkit->udev = udev; + + if (touchkit_alloc_buffers(udev, touchkit)) { + ret = -ENOMEM; + goto out_free; + } + + touchkit->input.private = touchkit; + touchkit->input.open = touchkit_open; + touchkit->input.close = touchkit_close; + + usb_make_path(udev, path, 64); + sprintf(touchkit->phys, "%s/input0", path); + + touchkit->input.name = touchkit->name; + touchkit->input.phys = touchkit->phys; + touchkit->input.id.bustype = BUS_USB; + touchkit->input.id.vendor = udev->descriptor.idVendor; + touchkit->input.id.product = udev->descriptor.idProduct; + touchkit->input.id.version = udev->descriptor.bcdDevice; + touchkit->input.dev = &intf->dev; + + touchkit->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + touchkit->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + touchkit->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + + /* Used to Scale Compensated Data */ + touchkit->input.absmin[ABS_X] = TOUCHKIT_MIN_XC; + touchkit->input.absmax[ABS_X] = TOUCHKIT_MAX_XC; + touchkit->input.absfuzz[ABS_X] = TOUCHKIT_XC_FUZZ; + touchkit->input.absflat[ABS_X] = TOUCHKIT_XC_FLAT; + touchkit->input.absmin[ABS_Y] = TOUCHKIT_MIN_YC; + touchkit->input.absmax[ABS_Y] = TOUCHKIT_MAX_YC; + touchkit->input.absfuzz[ABS_Y] = TOUCHKIT_YC_FUZZ; + touchkit->input.absflat[ABS_Y] = TOUCHKIT_YC_FLAT; + + buf = kmalloc(63, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out_free_buffers; + } + + if (udev->descriptor.iManufacturer && + usb_string(udev, udev->descriptor.iManufacturer, buf, 63) > 0) + strcat(touchkit->name, buf); + if (udev->descriptor.iProduct && + usb_string(udev, udev->descriptor.iProduct, buf, 63) > 0) + sprintf(touchkit->name, "%s %s", touchkit->name, buf); + + if (!strlen(touchkit->name)) + sprintf(touchkit->name, "USB Touchscreen %04x:%04x", + touchkit->input.id.vendor, touchkit->input.id.product); + + kfree(buf); + + touchkit->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!touchkit->irq) { + dbg("%s - usb_alloc_urb failed: touchkit->irq", __FUNCTION__); + ret = -ENOMEM; + goto out_free_buffers; + } + + usb_fill_int_urb(touchkit->irq, touchkit->udev, + usb_rcvintpipe(touchkit->udev, 0x81), + touchkit->data, TOUCHKIT_REPORT_DATA_SIZE, + touchkit_irq, touchkit, endpoint->bInterval); + + input_register_device(&touchkit->input); + + printk(KERN_INFO "input: %s on %s\n", touchkit->name, path); + usb_set_intfdata(intf, touchkit); + + return 0; + +out_free_buffers: + touchkit_free_buffers(udev, touchkit); +out_free: + kfree(touchkit); + return ret; +} + +static void touchkit_disconnect(struct usb_interface *intf) +{ + struct touchkit_usb *touchkit = usb_get_intfdata(intf); + + dbg("%s - called", __FUNCTION__); + + if (!touchkit) + return; + + dbg("%s - touchkit is initialized, cleaning up", __FUNCTION__); + usb_set_intfdata(intf, NULL); + input_unregister_device(&touchkit->input); + usb_unlink_urb(touchkit->irq); + usb_free_urb(touchkit->irq); + touchkit_free_buffers(interface_to_usbdev(intf), touchkit); + kfree(touchkit); +} + +MODULE_DEVICE_TABLE(usb, touchkit_devices); + +static struct usb_driver touchkit_driver = { + .owner = THIS_MODULE, + .name = "touchkitusb", + .probe = touchkit_probe, + .disconnect = touchkit_disconnect, + .id_table = touchkit_devices, +}; + +static int __init touchkit_init(void) +{ + return usb_register(&touchkit_driver); +} + +static void __exit touchkit_cleanup(void) +{ + usb_deregister(&touchkit_driver); +} + +module_init(touchkit_init); +module_exit(touchkit_cleanup); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/usb/media/dsbr100.c b/drivers/usb/media/dsbr100.c --- a/drivers/usb/media/dsbr100.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/media/dsbr100.c Sun May 9 21:32:55 2004 @@ -33,6 +33,9 @@ History: + Version 0.40: + Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing + Version 0.30: Markus: Updates for 2.5.x kernel and more ISO compliant source @@ -75,13 +78,17 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.30" +#define DRIVER_VERSION "v0.40" #define DRIVER_AUTHOR "Markus Demleitner " #define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver" #define DSB100_VENDOR 0x04b4 #define DSB100_PRODUCT 0x1002 +/* Commands the device appears to understand */ +#define DSB100_TUNE 1 +#define DSB100_ONOFF 2 + #define TB_LEN 16 /* Frequency limits in MHz -- these are European values. For Japanese @@ -102,15 +109,19 @@ static int radio_nr = -1; MODULE_PARM(radio_nr, "i"); -typedef struct -{ - struct usb_device *dev; +/* Data for one (physical) device */ +typedef struct { + struct usb_device *usbdev; + struct video_device *videodev; unsigned char transfer_buffer[TB_LEN]; int curfreq; int stereo; -} usb_dsbr100; + int users; + int removed; +} dsbr100_device; +/* File system interface */ static struct file_operations usb_dsbr100_fops = { .owner = THIS_MODULE, .open = usb_dsbr100_open, @@ -118,65 +129,84 @@ .ioctl = usb_dsbr100_ioctl, .llseek = no_llseek, }; -static struct video_device usb_dsbr100_radio= + +/* V4L interface */ +static struct video_device dsbr100_videodev_template= { .owner = THIS_MODULE, .name = "D-Link DSB-R 100", .type = VID_TYPE_TUNER, .hardware = VID_HARDWARE_AZTECH, .fops = &usb_dsbr100_fops, + .release = video_device_release, }; -static int users = 0; - -static struct usb_device_id usb_dsbr100_table [] = { +static struct usb_device_id usb_dsbr100_device_table [] = { { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, usb_dsbr100_table); +MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); +/* USB subsystem interface */ static struct usb_driver usb_dsbr100_driver = { .owner = THIS_MODULE, .name = "dsbr100", .probe = usb_dsbr100_probe, .disconnect = usb_dsbr100_disconnect, - .id_table = usb_dsbr100_table, + .id_table = usb_dsbr100_device_table, }; +/* Low-level device interface begins here */ -static int dsbr100_start(usb_dsbr100 *radio) +/* switch on radio */ +static int dsbr100_start(dsbr100_device *radio) { - if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 || - usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300)<0) + if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_ONOFF, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x01, 0x00, radio->transfer_buffer, 8, 300)<0) return -1; return (radio->transfer_buffer)[0]; } -static int dsbr100_stop(usb_dsbr100 *radio) +/* switch off radio */ +static int dsbr100_stop(dsbr100_device *radio) { - if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 || - usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300)<0) + if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_ONOFF, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0x00, radio->transfer_buffer, 8, 300)<0) return -1; return (radio->transfer_buffer)[0]; } - -static int dsbr100_setfreq(usb_dsbr100 *radio, int freq) +/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ +static int dsbr100_setfreq(dsbr100_device *radio, int freq) { freq = (freq/16*80)/1000+856; - if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x01, 0xC0, (freq>>8)&0x00ff, freq&0xff, - radio->transfer_buffer, 8, 300)<0 || - usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 || - usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), - 0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) { + if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_TUNE, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + (freq>>8)&0x00ff, freq&0xff, + radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) { radio->stereo = -1; return -1; } @@ -184,61 +214,92 @@ 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; + struct video_device *videodev; - 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 +309,7 @@ } case VIDIOCGTUNER: { struct video_tuner *v = arg; + dsbr100_getstat(radio); if(v->tuner) /* Only 1 tuner */ return -EINVAL; @@ -263,21 +325,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 +349,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 +360,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 +385,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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/media/ibmcam.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/media/konicawc.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/media/ov511.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/media/pwc-if.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/media/se401.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/media/ultracam.c Sun May 9 21:32:55 2004 @@ -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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/media/vicam.c Sun May 9 21:32:55 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/misc/Kconfig b/drivers/usb/misc/Kconfig --- a/drivers/usb/misc/Kconfig Sun May 9 21:32:55 2004 +++ b/drivers/usb/misc/Kconfig Sun May 9 21:32:55 2004 @@ -133,6 +133,18 @@ To compile this driver as a module, choose M here: the module will be called speedtch. +config USB_PHIDGETSERVO + tristate "USB PhidgetServo support" + depends on USB + help + Say Y here if you want to connect an 1 or 4 Motor PhidgetServo + servo controller version 2.0 or 3.0. + + Phidgets Inc. has a web page at . + + To compile this driver as a module, choose M here: the + module will be called phidgetservo. + config USB_TEST tristate "USB testing driver (DEVELOPMENT)" depends on USB && USB_DEVICEFS && EXPERIMENTAL diff -Nru a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile --- a/drivers/usb/misc/Makefile Sun May 9 21:32:55 2004 +++ b/drivers/usb/misc/Makefile Sun May 9 21:32:55 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/legousbtower.c b/drivers/usb/misc/legousbtower.c --- a/drivers/usb/misc/legousbtower.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/misc/legousbtower.c Sun May 9 21:32:55 2004 @@ -1,8 +1,8 @@ /* * LEGO USB Tower driver * - * Copyright (C) 2003 David Glance - * 2001 Juergen Stuber + * Copyright (C) 2003 David Glance + * 2001-2004 Juergen Stuber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -33,14 +33,44 @@ * - changed to use lego0 rather than tower0 * - changed dbg() to use __func__ rather than deprecated __FUNCTION__ * 2003-01-12 - 0.53 david (david@csse.uwa.edu.au) - * - changed read and write to write everything or timeout (from a patch by Chris Riesen and - * Brett Thaeler driver) + * - changed read and write to write everything or + * timeout (from a patch by Chris Riesen and Brett Thaeler driver) * - added ioctl functionality to set timeouts - * 2003-07-18 - 0.54 davidgsf (david@csse.uwa.edu.au) + * 2003-07-18 - 0.54 davidgsf (david@csse.uwa.edu.au) * - initial import into LegoUSB project * - merge of existing LegoUSB.c driver - * 2003-07-18 - 0.56 davidgsf (david@csse.uwa.edu.au) + * 2003-07-18 - 0.56 davidgsf (david@csse.uwa.edu.au) * - port to 2.6 style driver + * 2004-02-29 - 0.6 Juergen Stuber + * - fix locking + * - unlink read URBs which are no longer needed + * - allow increased buffer size, eliminates need for timeout on write + * - have read URB running continuously + * - added poll + * - forbid seeking + * - added nonblocking I/O + * - changed back __func__ to __FUNCTION__ + * - read and log tower firmware version + * - reset tower on probe, avoids failure of first write + * 2004-03-09 - 0.7 Juergen Stuber + * - timeout read now only after inactivity, shorten default accordingly + * 2004-03-11 - 0.8 Juergen Stuber + * - log major, minor instead of possibly confusing device filename + * - whitespace cleanup + * 2004-03-12 - 0.9 Juergen Stuber + * - normalize whitespace in debug messages + * - take care about endianness in control message responses + * 2004-03-13 - 0.91 Juergen Stuber + * - make default intervals longer to accommodate current EHCI driver + * 2004-03-19 - 0.92 Juergen Stuber + * - replaced atomic_t by memory barriers + * 2004-04-21 - 0.93 Juergen Stuber + * - wait for completion of write urb in release (needed for remotecontrol) + * - corrected poll for write direction (missing negation) + * 2004-04-22 - 0.94 Juergen Stuber + * - make device locking interruptible + * 2004-04-30 - 0.95 Juergen Stuber + * - check for valid udev on resubmitting and unlinking urbs */ #include @@ -53,33 +83,113 @@ #include #include #include +#include #ifdef CONFIG_USB_DEBUG static int debug = 4; #else - static int debug = 1; + static int debug = 0; #endif /* Use our own dbg macro */ #undef dbg -#define dbg(lvl, format, arg...) do { if (debug >= lvl) printk(KERN_DEBUG __FILE__ " : " format " \n", ## arg); } while (0) +#define dbg(lvl, format, arg...) do { if (debug >= lvl) printk(KERN_DEBUG __FILE__ ": " format "\n", ## arg); } while (0) /* Version Information */ -#define DRIVER_VERSION "v0.56" -#define DRIVER_AUTHOR "David Glance, davidgsf@sourceforge.net" +#define DRIVER_VERSION "v0.95" +#define DRIVER_AUTHOR "Juergen Stuber " #define DRIVER_DESC "LEGO USB Tower Driver" -/* Module paramaters */ +/* Module parameters */ MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Debug enabled or not"); +/* The defaults are chosen to work with the latest versions of leJOS and NQC. + */ + +/* Some legacy software likes to receive packets in one piece. + * In this case read_buffer_size should exceed the maximal packet length + * (417 for datalog uploads), and packet_timeout should be set. + */ +static size_t read_buffer_size = 480; +MODULE_PARM(read_buffer_size, "i"); +MODULE_PARM_DESC(read_buffer_size, "Read buffer size"); + +/* Some legacy software likes to send packets in one piece. + * In this case write_buffer_size should exceed the maximal packet length + * (417 for firmware and program downloads). + * A problem with long writes is that the following read may time out + * if the software is not prepared to wait long enough. + */ +static size_t write_buffer_size = 480; +MODULE_PARM(write_buffer_size, "i"); +MODULE_PARM_DESC(write_buffer_size, "Write buffer size"); + +/* Some legacy software expects reads to contain whole LASM packets. + * To achieve this, characters which arrive before a packet timeout + * occurs will be returned in a single read operation. + * A problem with long reads is that the software may time out + * if it is not prepared to wait long enough. + * The packet timeout should be greater than the time between the + * reception of subsequent characters, which should arrive about + * every 5ms for the standard 2400 baud. + * Set it to 0 to disable. + */ +static int packet_timeout = 50; +MODULE_PARM(packet_timeout, "i"); +MODULE_PARM_DESC(packet_timeout, "Packet timeout in ms"); + +/* Some legacy software expects blocking reads to time out. + * Timeout occurs after the specified time of read and write inactivity. + * Set it to 0 to disable. + */ +static int read_timeout = 200; +MODULE_PARM(read_timeout, "i"); +MODULE_PARM_DESC(read_timeout, "Read timeout in ms"); + +/* As of kernel version 2.6.4 ehci-hcd uses an + * "only one interrupt transfer per frame" shortcut + * to simplify the scheduling of periodic transfers. + * This conflicts with our standard 1ms intervals for in and out URBs. + * We use default intervals of 2ms for in and 8ms for out transfers, + * which is fast enough for 2400 baud and allows a small additional load. + * Increase the interval to allow more devices that do interrupt transfers, + * or set to 0 to use the standard interval from the endpoint descriptors. + */ +static int interrupt_in_interval = 2; +MODULE_PARM(interrupt_in_interval, "i"); +MODULE_PARM_DESC(interrupt_in_interval, "Interrupt in interval in ms"); + +static int interrupt_out_interval = 8; +MODULE_PARM(interrupt_out_interval, "i"); +MODULE_PARM_DESC(interrupt_out_interval, "Interrupt out interval in ms"); /* Define these values to match your device */ #define LEGO_USB_TOWER_VENDOR_ID 0x0694 #define LEGO_USB_TOWER_PRODUCT_ID 0x0001 +/* Vendor requests */ +#define LEGO_USB_TOWER_REQUEST_RESET 0x04 +#define LEGO_USB_TOWER_REQUEST_GET_VERSION 0xFD + +struct tower_reset_reply { + __u16 size; /* little-endian */ + __u8 err_code; + __u8 spare; +} __attribute__ ((packed)); + +struct tower_get_version_reply { + __u16 size; /* little-endian */ + __u8 err_code; + __u8 spare; + __u8 major; + __u8 minor; + __u16 build_no; /* little-endian */ +} __attribute__ ((packed)); + + /* table of devices that work with this driver */ static struct usb_device_id tower_table [] = { { USB_DEVICE(LEGO_USB_TOWER_VENDOR_ID, LEGO_USB_TOWER_PRODUCT_ID) }, @@ -90,22 +200,21 @@ #define LEGO_USB_TOWER_MINOR_BASE 160 -/* we can have up to this number of device plugged in at once */ -#define MAX_DEVICES 16 - -#define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */ /* Structure to hold all of our device specific stuff */ struct lego_usb_tower { struct semaphore sem; /* locks this structure */ - struct usb_device* udev; /* save off the usb device pointer */ - struct usb_interface* interface; + struct usb_device* udev; /* save off the usb device pointer */ unsigned char minor; /* the starting minor number for this device */ int open_count; /* number of times this port has been opened */ char* read_buffer; - int read_buffer_length; + size_t read_buffer_length; /* this much came in */ + size_t read_packet_length; /* this much will be returned on read */ + spinlock_t read_buffer_lock; + int packet_timeout_jiffies; + unsigned long read_last_arrival; wait_queue_head_t read_wait; wait_queue_head_t write_wait; @@ -113,18 +222,18 @@ char* interrupt_in_buffer; struct usb_endpoint_descriptor* interrupt_in_endpoint; struct urb* interrupt_in_urb; + int interrupt_in_interval; + int interrupt_in_running; + int interrupt_in_done; char* interrupt_out_buffer; struct usb_endpoint_descriptor* interrupt_out_endpoint; struct urb* interrupt_out_urb; + int interrupt_out_interval; + int interrupt_out_busy; }; -/* Note that no locking is needed: - * read_buffer is arbitrated by read_buffer_length == 0 - * interrupt_out_buffer is arbitrated by interrupt_out_urb->status == -EINPROGRESS - * interrupt_in_buffer belongs to urb alone and is overwritten on overflow - */ /* local function prototypes */ static ssize_t tower_read (struct file *file, char *buffer, size_t count, loff_t *ppos); @@ -132,8 +241,11 @@ static inline void tower_delete (struct lego_usb_tower *dev); static int tower_open (struct inode *inode, struct file *file); static int tower_release (struct inode *inode, struct file *file); -static int tower_release_internal (struct lego_usb_tower *dev); +static unsigned int tower_poll (struct file *file, poll_table *wait); +static loff_t tower_llseek (struct file *file, loff_t off, int whence); + static void tower_abort_transfers (struct lego_usb_tower *dev); +static void tower_check_for_read_packet (struct lego_usb_tower *dev); static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs); static void tower_interrupt_out_callback (struct urb *urb, struct pt_regs *regs); @@ -146,14 +258,16 @@ /* file operations needed when we register this driver */ static struct file_operations tower_fops = { - .owner = THIS_MODULE, - .read = tower_read, + .owner = THIS_MODULE, + .read = tower_read, .write = tower_write, .open = tower_open, - .release = tower_release, + .release = tower_release, + .poll = tower_poll, + .llseek = tower_llseek, }; -/* +/* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with devfs and the driver core */ @@ -167,11 +281,11 @@ /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver tower_driver = { - .owner = THIS_MODULE, - .name = "legousbtower", - .probe = tower_probe, - .disconnect = tower_disconnect, - .id_table = tower_table, + .owner = THIS_MODULE, + .name = "legousbtower", + .probe = tower_probe, + .disconnect = tower_disconnect, + .id_table = tower_table, }; @@ -183,8 +297,8 @@ int i; if (debug < level) - return; - + return; + printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", function, size); for (i = 0; i < size; ++i) { printk ("%.2x ", data[i]); @@ -198,7 +312,7 @@ */ static inline void tower_delete (struct lego_usb_tower *dev) { - dbg(2, "%s enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); tower_abort_transfers (dev); @@ -214,7 +328,7 @@ kfree (dev->interrupt_out_buffer); kfree (dev); - dbg(2, "%s : leave", __func__); + dbg(2, "%s: leave", __FUNCTION__); } @@ -228,7 +342,7 @@ int retval = 0; struct usb_interface *interface; - dbg(2,"%s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); subminor = iminor(inode); @@ -240,37 +354,63 @@ err ("%s - error, can't find device for minor %d", __FUNCTION__, subminor); retval = -ENODEV; - goto exit_no_device; + goto unlock_disconnect_exit; } dev = usb_get_intfdata(interface); if (!dev) { retval = -ENODEV; - goto exit_no_device; + goto unlock_disconnect_exit; } /* lock this device */ - down (&dev->sem); - - - /* increment our usage count for the device */ - ++dev->open_count; - - /* save device in the file's private structure */ - file->private_data = dev; + if (down_interruptible (&dev->sem)) { + retval = -ERESTARTSYS; + goto unlock_disconnect_exit; + } + /* allow opening only once */ + if (dev->open_count) { + retval = -EBUSY; + goto unlock_exit; + } + dev->open_count = 1; /* initialize in direction */ dev->read_buffer_length = 0; + dev->read_packet_length = 0; + usb_fill_int_urb (dev->interrupt_in_urb, + dev->udev, + usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), + dev->interrupt_in_buffer, + dev->interrupt_in_endpoint->wMaxPacketSize, + tower_interrupt_in_callback, + dev, + dev->interrupt_in_interval); + + dev->interrupt_in_running = 1; + dev->interrupt_in_done = 0; + mb(); - up (&dev->sem); + retval = usb_submit_urb (dev->interrupt_in_urb, GFP_KERNEL); + if (retval) { + err("Couldn't submit interrupt_in_urb %d", retval); + dev->interrupt_in_running = 0; + dev->open_count = 0; + goto unlock_exit; + } -exit_no_device: + /* save device in the file's private structure */ + file->private_data = dev; +unlock_exit: + up (&dev->sem); + +unlock_disconnect_exit: up (&disconnect_sem); - dbg(2,"%s : leave, return value %d ", __func__, retval); + dbg(2, "%s: leave, return value %d ", __FUNCTION__, retval); return retval; } @@ -283,87 +423,137 @@ struct lego_usb_tower *dev; int retval = 0; - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); dev = (struct lego_usb_tower *)file->private_data; if (dev == NULL) { - dbg(1," %s : object is NULL", __func__); + dbg(1, "%s: object is NULL", __FUNCTION__); retval = -ENODEV; goto exit; } + if (down_interruptible (&dev->sem)) { + retval = -ERESTARTSYS; + goto exit; + } - /* lock our device */ - down (&dev->sem); - - if (dev->open_count <= 0) { - dbg(1," %s : device not opened", __func__); + if (dev->open_count != 1) { + dbg(1, "%s: device not opened exactly once", __FUNCTION__); retval = -ENODEV; + goto unlock_exit; + } + if (dev->udev == NULL) { + /* the device was unplugged before the file was released */ + up (&dev->sem); /* unlock here as tower_delete frees dev */ + tower_delete (dev); goto exit; } - /* do the work */ - retval = tower_release_internal (dev); + /* wait until write transfer is finished */ + if (dev->interrupt_out_busy) { + wait_event_interruptible_timeout (dev->write_wait, !dev->interrupt_out_busy, 2 * HZ); + } + tower_abort_transfers (dev); + dev->open_count = 0; -exit: +unlock_exit: up (&dev->sem); - dbg(2," %s : leave, return value %d", __func__, retval); + +exit: + dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval; } /** - * tower_release_internal + * tower_abort_transfers + * aborts transfers and frees associated data structures */ -static int tower_release_internal (struct lego_usb_tower *dev) +static void tower_abort_transfers (struct lego_usb_tower *dev) { - int retval = 0; + dbg(2, "%s: enter", __FUNCTION__); - dbg(2," %s : enter", __func__); - - if (dev->udev == NULL) { - /* the device was unplugged before the file was released */ - tower_delete (dev); + if (dev == NULL) { + dbg(1, "%s: dev is null", __FUNCTION__); goto exit; } - /* decrement our usage count for the device */ - --dev->open_count; - if (dev->open_count <= 0) { - tower_abort_transfers (dev); - dev->open_count = 0; + /* shutdown transfer */ + if (dev->interrupt_in_running) { + dev->interrupt_in_running = 0; + mb(); + if (dev->interrupt_in_urb != NULL && dev->udev) { + usb_unlink_urb (dev->interrupt_in_urb); + } + } + if (dev->interrupt_out_busy) { + if (dev->interrupt_out_urb != NULL && dev->udev) { + usb_unlink_urb (dev->interrupt_out_urb); + } } exit: - dbg(2," %s : leave", __func__); - return retval; + dbg(2, "%s: leave", __FUNCTION__); } /** - * tower_abort_transfers - * aborts transfers and frees associated data structures + * tower_check_for_read_packet + * + * To get correct semantics for signals and non-blocking I/O + * with packetizing we pretend not to see any data in the read buffer + * until it has been there unchanged for at least + * dev->packet_timeout_jiffies, or until the buffer is full. */ -static void tower_abort_transfers (struct lego_usb_tower *dev) +static void tower_check_for_read_packet (struct lego_usb_tower *dev) { - dbg(2," %s : enter", __func__); - - if (dev == NULL) { - dbg(1," %s : dev is null", __func__); - goto exit; + spin_lock_irq (&dev->read_buffer_lock); + if (!packet_timeout + || time_after(jiffies, dev->read_last_arrival + dev->packet_timeout_jiffies) + || dev->read_buffer_length == read_buffer_size) { + dev->read_packet_length = dev->read_buffer_length; } + dev->interrupt_in_done = 0; + spin_unlock_irq (&dev->read_buffer_lock); +} - /* shutdown transfer */ - if (dev->interrupt_in_urb != NULL) { - usb_unlink_urb (dev->interrupt_in_urb); + +/** + * tower_poll + */ +static unsigned int tower_poll (struct file *file, poll_table *wait) +{ + struct lego_usb_tower *dev; + unsigned int mask = 0; + + dbg(2, "%s: enter", __FUNCTION__); + + dev = file->private_data; + + poll_wait(file, &dev->read_wait, wait); + poll_wait(file, &dev->write_wait, wait); + + tower_check_for_read_packet(dev); + if (dev->read_packet_length > 0) { + mask |= POLLIN | POLLRDNORM; } - if (dev->interrupt_out_urb != NULL) { - usb_unlink_urb (dev->interrupt_out_urb); + if (!dev->interrupt_out_busy) { + mask |= POLLOUT | POLLWRNORM; } -exit: - dbg(2," %s : leave", __func__); + dbg(2, "%s: leave, mask = %d", __FUNCTION__, mask); + + return mask; +} + + +/** + * tower_llseek + */ +static loff_t tower_llseek (struct file *file, loff_t off, int whence) +{ + return -ESPIPE; /* unseekable */ } @@ -373,96 +563,87 @@ static ssize_t tower_read (struct file *file, char *buffer, size_t count, loff_t *ppos) { struct lego_usb_tower *dev; - size_t bytes_read = 0; size_t bytes_to_read; int i; int retval = 0; - int timeout = 0; + unsigned long timeout = 0; - dbg(2," %s : enter, count = %Zd", __func__, count); + dbg(2, "%s: enter, count = %Zd", __FUNCTION__, count); dev = (struct lego_usb_tower *)file->private_data; - + /* lock this object */ - down (&dev->sem); + if (down_interruptible (&dev->sem)) { + retval = -ERESTARTSYS; + goto exit; + } /* verify that the device wasn't unplugged */ if (dev->udev == NULL) { retval = -ENODEV; err("No device or device unplugged %d", retval); - goto exit; + goto unlock_exit; } /* verify that we actually have some data to read */ if (count == 0) { - dbg(1," %s : read request of 0 bytes", __func__); - goto exit; + dbg(1, "%s: read request of 0 bytes", __FUNCTION__); + goto unlock_exit; } + if (read_timeout) { + timeout = jiffies + read_timeout * HZ / 1000; + } + + /* wait for data */ + tower_check_for_read_packet (dev); + while (dev->read_packet_length == 0) { + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto unlock_exit; + } + retval = wait_event_interruptible_timeout(dev->read_wait, dev->interrupt_in_done, dev->packet_timeout_jiffies); + if (retval < 0) { + goto unlock_exit; + } - timeout = COMMAND_TIMEOUT; + /* reset read timeout during read or write activity */ + if (read_timeout + && (dev->read_buffer_length || dev->interrupt_out_busy)) { + timeout = jiffies + read_timeout * HZ / 1000; + } + /* check for read timeout */ + if (read_timeout && time_after (jiffies, timeout)) { + retval = -ETIMEDOUT; + goto unlock_exit; + } + tower_check_for_read_packet (dev); + } - while (1) { - if (dev->read_buffer_length == 0) { + /* copy the data from read_buffer into userspace */ + bytes_to_read = min(count, dev->read_packet_length); - /* start reading */ - usb_fill_int_urb (dev->interrupt_in_urb,dev->udev, - usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), - dev->interrupt_in_buffer, - dev->interrupt_in_endpoint->wMaxPacketSize, - tower_interrupt_in_callback, - dev, - dev->interrupt_in_endpoint->bInterval); - - retval = usb_submit_urb (dev->interrupt_in_urb, GFP_KERNEL); - - if (retval < 0) { - err("Couldn't submit interrupt_in_urb"); - goto exit; - } - - if (timeout <= 0) { - retval = -ETIMEDOUT; - goto exit; - } - - if (signal_pending(current)) { - retval = -EINTR; - goto exit; - } - - up (&dev->sem); - timeout = interruptible_sleep_on_timeout (&dev->read_wait, timeout); - down (&dev->sem); + if (copy_to_user (buffer, dev->read_buffer, bytes_to_read)) { + retval = -EFAULT; + goto unlock_exit; + } - } else { - /* copy the data from read_buffer into userspace */ - bytes_to_read = count > dev->read_buffer_length ? dev->read_buffer_length : count; - if (copy_to_user (buffer, dev->read_buffer, bytes_to_read) != 0) { - retval = -EFAULT; - goto exit; - } - dev->read_buffer_length -= bytes_to_read; - for (i=0; iread_buffer_length; i++) { - dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read]; - } - - buffer += bytes_to_read; - count -= bytes_to_read; - bytes_read += bytes_to_read; - if (count == 0) { - break; - } - } + spin_lock_irq (&dev->read_buffer_lock); + dev->read_buffer_length -= bytes_to_read; + dev->read_packet_length -= bytes_to_read; + for (i=0; iread_buffer_length; i++) { + dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read]; } + spin_unlock_irq (&dev->read_buffer_lock); - retval = bytes_read; + retval = bytes_to_read; -exit: +unlock_exit: /* unlock the device */ up (&dev->sem); - dbg(2," %s : leave, return value %d", __func__, retval); +exit: + dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval; } @@ -473,107 +654,80 @@ static ssize_t tower_write (struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct lego_usb_tower *dev; - size_t bytes_written = 0; size_t bytes_to_write; - size_t buffer_size; int retval = 0; - int timeout = 0; - dbg(2," %s : enter, count = %Zd", __func__, count); + dbg(2, "%s: enter, count = %Zd", __FUNCTION__, count); dev = (struct lego_usb_tower *)file->private_data; /* lock this object */ - down (&dev->sem); + if (down_interruptible (&dev->sem)) { + retval = -ERESTARTSYS; + goto exit; + } /* verify that the device wasn't unplugged */ if (dev->udev == NULL) { retval = -ENODEV; err("No device or device unplugged %d", retval); - goto exit; + goto unlock_exit; } /* verify that we actually have some data to write */ if (count == 0) { - dbg(1," %s : write request of 0 bytes", __func__); - goto exit; + dbg(1, "%s: write request of 0 bytes", __FUNCTION__); + goto unlock_exit; } + /* wait until previous transfer is finished */ + while (dev->interrupt_out_busy) { + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto unlock_exit; + } + retval = wait_event_interruptible (dev->write_wait, !dev->interrupt_out_busy); + if (retval) { + goto unlock_exit; + } + } - while (count > 0) { - if (dev->interrupt_out_urb->status == -EINPROGRESS) { - timeout = COMMAND_TIMEOUT; - - while (timeout > 0) { - if (signal_pending(current)) { - dbg(1," %s : interrupted", __func__); - retval = -EINTR; - goto exit; - } - up (&dev->sem); - timeout = interruptible_sleep_on_timeout (&dev->write_wait, timeout); - down (&dev->sem); - if (timeout > 0) { - break; - } - dbg(1," %s : interrupted timeout: %d", __func__, timeout); - } - - - dbg(1," %s : final timeout: %d", __func__, timeout); - - if (timeout == 0) { - dbg(1, "%s - command timed out.", __func__); - retval = -ETIMEDOUT; - goto exit; - } - - dbg(4," %s : in progress, count = %Zd", __func__, count); - } else { - dbg(4," %s : sending, count = %Zd", __func__, count); - - /* write the data into interrupt_out_buffer from userspace */ - buffer_size = dev->interrupt_out_endpoint->wMaxPacketSize; - bytes_to_write = count > buffer_size ? buffer_size : count; - dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd", __func__, buffer_size, count, bytes_to_write); - - if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) { - retval = -EFAULT; - goto exit; - } - - /* send off the urb */ - usb_fill_int_urb(dev->interrupt_out_urb, - dev->udev, - usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress), - dev->interrupt_out_buffer, - bytes_to_write, - tower_interrupt_out_callback, - dev, - dev->interrupt_in_endpoint->bInterval); - - dev->interrupt_out_urb->actual_length = bytes_to_write; - retval = usb_submit_urb (dev->interrupt_out_urb, GFP_KERNEL); - - if (retval < 0) { - err("Couldn't submit interrupt_out_urb %d", retval); - goto exit; - } + /* write the data into interrupt_out_buffer from userspace */ + bytes_to_write = min(count, write_buffer_size); + dbg(4, "%s: count = %Zd, bytes_to_write = %Zd", __FUNCTION__, count, bytes_to_write); + + if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write)) { + retval = -EFAULT; + goto unlock_exit; + } + + /* send off the urb */ + usb_fill_int_urb(dev->interrupt_out_urb, + dev->udev, + usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress), + dev->interrupt_out_buffer, + bytes_to_write, + tower_interrupt_out_callback, + dev, + dev->interrupt_out_interval); - buffer += bytes_to_write; - count -= bytes_to_write; + dev->interrupt_out_busy = 1; + wmb(); - bytes_written += bytes_to_write; - } + retval = usb_submit_urb (dev->interrupt_out_urb, GFP_KERNEL); + if (retval) { + dev->interrupt_out_busy = 0; + err("Couldn't submit interrupt_out_urb %d", retval); + goto unlock_exit; } + retval = bytes_to_write; - retval = bytes_written; - -exit: +unlock_exit: /* unlock the device */ up (&dev->sem); - dbg(2," %s : leave, return value %d", __func__, retval); +exit: + dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval; } @@ -585,39 +739,53 @@ static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs) { struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context; + int retval; - dbg(4," %s : enter, status %d", __func__, urb->status); + dbg(4, "%s: enter, status %d", __FUNCTION__, urb->status); - lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); + lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); - if (urb->status != 0) { - if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) { - dbg(1," %s : nonzero status received: %d", __func__, urb->status); + if (urb->status) { + if (urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN) { + goto exit; + } else { + dbg(1, "%s: nonzero status received: %d", __FUNCTION__, urb->status); + goto resubmit; /* maybe we can recover */ } - goto exit; } - down (&dev->sem); - if (urb->actual_length > 0) { - if (dev->read_buffer_length < (4 * dev->interrupt_in_endpoint->wMaxPacketSize) - (urb->actual_length)) { - - memcpy (dev->read_buffer+dev->read_buffer_length, dev->interrupt_in_buffer, urb->actual_length); - + spin_lock (&dev->read_buffer_lock); + if (dev->read_buffer_length + urb->actual_length < read_buffer_size) { + memcpy (dev->read_buffer + dev->read_buffer_length, + dev->interrupt_in_buffer, + urb->actual_length); dev->read_buffer_length += urb->actual_length; - dbg(1," %s reading %d ", __func__, urb->actual_length); - wake_up_interruptible (&dev->read_wait); - + dev->read_last_arrival = jiffies; + dbg(3, "%s: received %d bytes", __FUNCTION__, urb->actual_length); } else { - dbg(1," %s : read_buffer overflow", __func__); + printk(KERN_WARNING "%s: read_buffer overflow, %d bytes dropped", __FUNCTION__, urb->actual_length); } + spin_unlock (&dev->read_buffer_lock); } - up (&dev->sem); +resubmit: + /* resubmit if we're still running */ + if (dev->interrupt_in_running && dev->udev) { + retval = usb_submit_urb (dev->interrupt_in_urb, GFP_ATOMIC); + if (retval) { + err("%s: usb_submit_urb failed (%d)", __FUNCTION__, retval); + } + } exit: - lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); - dbg(4," %s : leave, status %d", __func__, urb->status); + dev->interrupt_in_done = 1; + wake_up_interruptible (&dev->read_wait); + + lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); + dbg(4, "%s: leave, status %d", __FUNCTION__, urb->status); } @@ -628,22 +796,22 @@ { struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context; - dbg(4," %s : enter, status %d", __func__, urb->status); - lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); + dbg(4, "%s: enter, status %d", __FUNCTION__, urb->status); + lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); - if (urb->status != 0) { - if ((urb->status != -ENOENT) && - (urb->status != -ECONNRESET)) { - dbg(1, " %s :nonzero status received: %d", __func__, urb->status); - } - goto exit; - } + /* sync/async unlink faults aren't errors */ + if (urb->status && !(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) { + dbg(1, "%s - nonzero write bulk status received: %d", + __FUNCTION__, urb->status); + } + dev->interrupt_out_busy = 0; wake_up_interruptible(&dev->write_wait); -exit: - lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); - dbg(4," %s : leave, status %d", __func__, urb->status); + lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); + dbg(4, "%s: leave, status %d", __FUNCTION__, urb->status); } @@ -659,15 +827,18 @@ struct lego_usb_tower *dev = NULL; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor* endpoint; + struct tower_reset_reply reset_reply; + struct tower_get_version_reply get_version_reply; int i; int retval = -ENOMEM; + int result; - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); if (udev == NULL) { info ("udev is NULL."); } - + /* See if the device offered us matches what we can accept */ if ((udev->descriptor.idVendor != LEGO_USB_TOWER_VENDOR_ID) || (udev->descriptor.idProduct != LEGO_USB_TOWER_PRODUCT_ID)) { @@ -691,6 +862,10 @@ dev->read_buffer = NULL; dev->read_buffer_length = 0; + dev->read_packet_length = 0; + spin_lock_init (&dev->read_buffer_lock); + dev->packet_timeout_jiffies = packet_timeout * HZ / 1000; + dev->read_last_arrival = jiffies; init_waitqueue_head (&dev->read_wait); init_waitqueue_head (&dev->write_wait); @@ -698,13 +873,15 @@ dev->interrupt_in_buffer = NULL; dev->interrupt_in_endpoint = NULL; dev->interrupt_in_urb = NULL; + dev->interrupt_in_running = 0; + dev->interrupt_in_done = 0; dev->interrupt_out_buffer = NULL; dev->interrupt_out_endpoint = NULL; dev->interrupt_out_urb = NULL; + dev->interrupt_out_busy = 0; - - iface_desc = &interface->altsetting[0]; + iface_desc = interface->cur_altsetting; /* set up the endpoint information */ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { @@ -714,7 +891,7 @@ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) { dev->interrupt_in_endpoint = endpoint; } - + if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) { dev->interrupt_out_endpoint = endpoint; @@ -729,7 +906,7 @@ goto error; } - dev->read_buffer = kmalloc ((4*dev->interrupt_in_endpoint->wMaxPacketSize), GFP_KERNEL); + dev->read_buffer = kmalloc (read_buffer_size, GFP_KERNEL); if (!dev->read_buffer) { err("Couldn't allocate read_buffer"); goto error; @@ -744,7 +921,7 @@ err("Couldn't allocate interrupt_in_urb"); goto error; } - dev->interrupt_out_buffer = kmalloc (dev->interrupt_out_endpoint->wMaxPacketSize, GFP_KERNEL); + dev->interrupt_out_buffer = kmalloc (write_buffer_size, GFP_KERNEL); if (!dev->interrupt_out_buffer) { err("Couldn't allocate interrupt_out_buffer"); goto error; @@ -753,7 +930,9 @@ if (!dev->interrupt_out_urb) { err("Couldn't allocate interrupt_out_urb"); goto error; - } + } + dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval; + dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval; /* we can register the device now, as it is ready */ usb_set_intfdata (interface, dev); @@ -766,16 +945,50 @@ usb_set_intfdata (interface, NULL); goto error; } - dev->minor = interface->minor; /* let the user know what node this device is now attached to */ - info ("LEGO USB Tower device now attached to /dev/usb/lego%d", (dev->minor - LEGO_USB_TOWER_MINOR_BASE)); + info ("LEGO USB Tower #%d now attached to major %d minor %d", (dev->minor - LEGO_USB_TOWER_MINOR_BASE), USB_MAJOR, dev->minor); + /* reset the tower */ + result = usb_control_msg (udev, + usb_rcvctrlpipe(udev, 0), + LEGO_USB_TOWER_REQUEST_RESET, + USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, + 0, + 0, + &reset_reply, + sizeof(reset_reply), + HZ); + if (result < 0) { + err("LEGO USB Tower reset control request failed"); + retval = result; + goto error; + } + + /* get the firmware version and log it */ + result = usb_control_msg (udev, + usb_rcvctrlpipe(udev, 0), + LEGO_USB_TOWER_REQUEST_GET_VERSION, + USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, + 0, + 0, + &get_version_reply, + sizeof(get_version_reply), + HZ); + if (result < 0) { + err("LEGO USB Tower get version control request failed"); + retval = result; + goto error; + } + info("LEGO USB Tower firmware version is %d.%d build %d", + get_version_reply.major, + get_version_reply.minor, + le16_to_cpu(get_version_reply.build_no)); exit: - dbg(2," %s : leave, return value 0x%.8lx (dev)", __func__, (long) dev); + dbg(2, "%s: leave, return value 0x%.8lx (dev)", __FUNCTION__, (long) dev); return retval; @@ -795,7 +1008,7 @@ struct lego_usb_tower *dev; int minor; - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); down (&disconnect_sem); @@ -823,7 +1036,7 @@ info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE)); - dbg(2," %s : leave", __func__); + dbg(2, "%s: leave", __FUNCTION__); } @@ -836,7 +1049,7 @@ int result; int retval = 0; - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); /* register this driver with the USB subsystem */ result = usb_register(&tower_driver); @@ -849,7 +1062,7 @@ info(DRIVER_DESC " " DRIVER_VERSION); exit: - dbg(2," %s : leave, return value %d", __func__, retval); + dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval; } @@ -860,12 +1073,12 @@ */ static void __exit lego_usb_tower_exit(void) { - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); /* deregister this driver with the USB subsystem */ usb_deregister (&tower_driver); - dbg(2," %s : leave", __func__); + dbg(2, "%s: leave", __FUNCTION__); } module_init (lego_usb_tower_init); diff -Nru a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/misc/phidgetservo.c Sun May 9 21:32:55 2004 @@ -0,0 +1,327 @@ +/* + * USB PhidgetServo driver 1.0 + * + * Copyright (C) 2004 Sean Young + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is a driver for the USB PhidgetServo version 2.0 and 3.0 servo + * controllers available at: http://www.phidgets.com/ + * + * Note that the driver takes input as: degrees.minutes + * -23 < degrees < 203 + * 0 < minutes < 59 + * + * CAUTION: Generally you should use 0 < degrees < 180 as anything else + * is probably beyond the range of your servo and may damage it. + */ + +#include +#ifdef CONFIG_USB_DEBUG +#define DEBUG 1 +#endif +#include +#include +#include +#include +#include +#include + +#define DRIVER_AUTHOR "Sean Young " +#define DRIVER_DESC "USB PhidgetServo Driver" + +#define VENDOR_ID_GLAB 0x06c2 +#define DEVICE_ID_4MOTOR_SERVO_30 0x0038 +#define DEVICE_ID_1MOTOR_SERVO_30 0x0039 + +#define VENDOR_ID_WISEGROUP 0x0925 +#define DEVICE_ID_1MOTOR_SERVO_20 0x8101 +#define DEVICE_ID_4MOTOR_SERVO_20 0x8104 + +static struct usb_device_id id_table[] = { + {USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_4MOTOR_SERVO_30)}, + {USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_1MOTOR_SERVO_30)}, + {USB_DEVICE(VENDOR_ID_WISEGROUP, DEVICE_ID_4MOTOR_SERVO_20)}, + {USB_DEVICE(VENDOR_ID_WISEGROUP, DEVICE_ID_1MOTOR_SERVO_20)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, id_table); + +struct phidget_servo { + struct usb_device *udev; + int version; + int quad_servo; + int pulse[4]; + int degrees[4]; + int minutes[4]; +}; + +static void +change_position_v30(struct phidget_servo *servo, int servo_no, int degrees, + int minutes) +{ + int retval; + unsigned char *buffer; + + buffer = kmalloc(6, GFP_KERNEL); + if (!buffer) { + dev_err(&servo->udev->dev, "%s - out of memory\n", + __FUNCTION__); + return; + } + + /* + * pulse = 0 - 4095 + * angle = 0 - 180 degrees + * + * pulse = angle * 10.6 + 243.8 + */ + servo->pulse[servo_no] = ((degrees*60 + minutes)*106 + 2438*60)/600; + servo->degrees[servo_no]= degrees; + servo->minutes[servo_no]= minutes; + + /* + * The PhidgetServo v3.0 is controlled by sending 6 bytes, + * 4 * 12 bits for each servo. + * + * low = lower 8 bits pulse + * high = higher 4 bits pulse + * + * offset bits + * +---+-----------------+ + * | 0 | low 0 | + * +---+--------+--------+ + * | 1 | high 1 | high 0 | + * +---+--------+--------+ + * | 2 | low 1 | + * +---+-----------------+ + * | 3 | low 2 | + * +---+--------+--------+ + * | 4 | high 3 | high 2 | + * +---+--------+--------+ + * | 5 | low 3 | + * +---+-----------------+ + */ + + buffer[0] = servo->pulse[0] & 0xff; + buffer[1] = (servo->pulse[0] >> 8 & 0x0f) + | (servo->pulse[1] >> 4 & 0xf0); + buffer[2] = servo->pulse[1] & 0xff; + buffer[3] = servo->pulse[2] & 0xff; + buffer[4] = (servo->pulse[2] >> 8 & 0x0f) + | (servo->pulse[3] >> 4 & 0xf0); + buffer[5] = servo->pulse[3] & 0xff; + + dev_dbg(&servo->udev->dev, + "data: %02x %02x %02x %02x %02x %02x\n", + buffer[0], buffer[1], buffer[2], + buffer[3], buffer[4], buffer[5]); + + retval = usb_control_msg(servo->udev, + usb_sndctrlpipe(servo->udev, 0), + 0x09, 0x21, 0x0200, 0x0000, buffer, 6, 2 * HZ); + if (retval != 6) + dev_err(&servo->udev->dev, "retval = %d\n", retval); + kfree(buffer); +} + +static void +change_position_v20(struct phidget_servo *servo, int servo_no, int degrees, + int minutes) +{ + int retval; + unsigned char *buffer; + + buffer = kmalloc(2, GFP_KERNEL); + if (!buffer) { + dev_err(&servo->udev->dev, "%s - out of memory\n", + __FUNCTION__); + return; + } + + /* + * angle = 0 - 180 degrees + * pulse = angle + 23 + */ + servo->pulse[servo_no]= degrees + 23; + servo->degrees[servo_no]= degrees; + servo->minutes[servo_no]= 0; + + /* + * The PhidgetServo v2.0 is controlled by sending two bytes. The + * first byte is the servo number xor'ed with 2: + * + * servo 0 = 2 + * servo 1 = 3 + * servo 2 = 0 + * servo 3 = 1 + * + * The second byte is the position. + */ + + buffer[0] = servo_no ^ 2; + buffer[1] = servo->pulse[servo_no]; + + dev_dbg(&servo->udev->dev, "data: %02x %02x\n", buffer[0], buffer[1]); + + retval = usb_control_msg(servo->udev, + usb_sndctrlpipe(servo->udev, 0), + 0x09, 0x21, 0x0200, 0x0000, buffer, 2, 2 * HZ); + if (retval != 2) + dev_err(&servo->udev->dev, "retval = %d\n", retval); + kfree(buffer); +} + +#define show_set(value) \ +static ssize_t set_servo##value (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + int degrees, minutes; \ + struct usb_interface *intf = to_usb_interface (dev); \ + struct phidget_servo *servo = usb_get_intfdata (intf); \ + \ + minutes = 0; \ + /* must at least convert degrees */ \ + if (sscanf (buf, "%d.%d", °rees, &minutes) < 1) { \ + return -EINVAL; \ + } \ + \ + if (degrees < -23 || degrees > (180 + 23) || \ + minutes < 0 || minutes > 59) { \ + return -EINVAL; \ + } \ + \ + if (servo->version >= 3) \ + change_position_v30 (servo, value, degrees, minutes); \ + else \ + change_position_v20 (servo, value, degrees, minutes); \ + \ + return count; \ +} \ + \ +static ssize_t show_servo##value (struct device *dev, char *buf) \ +{ \ + struct usb_interface *intf = to_usb_interface (dev); \ + struct phidget_servo *servo = usb_get_intfdata (intf); \ + \ + return sprintf (buf, "%d.%02d\n", servo->degrees[value], \ + servo->minutes[value]); \ +} \ +static DEVICE_ATTR(servo##value, S_IWUGO | S_IRUGO, \ + show_servo##value, set_servo##value); + +show_set(0); +show_set(1); +show_set(2); +show_set(3); + +static int +servo_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct phidget_servo *dev = NULL; + + dev = kmalloc(sizeof (struct phidget_servo), GFP_KERNEL); + if (dev == NULL) { + dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__); + return -ENOMEM; + } + memset(dev, 0x00, sizeof (*dev)); + + dev->udev = usb_get_dev(udev); + switch (udev->descriptor.idVendor) { + case VENDOR_ID_WISEGROUP: + dev->version = 2; + break; + case VENDOR_ID_GLAB: + dev->version = 3; + break; + } + switch (udev->descriptor.idProduct) { + case DEVICE_ID_4MOTOR_SERVO_20: + case DEVICE_ID_4MOTOR_SERVO_30: + dev->quad_servo = 1; + break; + case DEVICE_ID_1MOTOR_SERVO_20: + case DEVICE_ID_1MOTOR_SERVO_30: + dev->quad_servo = 0; + break; + } + + usb_set_intfdata(interface, dev); + + device_create_file(&interface->dev, &dev_attr_servo0); + if (dev->quad_servo) { + device_create_file(&interface->dev, &dev_attr_servo1); + device_create_file(&interface->dev, &dev_attr_servo2); + device_create_file(&interface->dev, &dev_attr_servo3); + } + + dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 attached\n", + dev->quad_servo ? 4 : 1, dev->version); + if (dev->version == 2) + dev_info(&interface->dev, + "WARNING: v2.0 not tested! Please report if it works.\n"); + + return 0; +} + +static void +servo_disconnect(struct usb_interface *interface) +{ + struct phidget_servo *dev; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + device_remove_file(&interface->dev, &dev_attr_servo0); + if (dev->quad_servo) { + device_remove_file(&interface->dev, &dev_attr_servo1); + device_remove_file(&interface->dev, &dev_attr_servo2); + device_remove_file(&interface->dev, &dev_attr_servo3); + } + + usb_put_dev(dev->udev); + + kfree(dev); + + dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 detached\n", + dev->quad_servo ? 4 : 1, dev->version); +} + +static struct usb_driver servo_driver = { + .owner = THIS_MODULE, + .name = "phidgetservo", + .probe = servo_probe, + .disconnect = servo_disconnect, + .id_table = id_table +}; + +static int __init +phidget_servo_init(void) +{ + int retval = 0; + + retval = usb_register(&servo_driver); + if (retval) + err("usb_register failed. Error number %d", retval); + + return retval; +} + +static void __exit +phidget_servo_exit(void) +{ + usb_deregister(&servo_driver); +} + +module_init(phidget_servo_init); +module_exit(phidget_servo_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c --- a/drivers/usb/misc/usbtest.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/misc/usbtest.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/misc/uss720.c Sun May 9 21:32:55 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/usbnet.c b/drivers/usb/net/usbnet.c --- a/drivers/usb/net/usbnet.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/net/usbnet.c Sun May 9 21:32:55 2004 @@ -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); @@ -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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/serial/io_ti.c Sun May 9 21:32:55 2004 @@ -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/keyspan.c b/drivers/usb/serial/keyspan.c --- a/drivers/usb/serial/keyspan.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/serial/keyspan.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/serial/kobil_sct.c Sun May 9 21:32:55 2004 @@ -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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/serial/pl2303.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/serial/pl2303.h Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/serial/safe_serial.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/serial/usb-serial.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/serial/visor.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/serial/visor.h Sun May 9 21:32:55 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/scsiglue.c b/drivers/usb/storage/scsiglue.c --- a/drivers/usb/storage/scsiglue.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/storage/scsiglue.c Sun May 9 21:32:55 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/transport.c b/drivers/usb/storage/transport.c --- a/drivers/usb/storage/transport.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/storage/transport.c Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/drivers/usb/storage/unusual_devs.h Sun May 9 21:32:55 2004 @@ -90,6 +90,12 @@ US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ), #endif +/* Patch submitted by Alessandro Fracchetti */ +UNUSUAL_DEV( 0x0482, 0x0105, 0x0100, 0x0100, + "Kyocera", + "Finecam L3", + US_SC_SCSI, US_PR_BULK, NULL, US_FL_FIX_INQUIRY), + /* Patch submitted by Philipp Friedrich */ UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100, "Kyocera", @@ -297,6 +303,13 @@ US_SC_SCSI, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN | US_FL_MODE_XLATE), +/* Submitted by Rajesh Kumble Nayak */ +UNUSUAL_DEV( 0x054c, 0x002e, 0x0500, 0x0500, + "Sony", + "Handycam HC-85", + US_SC_UFI, US_PR_DEVICE, NULL, + US_FL_SINGLE_LUN | US_FL_MODE_XLATE), + UNUSUAL_DEV( 0x054c, 0x0032, 0x0000, 0x9999, "Sony", "Memorystick MSC-U01N", @@ -322,6 +335,13 @@ "PEG Mass Storage", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), + +/* Submitted by Mike Alborn */ +UNUSUAL_DEV( 0x054c, 0x016a, 0x0000, 0x9999, + "Sony", + "PEG Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), UNUSUAL_DEV( 0x057b, 0x0000, 0x0000, 0x0299, "Y-E Data", @@ -438,22 +458,6 @@ US_FL_SINGLE_LUN ), #endif -/* Following three Minolta cameras reported by Martin Pool - * . Originally discovered by Kedar Petankar, - * Matthew Geier, Mikael Lofj"ard, Marcel de Boer. - */ -UNUSUAL_DEV( 0x0686, 0x4006, 0x0001, 0x0001, - "Minolta", - "DiMAGE 7", - US_SC_SCSI, US_PR_DEVICE, NULL, - 0 ), - -UNUSUAL_DEV( 0x0686, 0x400f, 0x0001, 0x0001, - "Minolta", - "DiMAGE 7Hi", - US_SC_SCSI, US_PR_DEVICE, NULL, - 0 ), - /* Submitted by Benny Sjostrand */ UNUSUAL_DEV( 0x0686, 0x4011, 0x0001, 0x0001, "Minolta", @@ -629,7 +633,7 @@ "Casio", "QV DigitalCamera", US_SC_DEVICE, US_PR_CB, NULL, - US_FL_FIX_INQUIRY ), + US_FL_NEED_OVERRIDE | US_FL_FIX_INQUIRY ), /* Later Casio cameras apparently tell the truth */ UNUSUAL_DEV( 0x07cf, 0x1001, 0x9010, 0x9999, @@ -688,7 +692,7 @@ UNUSUAL_DEV( 0x0a16, 0x8888, 0x0100, 0x0100, "IBM", "IBM USB Memory Key", - US_SC_SCSI, US_PR_BULK, NULL, + US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), /* This Pentax still camera is not conformant diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c --- a/drivers/usb/storage/usb.c Sun May 9 21:32:55 2004 +++ b/drivers/usb/storage/usb.c Sun May 9 21:32:55 2004 @@ -84,8 +84,6 @@ #include -#include -#include #include #include @@ -490,7 +488,7 @@ if (unusual_dev->useTransport != US_PR_DEVICE && us->protocol == idesc->bInterfaceProtocol) msg += 2; - if (msg >= 0) + if (msg >= 0 && !(unusual_dev->flags & US_FL_NEED_OVERRIDE)) printk(KERN_NOTICE USB_STORAGE "This device " "(%04x,%04x,%04x S %02x P %02x)" " has %s in unusual_devs.h\n" diff -Nru a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h --- a/drivers/usb/storage/usb.h Sun May 9 21:32:55 2004 +++ b/drivers/usb/storage/usb.h Sun May 9 21:32:55 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 Sun May 9 21:32:55 2004 +++ b/include/linux/usb.h Sun May 9 21:32:55 2004 @@ -14,6 +14,7 @@ #include /* for mdelay() */ #include /* for in_interrupt() */ #include /* for struct list_head */ +#include /* for struct kref */ #include /* for struct device */ #include /* for struct file_operations */ #include /* for struct completion */ @@ -147,11 +148,42 @@ #define USB_MAXINTERFACES 32 /** + * struct usb_interface_cache - long-term representation of a device interface + * @num_altsetting: number of altsettings defined. + * @ref: reference counter. + * @altsetting: variable-length array of interface structures, one for + * each alternate setting that may be selected. Each one includes a + * set of endpoint configurations. They will be in no particular order. + * + * These structures persist for the lifetime of a usb_device, unlike + * struct usb_interface (which persists only as long as its configuration + * is installed). The altsetting arrays can be accessed through these + * structures at any time, permitting comparison of configurations and + * providing support for the /proc/bus/usb/devices pseudo-file. + */ +struct usb_interface_cache { + unsigned num_altsetting; /* number of alternate settings */ + struct kref ref; /* reference counter */ + + /* variable-length array of alternate settings for this interface, + * stored in no particular order */ + struct usb_host_interface altsetting[0]; +}; +#define ref_to_usb_interface_cache(r) \ + container_of(r, struct usb_interface_cache, ref) +#define altsetting_to_usb_interface_cache(a) \ + container_of(a, struct usb_interface_cache, altsetting[0]) + +/** * struct usb_host_config - representation of a device's configuration * @desc: the device's configuration descriptor. - * @interface: array of usb_interface structures, one for each interface - * in the configuration. The number of interfaces is stored in - * desc.bNumInterfaces. + * @interface: array of pointers to usb_interface structures, one for each + * interface in the configuration. The number of interfaces is stored + * in desc.bNumInterfaces. These pointers are valid only while the + * the configuration is active. + * @intf_cache: array of pointers to usb_interface_cache structures, one + * for each interface in the configuration. These structures exist + * for the entire life of the device. * @extra: pointer to buffer containing all extra descriptors associated * with this configuration (those preceding the first interface * descriptor). @@ -185,6 +217,10 @@ * stored in no particular order */ struct usb_interface *interface[USB_MAXINTERFACES]; + /* Interface information available even when this is not the + * active configuration */ + struct usb_interface_cache *intf_cache[USB_MAXINTERFACES]; + unsigned char *extra; /* Extra descriptors */ int extralen; }; @@ -676,7 +712,7 @@ * URB_NO_SETUP_DMA_MAP indicate which buffers have already been mapped. * URB_NO_SETUP_DMA_MAP is ignored for non-control URBs. * - * Interrupt UBS must provide an interval, saying how often (in milliseconds + * Interrupt URBs must provide an interval, saying how often (in milliseconds * or, for highspeed devices, 125 microsecond units) * to poll for transfers. After the URB has been submitted, the interval * field reflects how the transfer was actually scheduled. @@ -731,8 +767,8 @@ struct urb { /* private, usb core and host controller only fields in the urb */ + struct kref kref; /* reference count of the URB */ spinlock_t lock; /* lock for the URB */ - atomic_t count; /* reference count of the URB */ void *hcpriv; /* private data for host controller */ struct list_head urb_list; /* list pointer to all active urbs */ int bandwidth; /* bandwidth for INT/ISO request */ diff -Nru a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h --- a/include/linux/usbdevice_fs.h Sun May 9 21:32:55 2004 +++ b/include/linux/usbdevice_fs.h Sun May 9 21:32:55 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 */