bk://kernel.bkbits.net/gregkh/linux/usb-2.6
greg@kroah.com|ChangeSet|20050113234558|56335 greg

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2005/01/13 15:45:58-08:00 greg@kroah.com 
#   USB: give the idmouse the 132 minor number
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/misc/idmouse.c
#   2005/01/13 15:45:18-08:00 greg@kroah.com +1 -1
#   USB: give the idmouse the 132 minor number
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# Documentation/devices.txt
#   2005/01/13 15:45:18-08:00 greg@kroah.com +1 -0
#   USB: give the idmouse the 132 minor number
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# ChangeSet
#   2005/01/13 13:32:01-08:00 arekm@pld-linux.org 
#   [PATCH] USB: add Ever UPS vendor/product id to ftdi_sio driver
#   
#   This patch allows to use ftdi_sio driver with Ever ECO Pro CDS UPS.
#   Patch was tested on pre-2.6.10 kernel.
#   
#   Signed-Off: Arkadiusz Miskiewicz <arekm@pld-linux.org>
# 
# drivers/usb/serial/ftdi_sio.h
#   2005/01/13 07:37:33-08:00 arekm@pld-linux.org +6 -0
#   USB: add Ever UPS vendor/product id to ftdi_sio driver
# 
# drivers/usb/serial/ftdi_sio.c
#   2005/01/13 08:04:12-08:00 arekm@pld-linux.org +3 -0
#   USB: add Ever UPS vendor/product id to ftdi_sio driver
# 
# ChangeSet
#   2005/01/13 12:46:54-08:00 greg@kroah.com 
#   [PATCH] USB: fix sparse warnings in the idmouse.c driver
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/misc/idmouse.c
#   2005/01/13 12:40:10-08:00 greg@kroah.com +3 -3
#   USB: fix sparse warnings in the idmouse.c driver
# 
# ChangeSet
#   2005/01/13 12:46:08-08:00 echtler@fs.tum.de 
#   [PATCH] USB: add driver for the Siemens ID Mouse fingerprint sensor
#   
#   This patch adds a new usb-misc driver for the fingerprint sensor that
#   can be found in the Siemens ID Mouse USB. "cat /dev/usb/idmouseX"
#   yields a 225x288 greyscale PNM with the fingerprint information.
#   
#   It's now in version 0.5, which uses memcpy() instead of snprintf()
#   and allows interruption of the image acquisition process, in case
#   it should get stuck.
#   
#   It may be considered controversial that it outputs a PNM instead
#   of raw data, but I hold the opinion that the 15 bytes of header,
#   "P5 225 288 256 ", do not do any harm and allow the device to be
#   used in shell scripts or similar, too.
#   
#   The setup packets are not described further, simply because I don"t
#   know anything about them myself. We captured them under Windows
#   using SnoopyPro.
#   
#   Please include this into the main USB kernel tree - I think it has
#   by now been scrutinized and tested quite thoroughly.
#   
#   Signed-off-by: Florian Echtler  <echtler@fs.tum.de>
#   Signed-off-by: Andreas Deresch <aderesch@fs.tum.de>
# 
# drivers/usb/misc/idmouse.c
#   2005/01/13 01:00:45-08:00 echtler@fs.tum.de +442 -0
#   USB: add driver for the Siemens ID Mouse fingerprint sensor
# 
# drivers/usb/misc/Makefile
#   2005/01/13 01:00:25-08:00 echtler@fs.tum.de +1 -0
#   USB: add driver for the Siemens ID Mouse fingerprint sensor
# 
# drivers/usb/misc/Kconfig
#   2005/01/13 01:00:25-08:00 echtler@fs.tum.de +14 -0
#   USB: add driver for the Siemens ID Mouse fingerprint sensor
# 
# drivers/usb/Makefile
#   2005/01/13 01:00:25-08:00 echtler@fs.tum.de +1 -0
#   USB: add driver for the Siemens ID Mouse fingerprint sensor
# 
# drivers/usb/misc/idmouse.c
#   2005/01/13 01:00:45-08:00 echtler@fs.tum.de +0 -0
#   BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/misc/idmouse.c
# 
# ChangeSet
#   2005/01/13 12:44:59-08:00 stern@rowland.harvard.edu 
#   [PATCH] USB: correct and clarify error-code documentation
#   
#   This patch corrects some misconceptions that have persisted in the USB
#   error-code documentation for quite some time.
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# Documentation/usb/error-codes.txt
#   2005/01/12 14:10:28-08:00 stern@rowland.harvard.edu +13 -4
#   USB: correct and clarify error-code documentation
# 
# ChangeSet
#   2005/01/13 12:43:52-08:00 petkan@nucleusys.com 
#   [PATCH] pegasus 2.6.10 cset
#   
#   Various fixes to the 'pegasus' driver, notably fixing OSDL bugid #3978
#   so this can be used with bridges again (or for that matter, other
#   normal usage).
#   
#       * Bugfixes in the status urb completion handler:
#   
#           - Never use garbage that happens to be sitting in the URB
#             data buffer to change the carrier status.
#   
#   
#           - There are two bits which claim to report parts of carrier
#             detect bit.  This switches to the one that works sometimes;
#             monitoring through MII might be the best solution.
#   
#           - Stop log spamming ... at least some of these chips seem
#             to get confused about data toggle, no point in warning
#             about each packet error as it's detected.
#   
#       * Report the normal Ethernet MTU.
#   
#       * Better ethtool support:
#   
#           - Save the message level set by userspace
#           - Basic WOL support
#   
#       * Add USB suspend() and resume() methods, to go with WOL.
#         Modeled on what stir4200 does.
#   
#   Also, some of the messages are converted to the more conventional
#   style:  "ethN: message text", or driver model style before the
#   device is registered.
#   
#   	* removed redundant MII code since CONFIG_MII is always set by Kconfig;
#     	* updated the version string;
#   
#   Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
#   Signed-off-by: Petko Manolov <petkan@nucleusys.com>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/net/pegasus.h
#   2005/01/13 03:04:59-08:00 petkan@nucleusys.com +3 -0
#   pegasus 2.6.10 cset
# 
# drivers/usb/net/pegasus.c
#   2005/01/13 03:04:59-08:00 petkan@nucleusys.com +114 -90
#   pegasus 2.6.10 cset
# 
# ChangeSet
#   2005/01/13 12:42:36-08:00 luca.risolia@studio.unibo.it 
#   [PATCH] USB: SN9C10x driver updates
#   
#   SN9C10x driver updates.
#   
#   Changes:
#   
#   @ Fix the sysfs interface
#   @ Fix allocated minor number after device detection
#   + Add "force_munmap" module parameter
#   + Documentation updates
#   + Add support for old VIDIOC_S_PARM_OLD and VIDIOC_S_CTRL_OLD ioctl's
#   
#   Signed-off-by: Luca Risolia <luca.risolia@studio.unibo.it>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/media/sn9c102_tas5130d1b.c
#   2005/01/12 18:05:21-08:00 luca.risolia@studio.unibo.it +1 -1
#   USB: SN9C10x driver updates
# 
# drivers/usb/media/sn9c102_tas5110c1b.c
#   2005/01/12 18:05:21-08:00 luca.risolia@studio.unibo.it +1 -1
#   USB: SN9C10x driver updates
# 
# drivers/usb/media/sn9c102_sensor.h
#   2005/01/12 18:05:21-08:00 luca.risolia@studio.unibo.it +1 -1
#   USB: SN9C10x driver updates
# 
# drivers/usb/media/sn9c102_pas106b.c
#   2005/01/12 18:05:20-08:00 luca.risolia@studio.unibo.it +1 -1
#   USB: SN9C10x driver updates
# 
# drivers/usb/media/sn9c102_mi0343.c
#   2005/01/12 18:05:20-08:00 luca.risolia@studio.unibo.it +1 -1
#   USB: SN9C10x driver updates
# 
# drivers/usb/media/sn9c102_hv7131d.c
#   2005/01/12 18:05:20-08:00 luca.risolia@studio.unibo.it +1 -1
#   USB: SN9C10x driver updates
# 
# drivers/usb/media/sn9c102_core.c
#   2005/01/12 18:05:20-08:00 luca.risolia@studio.unibo.it +67 -29
#   USB: SN9C10x driver updates
# 
# drivers/usb/media/sn9c102.h
#   2005/01/12 18:05:20-08:00 luca.risolia@studio.unibo.it +10 -3
#   USB: SN9C10x driver updates
# 
# Documentation/usb/sn9c102.txt
#   2005/01/12 18:15:19-08:00 luca.risolia@studio.unibo.it +17 -6
#   USB: SN9C10x driver updates
# 
# ChangeSet
#   2005/01/13 12:41:41-08:00 tglx@linutronix.de 
#   [PATCH] USB: Lock initializer cleanup - batch 4
#   
#   Use the new lock initializers DEFINE_SPIN_LOCK and DEFINE_RW_LOCK
#   
#   Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/core/hub.c
#   2005/01/12 14:01:19-08:00 tglx@linutronix.de +2 -2
#   USB: Lock initializer cleanup - batch 4
# 
# drivers/usb/core/hcd.c
#   2005/01/12 14:01:20-08:00 tglx@linutronix.de +1 -1
#   USB: Lock initializer cleanup - batch 4
# 
# drivers/usb/core/file.c
#   2005/01/12 14:01:20-08:00 tglx@linutronix.de +1 -1
#   USB: Lock initializer cleanup - batch 4
# 
# ChangeSet
#   2005/01/12 10:23:24-08:00 david-b@pacbell.net 
#   [PATCH] USB: usbnet:  Olympus R1000 PDA, and blacklisting if CDC && !ZAURUS
#   
#   Add support for the Zaurus-compatible configuration of the
#   Olympus R1000 PDA.  (IDs from Todd Blumer, todd@sdgsystems.com)
#   
#   Resolve a FIXME:  all the Zaurus support morphs into blacklist
#   entries when CDC Ethernet is enabled and Zaurus isn't (since the
#   Zaurus firmware falsely advertises itself as CDC conformant).
#   
#   Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/net/usbnet.c
#   2005/01/11 19:44:33-08:00 david-b@pacbell.net +56 -36
#   USB: usbnet:  Olympus R1000 PDA, and blacklisting if CDC && !ZAURUS
# 
# ChangeSet
#   2005/01/12 10:22:41-08:00 adobriyan@mail.ru 
#   [PATCH] USB: drivers/usb/*: s/0/NULL/ in pointer context
#   
#   Signed-off-by: Alexey Dobriyan <adobriyan@mail.ru>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/keyspan.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +3 -3
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/serial/garmin_gps.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +4 -4
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/net/usbnet.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +5 -4
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/misc/usbtest.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +2 -2
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/input/hid-core.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +1 -1
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/image/mdc800.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +2 -2
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/host/sl811-hcd.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +1 -1
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/host/ehci-sched.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +13 -13
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/host/ehci-q.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +7 -7
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/host/ehci-mem.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +4 -4
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/host/ehci-hcd.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +1 -1
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/core/message.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +2 -2
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/core/hub.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +3 -3
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/core/hcd.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +1 -1
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/core/devio.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +3 -3
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# drivers/usb/class/usblp.c
#   2005/01/11 16:00:00-08:00 adobriyan@mail.ru +1 -1
#   USB: drivers/usb/*: s/0/NULL/ in pointer context
# 
# ChangeSet
#   2005/01/11 17:06:37-08:00 stern@rowland.harvard.edu 
#   [PATCH] USB UHCI: protect DMA-able fields with barriers
#   
#   This is a revised patch to fix a problem in the UHCI driver, in which the
#   compiler incorrectly optimizes certain accesses to DMA-able memory
#   addresses.  The patch reorganizes the code to use special accessor
#   routines including a compiler optimization barrier, and stores the results
#   in local variables to help prevent repeated accesses.  No use is made of
#   the "volatile" keyword.  :-)
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/host/uhci-hcd.h
#   2005/01/11 17:06:12-08:00 stern@rowland.harvard.edu +23 -1
#   [PATCH] USB UHCI: protect DMA-able fields with barriers
#   
#   This is a revised patch to fix a problem in the UHCI driver, in which the
#   compiler incorrectly optimizes certain accesses to DMA-able memory
#   addresses.  The patch reorganizes the code to use special accessor
#   routines including a compiler optimization barrier, and stores the results
#   in local variables to help prevent repeated accesses.  No use is made of
#   the "volatile" keyword.  :-)
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/host/uhci-hcd.c
#   2005/01/11 17:06:12-08:00 stern@rowland.harvard.edu +27 -17
#   [PATCH] USB UHCI: protect DMA-able fields with barriers
#   
#   This is a revised patch to fix a problem in the UHCI driver, in which the
#   compiler incorrectly optimizes certain accesses to DMA-able memory
#   addresses.  The patch reorganizes the code to use special accessor
#   routines including a compiler optimization barrier, and stores the results
#   in local variables to help prevent repeated accesses.  No use is made of
#   the "volatile" keyword.  :-)
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/host/uhci-debug.c
#   2005/01/11 17:06:12-08:00 stern@rowland.harvard.edu +8 -7
#   [PATCH] USB UHCI: protect DMA-able fields with barriers
#   
#   This is a revised patch to fix a problem in the UHCI driver, in which the
#   compiler incorrectly optimizes certain accesses to DMA-able memory
#   addresses.  The patch reorganizes the code to use special accessor
#   routines including a compiler optimization barrier, and stores the results
#   in local variables to help prevent repeated accesses.  No use is made of
#   the "volatile" keyword.  :-)
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# ChangeSet
#   2005/01/11 17:06:02-08:00 zaitcev@redhat.com 
#   [PATCH] USB: Patch to fix ub looping with a tag mismatch
#   
#   If a command times out, we resubmit a retry. Some devices, however, buffer
#   everything we send and then eventually reply to a command we have timed out
#   already. We receive a bad tag, send a new command, device replies to the
#   one sent before, and so on without end.
#   
#   The fix is to flush pending replies if tags mismatch (by reading them).
#   
#   Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/block/ub.c
#   2005/01/11 17:05:37-08:00 zaitcev@redhat.com +44 -42
#   [PATCH] USB: Patch to fix ub looping with a tag mismatch
#   
#   If a command times out, we resubmit a retry. Some devices, however, buffer
#   everything we send and then eventually reply to a command we have timed out
#   already. We receive a bad tag, send a new command, device replies to the
#   one sent before, and so on without end.
#   
#   The fix is to flush pending replies if tags mismatch (by reading them).
#   
#   Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# ChangeSet
#   2005/01/11 17:04:56-08:00 phil@ipom.com 
#   [PATCH] USB unusual_devs addition: Ignore residue for ours-tech disk
#   
#   This "Ours Technology" device incorrectly reports 100% residue on transferred data.
#   Patch originally sent by Daniel Drake <dsd@gentoo.org>, with slight modification by me.
#   
#   Signed-off-by: Daniel Drake <dsd@gentoo.org>
#   Signed-off-by: Phil Dibowitz <phil@ipom.com>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/storage/unusual_devs.h
#   2005/01/11 17:04:25-08:00 phil@ipom.com +8 -0
#   [PATCH] USB unusual_devs addition: Ignore residue for ours-tech disk
#   
#   This "Ours Technology" device incorrectly reports 100% residue on transferred data.
#   Patch originally sent by Daniel Drake <dsd@gentoo.org>, with slight modification by me.
#   
#   Signed-off-by: Daniel Drake <dsd@gentoo.org>
#   Signed-off-by: Phil Dibowitz <phil@ipom.com>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# ChangeSet
#   2005/01/11 17:04:01-08:00 lmendez19@austin.rr.com 
#   [PATCH] USB cypress_m8: line setting bugfix, circular write buffer added, misc. fixes
#   
#   This patch brings up to date the driver with the current stable development
#   source.  A bug with RTS not raising upon first open was fixed, Al Borcher's
#   circular write buffer from the pl2303 driver was implemented, and various
#   fixes/cleanups were made.
#   
#   Signed-off-by: Lonnie Mendez <lmendez19@austin.rr.com>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/cypress_m8.c
#   2005/01/11 17:03:39-08:00 lmendez19@austin.rr.com +465 -149
#   [PATCH] USB cypress_m8: line setting bugfix, circular write buffer added, misc. fixes
#   
#   This patch brings up to date the driver with the current stable development
#   source.  A bug with RTS not raising upon first open was fixed, Al Borcher's
#   circular write buffer from the pl2303 driver was implemented, and various
#   fixes/cleanups were made.
#   
#   Signed-off-by: Lonnie Mendez <lmendez19@austin.rr.com>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# ChangeSet
#   2005/01/11 17:02:56-08:00 oliver@neukum.org 
#   [PATCH] USB: CDC ACM module and Zoom 2985 modem
#   
#   there's a bug in the acm driver's work arounds. This fixes it.
#   
#   Signed-Off-By: Oliver Neukum <oliver@neukum.name>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/class/cdc-acm.c
#   2005/01/11 17:02:27-08:00 oliver@neukum.org +13 -10
#   [PATCH] USB: CDC ACM module and Zoom 2985 modem
#   
#   there's a bug in the acm driver's work arounds. This fixes it.
#   
#   Signed-Off-By: Oliver Neukum <oliver@neukum.name>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# ChangeSet
#   2005/01/11 15:19:24-08:00 greg@kroah.com 
#   USB: remove some unneeded exported symbols.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/core/usb.c
#   2005/01/11 15:18:34-08:00 greg@kroah.com +1 -2
#   USB: remove some unneeded exported symbols.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/core/hub.c
#   2005/01/11 15:18:34-08:00 greg@kroah.com +0 -1
#   USB: remove some unneeded exported symbols.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/core/hcd.c
#   2005/01/11 15:18:34-08:00 greg@kroah.com +0 -3
#   USB: remove some unneeded exported symbols.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
diff -Nru a/Documentation/devices.txt b/Documentation/devices.txt
--- a/Documentation/devices.txt	2005-01-13 17:08:38 -08:00
+++ b/Documentation/devices.txt	2005-01-13 17:08:38 -08:00
@@ -2563,6 +2563,7 @@
 		128 = /dev/usb/brlvgr0	First Braille Voyager device
 		    ...
 		131 = /dev/usb/brlvgr3	Fourth Braille Voyager device
+		132 = /dev/usb/idmouse	ID Mouse (fingerprint scanner) device
 		144 = /dev/usb/lcd	USB LCD device
 		160 = /dev/usb/legousbtower0	1st USB Legotower device
 		    ...
diff -Nru a/Documentation/usb/error-codes.txt b/Documentation/usb/error-codes.txt
--- a/Documentation/usb/error-codes.txt	2005-01-13 17:08:38 -08:00
+++ b/Documentation/usb/error-codes.txt	2005-01-13 17:08:38 -08:00
@@ -83,7 +83,18 @@
 			   prescribed bus turn-around time
 			c) unknown USB error 
 
--EILSEQ (*, **)		CRC mismatch
+-EILSEQ (*, **)		a) CRC mismatch
+			b) no response packet received within the
+			   prescribed bus turn-around time
+			c) unknown USB error 
+
+			In cases b) and c) either -EPROTO or -EILSEQ
+			may be returned.  Note that often the controller
+			hardware does not distinguish among cases a),
+			b), and c), so a driver cannot tell whether
+			there was a protocol error, a failure to respond
+			(often caused by device disconnect), or some
+			other fault.
 
 -EPIPE (**)		Endpoint stalled.  For non-control endpoints,
 			reset this status with usb_clear_halt().
@@ -104,8 +115,6 @@
 			specified buffer, and URB_SHORT_NOT_OK was set in
 			urb->transfer_flags.
 
--ETIMEDOUT (**)		transfer timed out, NAK
-
 -ENODEV			Device was removed.  Often preceded by a burst of
 			other errors, since the hub driver does't detect
 			device removal events immediately.
@@ -143,4 +152,4 @@
 usb_get_*/usb_set_*():
 usb_control_msg():
 usb_bulk_msg():
-			All USB errors (submit/status) can occur
+-ETIMEDOUT		timeout expired before the transfer completed
diff -Nru a/Documentation/usb/sn9c102.txt b/Documentation/usb/sn9c102.txt
--- a/Documentation/usb/sn9c102.txt	2005-01-13 17:08:38 -08:00
+++ b/Documentation/usb/sn9c102.txt	2005-01-13 17:08:38 -08:00
@@ -26,7 +26,7 @@
 
 1. Copyright
 ============
-Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>
+Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>
 
 
 2. Disclaimer
@@ -165,6 +165,17 @@
                 other camera.
 Default:        -1
 -------------------------------------------------------------------------------
+Name:           force_munmap;
+Type:           bool array (min = 0, max = 64)
+Syntax:         <0|1[,...]> 
+Description:    Force the application to unmap previously mapped buffer memory
+                before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not
+                all the applications support this feature. This parameter is
+                specific for each detected camera.
+                0 = do not force memory unmapping"
+                1 = force memory unmapping (save memory)"
+Default:        0
+-------------------------------------------------------------------------------
 Name:           debug
 Type:           int
 Syntax:         <n> 
@@ -362,11 +373,11 @@
 file descriptor. Once it is selected, the application must close and reopen the
 device to switch to the other I/O method;
 
-- previously mapped buffer memory must always be unmapped before calling any
-of the "VIDIOC_S_CROP", "VIDIOC_TRY_FMT" and "VIDIOC_S_FMT" ioctl's. The same
-number of buffers as before will be allocated again to match the size of the
-new video frames, so you have to map the buffers again before any I/O attempts
-on them.
+- although it is not mandatory, previously mapped buffer memory should always
+be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's.
+The same number of buffers as before will be allocated again to match the size
+of the new video frames, so you have to map the buffers again before any I/O
+attempts on them.
 
 Consistently with the hardware limits, this driver also supports image
 downscaling with arbitrary scaling factors from 1, 2 and 4 in both directions.
diff -Nru a/drivers/block/ub.c b/drivers/block/ub.c
--- a/drivers/block/ub.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/block/ub.c	2005-01-13 17:08:38 -08:00
@@ -108,7 +108,8 @@
  */
 #define UB_URB_TIMEOUT	(HZ*2)
 #define UB_DATA_TIMEOUT	(HZ*5)	/* ZIP does spin-ups in the data phase */
-#define UB_CTRL_TIMEOUT	(HZ/2) /* 500ms ought to be enough to clear a stall */
+#define UB_STAT_TIMEOUT	(HZ*5)	/* Same spinups and eject for a dataless cmd. */
+#define UB_CTRL_TIMEOUT	(HZ/2)	/* 500ms ought to be enough to clear a stall */
 
 /*
  * An instance of a SCSI command in transit.
@@ -307,6 +308,7 @@
 static void ub_scsi_dispatch(struct ub_dev *sc);
 static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
 static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc);
+static void __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
 static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
 static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
 static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
@@ -894,7 +896,7 @@
 		if (urb->status == -EPIPE) {
 			/*
 			 * STALL while clearning STALL.
-			 * A STALL is illegal on a control pipe!
+			 * The control pipe clears itself - nothing to do.
 			 * XXX Might try to reset the device here and retry.
 			 */
 			printk(KERN_NOTICE "%s: "
@@ -917,7 +919,7 @@
 		if (urb->status == -EPIPE) {
 			/*
 			 * STALL while clearning STALL.
-			 * A STALL is illegal on a control pipe!
+			 * The control pipe clears itself - nothing to do.
 			 * XXX Might try to reset the device here and retry.
 			 */
 			printk(KERN_NOTICE "%s: "
@@ -941,7 +943,8 @@
 			rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
 			if (rc != 0) {
 				printk(KERN_NOTICE "%s: "
-				    "unable to submit clear for device %u (%d)\n",
+				    "unable to submit clear for device %u"
+				    " (code %d)\n",
 				    sc->name, sc->dev->devnum, rc);
 				/*
 				 * This is typically ENOMEM or some other such shit.
@@ -1001,7 +1004,8 @@
 			rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
 			if (rc != 0) {
 				printk(KERN_NOTICE "%s: "
-				    "unable to submit clear for device %u (%d)\n",
+				    "unable to submit clear for device %u"
+				    " (code %d)\n",
 				    sc->name, sc->dev->devnum, rc);
 				/*
 				 * This is typically ENOMEM or some other such shit.
@@ -1033,7 +1037,8 @@
 			rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
 			if (rc != 0) {
 				printk(KERN_NOTICE "%s: "
-				    "unable to submit clear for device %u (%d)\n",
+				    "unable to submit clear for device %u"
+				    " (code %d)\n",
 				    sc->name, sc->dev->devnum, rc);
 				/*
 				 * This is typically ENOMEM or some other such shit.
@@ -1061,33 +1066,7 @@
 				    sc->name, sc->dev->devnum);
 				goto Bad_End;
 			}
-
-			/*
-			 * ub_state_stat only not dropping the count...
-			 */
-			UB_INIT_COMPLETION(sc->work_done);
-
-			sc->last_pipe = sc->recv_bulk_pipe;
-			usb_fill_bulk_urb(&sc->work_urb, sc->dev,
-			    sc->recv_bulk_pipe, &sc->work_bcs,
-			    US_BULK_CS_WRAP_LEN, ub_urb_complete, sc);
-			sc->work_urb.transfer_flags = URB_ASYNC_UNLINK;
-			sc->work_urb.actual_length = 0;
-			sc->work_urb.error_count = 0;
-			sc->work_urb.status = 0;
-
-			rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC);
-			if (rc != 0) {
-				/* XXX Clear stalls */
-				printk("%s: CSW #%d submit failed (%d)\n",
-				   sc->name, cmd->tag, rc); /* P3 */
-				ub_complete(&sc->work_done);
-				ub_state_done(sc, cmd, rc);
-				return;
-			}
-
-			sc->work_timer.expires = jiffies + UB_URB_TIMEOUT;
-			add_timer(&sc->work_timer);
+			__ub_state_stat(sc, cmd);
 			return;
 		}
 
@@ -1108,17 +1087,31 @@
 			goto Bad_End;
 		}
 
+#if 0
 		if (bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN) &&
 		    bcs->Signature != cpu_to_le32(US_BULK_CS_OLYMPUS_SIGN)) {
-			/* XXX Rate-limit, even for P3 tagged */
-			/* P3 */ printk("ub: signature 0x%x\n", bcs->Signature);
 			/* Windows ignores signatures, so do we. */
 		}
+#endif
 
 		if (bcs->Tag != cmd->tag) {
-			/* P3 */ printk("%s: tag orig 0x%x reply 0x%x\n",
-			    sc->name, cmd->tag, bcs->Tag);
-			goto Bad_End;
+			/*
+			 * This usually happens when we disagree with the
+			 * device's microcode about something. For instance,
+			 * a few of them throw this after timeouts. They buffer
+			 * commands and reply at commands we timed out before.
+			 * Without flushing these replies we loop forever.
+			 */
+			if (++cmd->stat_count >= 4) {
+				printk(KERN_NOTICE "%s: "
+				    "tag mismatch orig 0x%x reply 0x%x "
+				    "on device %u\n",
+				    sc->name, cmd->tag, bcs->Tag,
+				    sc->dev->devnum);
+				goto Bad_End;
+			}
+			__ub_state_stat(sc, cmd);
+			return;
 		}
 
 		switch (bcs->Status) {
@@ -1174,9 +1167,9 @@
 
 /*
  * Factorization helper for the command state machine:
- * Submit a CSW read and go to STAT state.
+ * Submit a CSW read.
  */
-static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
+static void __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
 {
 	int rc;
 
@@ -1192,14 +1185,23 @@
 
 	if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
 		/* XXX Clear stalls */
-		printk("ub: CSW #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */
+		printk("%s: CSW #%d submit failed (%d)\n", sc->name, cmd->tag, rc); /* P3 */
 		ub_complete(&sc->work_done);
 		ub_state_done(sc, cmd, rc);
 		return;
 	}
 
-	sc->work_timer.expires = jiffies + UB_URB_TIMEOUT;
+	sc->work_timer.expires = jiffies + UB_STAT_TIMEOUT;
 	add_timer(&sc->work_timer);
+}
+
+/*
+ * Factorization helper for the command state machine:
+ * Submit a CSW read and go to STAT state.
+ */
+static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
+{
+	__ub_state_stat(sc, cmd);
 
 	cmd->stat_count = 0;
 	cmd->state = UB_CMDST_STAT;
diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile
--- a/drivers/usb/Makefile	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/Makefile	2005-01-13 17:08:38 -08:00
@@ -59,6 +59,7 @@
 obj-$(CONFIG_USB_CYTHERM)	+= misc/
 obj-$(CONFIG_USB_EMI26)		+= misc/
 obj-$(CONFIG_USB_EMI62)		+= misc/
+obj-$(CONFIG_USB_IDMOUSE)	+= misc/
 obj-$(CONFIG_USB_LCD)		+= misc/
 obj-$(CONFIG_USB_LED)		+= misc/
 obj-$(CONFIG_USB_LEGOTOWER)	+= misc/
diff -Nru a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
--- a/drivers/usb/class/cdc-acm.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/class/cdc-acm.c	2005-01-13 17:08:38 -08:00
@@ -544,24 +544,24 @@
 	
 	/* normal probing*/
 	if (!buffer) {
-		err("Wierd descriptor references");
+		err("Wierd descriptor references\n");
 		return -EINVAL;
 	}
 
 	if (!buflen) {
 		if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
-			dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint");
+			dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
 			buflen = intf->cur_altsetting->endpoint->extralen;
 			buffer = intf->cur_altsetting->endpoint->extra;
 		} else {
-			err("Zero length descriptor references");
+			err("Zero length descriptor references\n");
 			return -EINVAL;
 		}
 	}
 
 	while (buflen > 0) {
 		if (buffer [1] != USB_DT_CS_INTERFACE) {
-			err("skipping garbage");
+			err("skipping garbage\n");
 			goto next_desc;
 		}
 
@@ -614,14 +614,10 @@
 		}
 	}
 	
-		if (data_interface_num != call_interface_num)
-			dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.");
+	if (data_interface_num != call_interface_num)
+		dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.\n");
 
 skip_normal_probe:
-	if (usb_interface_claimed(data_interface)) { /* valid in this context */
-		dev_dbg(&intf->dev,"The data interface isn't available\n");
-		return -EBUSY;
-	}
 
 	/*workaround for switched interfaces */
 	if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
@@ -636,6 +632,13 @@
 			return -EINVAL;
 		}
 	}
+	
+	if (usb_interface_claimed(data_interface)) { /* valid in this context */
+		dev_dbg(&intf->dev,"The data interface isn't available\n");
+		return -EBUSY;
+	}
+
+
 	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
 		return -EINVAL;
 
diff -Nru a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
--- a/drivers/usb/class/usblp.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/class/usblp.c	2005-01-13 17:08:38 -08:00
@@ -1096,7 +1096,7 @@
 		usblp->writebuf, 0,
 		usblp_bulk_write, usblp);
 
-	usblp->bidir = (usblp->protocol[protocol].epread != 0);
+	usblp->bidir = (usblp->protocol[protocol].epread != NULL);
 	if (usblp->bidir)
 		usb_fill_bulk_urb(usblp->readurb, usblp->dev,
 			usb_rcvbulkpipe(usblp->dev,
diff -Nru a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
--- a/drivers/usb/core/devio.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/core/devio.c	2005-01-13 17:08:38 -08:00
@@ -1127,7 +1127,7 @@
 	if (copy_from_user(&ctrl, arg, sizeof (ctrl)))
 		return -EFAULT;
 	if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) {
-		if ((buf = kmalloc (size, GFP_KERNEL)) == 0)
+		if ((buf = kmalloc (size, GFP_KERNEL)) == NULL)
 			return -ENOMEM;
 		if ((_IOC_DIR(ctrl.ioctl_code) & _IOC_WRITE)) {
 			if (copy_from_user (buf, ctrl.data, size)) {
@@ -1187,7 +1187,7 @@
 		down_read(&usb_bus_type.subsys.rwsem);
 		if (intf->dev.driver)
 			driver = to_usb_driver(intf->dev.driver);
-		if (driver == 0 || driver->ioctl == 0) {
+		if (driver == NULL || driver->ioctl == NULL) {
 			retval = -ENOTTY;
 		} else {
 			retval = driver->ioctl (intf, ctrl.ioctl_code, buf);
@@ -1203,7 +1203,7 @@
 			&& size > 0
 			&& copy_to_user (ctrl.data, buf, size) != 0)
 		retval = -EFAULT;
-	if (buf != 0)
+	if (buf != NULL)
 		kfree (buf);
 	return retval;
 }
diff -Nru a/drivers/usb/core/file.c b/drivers/usb/core/file.c
--- a/drivers/usb/core/file.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/core/file.c	2005-01-13 17:08:38 -08:00
@@ -30,7 +30,7 @@
 
 #define MAX_USB_MINORS	256
 static struct file_operations *usb_minors[MAX_USB_MINORS];
-static spinlock_t minor_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(minor_lock);
 
 static int usb_open(struct inode * inode, struct file * file)
 {
diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
--- a/drivers/usb/core/hcd.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/core/hcd.c	2005-01-13 17:08:38 -08:00
@@ -86,7 +86,6 @@
 
 /* host controllers we manage */
 LIST_HEAD (usb_bus_list);
-EXPORT_SYMBOL_GPL (usb_bus_list);
 
 /* used when allocating bus numbers */
 #define USB_MAXBUS		64
@@ -97,10 +96,9 @@
 
 /* used when updating list of hcds */
 DECLARE_MUTEX (usb_bus_list_lock);	/* exported only for usbfs */
-EXPORT_SYMBOL_GPL (usb_bus_list_lock);
 
 /* used when updating hcd data */
-static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(hcd_data_lock);
 
 /* wait queue for synchronous unlinks */
 DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue);
@@ -526,7 +524,7 @@
 	/* do nothing if the urb's been unlinked */
 	if (!urb->dev
 			|| urb->status != -EINPROGRESS
-			|| (hcd = urb->dev->bus->hcpriv) == 0) {
+			|| (hcd = urb->dev->bus->hcpriv) == NULL) {
 		spin_unlock (&urb->lock);
 		local_irq_restore (flags);
 		return;
@@ -1542,7 +1540,6 @@
 	usb_set_device_state(hcd->self.root_hub, USB_STATE_NOTATTACHED);
 	mod_timer(&hcd->rh_timer, jiffies);
 }
-EXPORT_SYMBOL (usb_hc_died);
 
 /*-------------------------------------------------------------------------*/
 
diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
--- a/drivers/usb/core/hub.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/core/hub.c	2005-01-13 17:08:38 -08:00
@@ -39,10 +39,10 @@
 /* Protect struct usb_device->state and ->children members
  * Note: Both are also protected by ->serialize, except that ->state can
  * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
-static spinlock_t device_state_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(device_state_lock);
 
 /* khubd's worklist and its lock */
-static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(hub_event_lock);
 static LIST_HEAD(hub_event_list);	/* List of hubs needing servicing */
 
 /* Wakes up khubd */
@@ -405,7 +405,7 @@
 	 * since each TT has "at least two" buffers that can need it (and
 	 * there can be many TTs per hub).  even if they're uncommon.
 	 */
-	if ((clear = kmalloc (sizeof *clear, SLAB_ATOMIC)) == 0) {
+	if ((clear = kmalloc (sizeof *clear, SLAB_ATOMIC)) == NULL) {
 		dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n");
 		/* FIXME recover somehow ... RESET_TT? */
 		return;
@@ -1618,7 +1618,6 @@
 		udev->dev.power.power_state = state;
 	return status;
 }
-EXPORT_SYMBOL(__usb_suspend_device);
 
 /**
  * usb_suspend_device - suspend a usb device
@@ -2300,7 +2299,7 @@
 	int				status;
 
 	qual = kmalloc (sizeof *qual, SLAB_KERNEL);
-	if (qual == 0)
+	if (qual == NULL)
 		return;
 
 	status = usb_get_descriptor (udev, USB_DT_DEVICE_QUALIFIER, 0,
@@ -2832,7 +2831,7 @@
 			len = le16_to_cpu(udev->config[index].desc.wTotalLength);
 	}
 	buf = kmalloc (len, SLAB_KERNEL);
-	if (buf == 0) {
+	if (buf == NULL) {
 		dev_err(&udev->dev, "no mem to re-read configs after reset\n");
 		/* assume the worst */
 		return 1;
diff -Nru a/drivers/usb/core/message.c b/drivers/usb/core/message.c
--- a/drivers/usb/core/message.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/core/message.c	2005-01-13 17:08:38 -08:00
@@ -209,7 +209,7 @@
 		kfree (io->urbs);
 		io->urbs = NULL;
 	}
-	if (io->dev->dev.dma_mask != 0)
+	if (io->dev->dev.dma_mask != NULL)
 		usb_buffer_unmap_sg (io->dev, io->pipe, io->sg, io->nents);
 	io->dev = NULL;
 }
@@ -334,7 +334,7 @@
 	/* not all host controllers use DMA (like the mainstream pci ones);
 	 * they can use PIO (sl811) or be software over another transport.
 	 */
-	dma = (dev->dev.dma_mask != 0);
+	dma = (dev->dev.dma_mask != NULL);
 	if (dma)
 		io->entries = usb_buffer_map_sg (dev, pipe, sg, nents);
 	else
diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/core/usb.c	2005-01-13 17:08:38 -08:00
@@ -63,8 +63,7 @@
 int nousb;		/* Disable USB when built into kernel image */
 			/* Not honored on modular build */
 
-DECLARE_RWSEM(usb_all_devices_rwsem);
-EXPORT_SYMBOL(usb_all_devices_rwsem);
+static DECLARE_RWSEM(usb_all_devices_rwsem);
 
 
 static int generic_probe (struct device *dev)
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/host/ehci-hcd.c	2005-01-13 17:08:38 -08:00
@@ -796,7 +796,7 @@
 	 * such lossage has been observed on both VT6202 and VT8235. 
 	 */
 	if (HCD_IS_RUNNING (ehci_to_hcd(ehci)->state) &&
-			(ehci->async->qh_next.ptr != 0 ||
+			(ehci->async->qh_next.ptr != NULL ||
 			 ehci->periodic_sched != 0))
 		timer_action (ehci, TIMER_IO_WATCHDOG);
 }
diff -Nru a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
--- a/drivers/usb/host/ehci-mem.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/host/ehci-mem.c	2005-01-13 17:08:38 -08:00
@@ -51,7 +51,7 @@
 	dma_addr_t		dma;
 
 	qtd = dma_pool_alloc (ehci->qtd_pool, flags, &dma);
-	if (qtd != 0) {
+	if (qtd != NULL) {
 		ehci_qtd_init (qtd, dma);
 	}
 	return qtd;
@@ -98,7 +98,7 @@
 
 	/* dummy td enables safe urb queuing */
 	qh->dummy = ehci_qtd_alloc (ehci, flags);
-	if (qh->dummy == 0) {
+	if (qh->dummy == NULL) {
 		ehci_dbg (ehci, "no dummy td\n");
 		dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
 		qh = NULL;
@@ -215,7 +215,7 @@
 		dma_alloc_coherent (ehci_to_hcd(ehci)->self.controller,
 			ehci->periodic_size * sizeof(__le32),
 			&ehci->periodic_dma, 0);
-	if (ehci->periodic == 0) {
+	if (ehci->periodic == NULL) {
 		goto fail;
 	}
 	for (i = 0; i < ehci->periodic_size; i++)
@@ -223,7 +223,7 @@
 
 	/* software shadow of hardware table */
 	ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), flags);
-	if (ehci->pshadow == 0) {
+	if (ehci->pshadow == NULL) {
 		goto fail;
 	}
 	memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *));
diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
--- a/drivers/usb/host/ehci-q.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/host/ehci-q.c	2005-01-13 17:08:38 -08:00
@@ -218,7 +218,7 @@
 __releases(ehci->lock)
 __acquires(ehci->lock)
 {
-	if (likely (urb->hcpriv != 0)) {
+	if (likely (urb->hcpriv != NULL)) {
 		struct ehci_qh	*qh = (struct ehci_qh *) urb->hcpriv;
 
 		/* S-mask in a QH means it's an interrupt urb */
@@ -404,7 +404,7 @@
 	}
 
 	/* last urb's completion might still need calling */
-	if (likely (last != 0)) {
+	if (likely (last != NULL)) {
 		ehci_urb_done (ehci, last->urb, regs);
 		count++;
 		ehci_qtd_free (ehci, last);
@@ -846,7 +846,7 @@
 		/* just one way to queue requests: swap with the dummy qtd.
 		 * only hc or qh_refresh() ever modify the overlay.
 		 */
-		if (likely (qtd != 0)) {
+		if (likely (qtd != NULL)) {
 			struct ehci_qtd		*dummy;
 			dma_addr_t		dma;
 			__le32			token;
@@ -921,12 +921,12 @@
 	/* Control/bulk operations through TTs don't need scheduling,
 	 * the HC and TT handle it when the TT has a buffer ready.
 	 */
-	if (likely (qh != 0)) {
+	if (likely (qh != NULL)) {
 		if (likely (qh->qh_state == QH_STATE_IDLE))
 			qh_link_async (ehci, qh_get (qh));
 	}
 	spin_unlock_irqrestore (&ehci->lock, flags);
-	if (unlikely (qh == 0)) {
+	if (unlikely (qh == NULL)) {
 		qtd_list_free (ehci, urb, qtd_list);
 		return -ENOMEM;
 	}
@@ -967,7 +967,7 @@
 		 * active but idle for a while once it empties.
 		 */
 		if (HCD_IS_RUNNING (ehci_to_hcd(ehci)->state)
-				&& ehci->async->qh_next.qh == 0)
+				&& ehci->async->qh_next.qh == NULL)
 			timer_action (ehci, TIMER_ASYNC_OFF);
 	}
 
@@ -1048,7 +1048,7 @@
 	timer_action_done (ehci, TIMER_ASYNC_SHRINK);
 rescan:
 	qh = ehci->async->qh_next.qh;
-	if (likely (qh != 0)) {
+	if (likely (qh != NULL)) {
 		do {
 			/* clean any finished work for this qh */
 			if (!list_empty (&qh->qtd_list)
diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/host/ehci-sched.c	2005-01-13 17:08:38 -08:00
@@ -604,7 +604,7 @@
 	/* get qh and force any scheduling errors */
 	INIT_LIST_HEAD (&empty);
 	qh = qh_append_tds (ehci, urb, &empty, epnum, &ep->hcpriv);
-	if (qh == 0) {
+	if (qh == NULL) {
 		status = -ENOMEM;
 		goto done;
 	}
@@ -615,7 +615,7 @@
 
 	/* then queue the urb's tds to the qh */
 	qh = qh_append_tds (ehci, urb, qtd_list, epnum, &ep->hcpriv);
-	BUG_ON (qh == 0);
+	BUG_ON (qh == NULL);
 
 	/* ... update usbfs periodic stats */
 	ehci_to_hcd(ehci)->self.bandwidth_int_reqs++;
@@ -638,7 +638,7 @@
 	struct ehci_iso_stream *stream;
 
 	stream = kmalloc(sizeof *stream, mem_flags);
-	if (likely (stream != 0)) {
+	if (likely (stream != NULL)) {
 		memset (stream, 0, sizeof(*stream));
 		INIT_LIST_HEAD(&stream->td_list);
 		INIT_LIST_HEAD(&stream->free_list);
@@ -791,7 +791,7 @@
 static inline struct ehci_iso_stream *
 iso_stream_get (struct ehci_iso_stream *stream)
 {
-	if (likely (stream != 0))
+	if (likely (stream != NULL))
 		stream->refcount++;
 	return stream;
 }
@@ -813,9 +813,9 @@
 	spin_lock_irqsave (&ehci->lock, flags);
 	stream = ep->hcpriv;
 
-	if (unlikely (stream == 0)) {
+	if (unlikely (stream == NULL)) {
 		stream = iso_stream_alloc(GFP_ATOMIC);
-		if (likely (stream != 0)) {
+		if (likely (stream != NULL)) {
 			/* dev->ep owns the initial refcount */
 			ep->hcpriv = stream;
 			stream->ep = ep;
@@ -850,7 +850,7 @@
 
 	size += packets * sizeof (struct ehci_iso_packet);
 	iso_sched = kmalloc (size, mem_flags);
-	if (likely (iso_sched != 0)) {
+	if (likely (iso_sched != NULL)) {
 		memset(iso_sched, 0, size);
 		INIT_LIST_HEAD (&iso_sched->td_list);
 	}
@@ -927,7 +927,7 @@
 	unsigned long		flags;
 
 	sched = iso_sched_alloc (urb->number_of_packets, mem_flags);
-	if (unlikely (sched == 0))
+	if (unlikely (sched == NULL))
 		return -ENOMEM;
 
 	itd_sched_init (sched, stream, urb);
@@ -961,7 +961,7 @@
 			spin_lock_irqsave (&ehci->lock, flags);
 		}
 
-		if (unlikely (0 == itd)) {
+		if (unlikely (NULL == itd)) {
 			iso_sched_free (stream, sched);
 			spin_unlock_irqrestore (&ehci->lock, flags);
 			return -ENOMEM;
@@ -1416,7 +1416,7 @@
 
 	/* Get iso_stream head */
 	stream = iso_stream_find (ehci, urb);
-	if (unlikely (stream == 0)) {
+	if (unlikely (stream == NULL)) {
 		ehci_dbg (ehci, "can't get iso stream\n");
 		return -ENOMEM;
 	}
@@ -1530,7 +1530,7 @@
 	unsigned long		flags;
 
 	iso_sched = iso_sched_alloc (urb->number_of_packets, mem_flags);
-	if (iso_sched == 0)
+	if (iso_sched == NULL)
 		return -ENOMEM;
 
 	sitd_sched_init (iso_sched, stream, urb);
@@ -1784,7 +1784,7 @@
 
 	/* Get iso_stream head */
 	stream = iso_stream_find (ehci, urb);
-	if (stream == 0) {
+	if (stream == NULL) {
 		ehci_dbg (ehci, "can't get iso stream\n");
 		return -ENOMEM;
 	}
@@ -1889,7 +1889,7 @@
 		type = Q_NEXT_TYPE (*hw_p);
 		modified = 0;
 
-		while (q.ptr != 0) {
+		while (q.ptr != NULL) {
 			unsigned		uf;
 			union ehci_shadow	temp;
 			int			live;
diff -Nru a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
--- a/drivers/usb/host/sl811-hcd.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/host/sl811-hcd.c	2005-01-13 17:08:38 -08:00
@@ -1042,7 +1042,7 @@
 
 	usb_put_dev(ep->udev);
 	kfree(ep);
-	hep->hcpriv = 0;
+	hep->hcpriv = NULL;
 }
 
 static int
diff -Nru a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
--- a/drivers/usb/host/uhci-debug.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/host/uhci-debug.c	2005-01-13 17:08:38 -08:00
@@ -95,24 +95,25 @@
 	struct list_head *head, *tmp;
 	struct uhci_td *td;
 	int i = 0, checked = 0, prevactive = 0;
+	__le32 element = qh_element(qh);
 
 	/* Try to make sure there's enough memory */
 	if (len < 80 * 6)
 		return 0;
 
 	out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "",
-			qh, le32_to_cpu(qh->link), le32_to_cpu(qh->element));
+			qh, le32_to_cpu(qh->link), le32_to_cpu(element));
 
-	if (qh->element & UHCI_PTR_QH)
+	if (element & UHCI_PTR_QH)
 		out += sprintf(out, "%*s  Element points to QH (bug?)\n", space, "");
 
-	if (qh->element & UHCI_PTR_DEPTH)
+	if (element & UHCI_PTR_DEPTH)
 		out += sprintf(out, "%*s  Depth traverse\n", space, "");
 
-	if (qh->element & cpu_to_le32(8))
+	if (element & cpu_to_le32(8))
 		out += sprintf(out, "%*s  Bit 3 set (bug?)\n", space, "");
 
-	if (!(qh->element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
+	if (!(element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
 		out += sprintf(out, "%*s  Element is NULL (bug?)\n", space, "");
 
 	if (!qh->urbp) {
@@ -127,7 +128,7 @@
 
 	td = list_entry(tmp, struct uhci_td, list);
 
-	if (cpu_to_le32(td->dma_handle) != (qh->element & ~UHCI_PTR_BITS))
+	if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS))
 		out += sprintf(out, "%*s Element != First TD\n", space, "");
 
 	while (tmp != head) {
@@ -447,7 +448,7 @@
 			if (qh->link != UHCI_PTR_TERM)
 				out += sprintf(out, "    bandwidth reclamation on!\n");
 
-			if (qh->element != cpu_to_le32(uhci->term_td->dma_handle))
+			if (qh_element(qh) != cpu_to_le32(uhci->term_td->dma_handle))
 				out += sprintf(out, "    skel_term_qh element is not set to term_td!\n");
 
 			continue;
diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
--- a/drivers/usb/host/uhci-hcd.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/host/uhci-hcd.c	2005-01-13 17:08:38 -08:00
@@ -236,7 +236,7 @@
 {
 	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 	struct uhci_td *td;
-	u32 *plink;
+	__le32 *plink;
 
 	/* Ordering isn't important here yet since the QH hasn't been */
 	/* inserted into the schedule yet */
@@ -637,8 +637,9 @@
 /*
  * Map status to standard result codes
  *
- * <status> is (td->status & 0xF60000) [a.k.a. uhci_status_bits(td->status)]
- * Note: status does not include the TD_CTRL_NAK bit.
+ * <status> is (td_status(td) & 0xF60000), a.k.a.
+ * uhci_status_bits(td_status(td)).
+ * Note: <status> does not include the TD_CTRL_NAK bit.
  * <dir_out> is True for output TDs and False for input TDs.
  */
 static int uhci_map_status(int status, int dir_out)
@@ -843,21 +844,24 @@
 	/* The rest of the TD's (but the last) are data */
 	tmp = tmp->next;
 	while (tmp != head && tmp->next != head) {
-		td = list_entry(tmp, struct uhci_td, list);
+		unsigned int ctrlstat;
 
+		td = list_entry(tmp, struct uhci_td, list);
 		tmp = tmp->next;
 
-		status = uhci_status_bits(td_status(td));
+		ctrlstat = td_status(td);
+		status = uhci_status_bits(ctrlstat);
 		if (status & TD_CTRL_ACTIVE)
 			return -EINPROGRESS;
 
-		urb->actual_length += uhci_actual_length(td_status(td));
+		urb->actual_length += uhci_actual_length(ctrlstat);
 
 		if (status)
 			goto td_error;
 
 		/* Check to see if we received a short packet */
-		if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) {
+		if (uhci_actual_length(ctrlstat) <
+				uhci_expected_length(td_token(td))) {
 			if (urb->transfer_flags & URB_SHORT_NOT_OK) {
 				ret = -EREMOTEIO;
 				goto err;
@@ -1031,16 +1035,19 @@
 	urb->actual_length = 0;
 
 	list_for_each_entry(td, &urbp->td_list, list) {
-		status = uhci_status_bits(td_status(td));
+		unsigned int ctrlstat = td_status(td);
+
+		status = uhci_status_bits(ctrlstat);
 		if (status & TD_CTRL_ACTIVE)
 			return -EINPROGRESS;
 
-		urb->actual_length += uhci_actual_length(td_status(td));
+		urb->actual_length += uhci_actual_length(ctrlstat);
 
 		if (status)
 			goto td_error;
 
-		if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) {
+		if (uhci_actual_length(ctrlstat) <
+				uhci_expected_length(td_token(td))) {
 			if (urb->transfer_flags & URB_SHORT_NOT_OK) {
 				ret = -EREMOTEIO;
 				goto err;
@@ -1209,15 +1216,16 @@
 	i = 0;
 	list_for_each_entry(td, &urbp->td_list, list) {
 		int actlength;
+		unsigned int ctrlstat = td_status(td);
 
-		if (td_status(td) & TD_CTRL_ACTIVE)
+		if (ctrlstat & TD_CTRL_ACTIVE)
 			return -EINPROGRESS;
 
-		actlength = uhci_actual_length(td_status(td));
+		actlength = uhci_actual_length(ctrlstat);
 		urb->iso_frame_desc[i].actual_length = actlength;
 		urb->actual_length += actlength;
 
-		status = uhci_map_status(uhci_status_bits(td_status(td)),
+		status = uhci_map_status(uhci_status_bits(ctrlstat),
 				usb_pipeout(urb->pipe));
 		urb->iso_frame_desc[i].status = status;
 		if (status) {
@@ -1423,19 +1431,21 @@
 	 */
 	head = &urbp->td_list;
 	list_for_each_entry(td, head, list) {
-		if (!(td_status(td) & TD_CTRL_ACTIVE) &&
-				(uhci_actual_length(td_status(td)) <
+		unsigned int ctrlstat = td_status(td);
+
+		if (!(ctrlstat & TD_CTRL_ACTIVE) &&
+				(uhci_actual_length(ctrlstat) <
 				 uhci_expected_length(td_token(td)) ||
 				td->list.next == head))
 			usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
 				uhci_packetout(td_token(td)),
 				uhci_toggle(td_token(td)) ^ 1);
-		else if ((td_status(td) & TD_CTRL_ACTIVE) && !prevactive)
+		else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive)
 			usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
 				uhci_packetout(td_token(td)),
 				uhci_toggle(td_token(td)));
 
-		prevactive = td_status(td) & TD_CTRL_ACTIVE;
+		prevactive = ctrlstat & TD_CTRL_ACTIVE;
 	}
 
 	uhci_delete_queued_urb(uhci, urb);
diff -Nru a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
--- a/drivers/usb/host/uhci-hcd.h	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/host/uhci-hcd.h	2005-01-13 17:08:38 -08:00
@@ -119,9 +119,19 @@
 } __attribute__((aligned(16)));
 
 /*
+ * We need a special accessor for the element pointer because it is
+ * subject to asynchronous updates by the controller
+ */
+static __le32 inline qh_element(struct uhci_qh *qh) {
+	__le32 element = qh->element;
+
+	barrier();
+	return element;
+}
+
+/*
  * for TD <status>:
  */
-#define td_status(td)		le32_to_cpu((td)->status)
 #define TD_CTRL_SPD		(1 << 29)	/* Short Packet Detect */
 #define TD_CTRL_C_ERR_MASK	(3 << 27)	/* Error Counter bits */
 #define TD_CTRL_C_ERR_SHIFT	27
@@ -202,6 +212,18 @@
 	int frame;			/* for iso: what frame? */
 	struct list_head fl_list;	/* P: uhci->frame_list_lock */
 } __attribute__((aligned(16)));
+
+/*
+ * We need a special accessor for the control/status word because it is
+ * subject to asynchronous updates by the controller
+ */
+static u32 inline td_status(struct uhci_td *td) {
+	__le32 status = td->status;
+
+	barrier();
+	return le32_to_cpu(status);
+}
+
 
 /*
  * The UHCI driver places Interrupt, Control and Bulk into QH's both
diff -Nru a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c
--- a/drivers/usb/image/mdc800.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/image/mdc800.c	2005-01-13 17:08:38 -08:00
@@ -456,7 +456,7 @@
 	dbg ("(mdc800_usb_probe) called.");
 
 
-	if (mdc800->dev != 0)
+	if (mdc800->dev != NULL)
 	{
 		warn ("only one Mustek MDC800 is supported.");
 		return -ENODEV;
@@ -1045,7 +1045,7 @@
 
 cleanup_on_fail:
 
-	if (mdc800 != 0)
+	if (mdc800 != NULL)
 	{
 		err ("can't alloc memory!");
 
diff -Nru a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
--- a/drivers/usb/input/hid-core.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/input/hid-core.c	2005-01-13 17:08:38 -08:00
@@ -676,7 +676,7 @@
 	parser->device = device;
 
 	end = start + size;
-	while ((start = fetch_item(start, end, &item)) != 0) {
+	while ((start = fetch_item(start, end, &item)) != NULL) {
 
 		if (item.format != HID_ITEM_FORMAT_SHORT) {
 			dbg("unexpected long global item");
diff -Nru a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h
--- a/drivers/usb/media/sn9c102.h	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/media/sn9c102.h	2005-01-13 17:08:38 -08:00
@@ -1,7 +1,7 @@
 /***************************************************************************
  * V4L2 driver for SN9C10x PC Camera Controllers                           *
  *                                                                         *
- * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
  *                                                                         *
  * 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    *
@@ -42,6 +42,7 @@
 #define SN9C102_DEBUG_LEVEL       2
 #define SN9C102_MAX_DEVICES       64
 #define SN9C102_PRESERVE_IMGSCALE 0
+#define SN9C102_FORCE_MUNMAP      0
 #define SN9C102_MAX_FRAMES        32
 #define SN9C102_URBS              2
 #define SN9C102_ISO_PACKETS       7
@@ -55,8 +56,8 @@
 #define SN9C102_MODULE_AUTHOR   "(C) 2004 Luca Risolia"
 #define SN9C102_AUTHOR_EMAIL    "<luca.risolia@studio.unibo.it>"
 #define SN9C102_MODULE_LICENSE  "GPL"
-#define SN9C102_MODULE_VERSION  "1:1.20"
-#define SN9C102_MODULE_VERSION_CODE  KERNEL_VERSION(1, 0, 20)
+#define SN9C102_MODULE_VERSION  "1:1.22"
+#define SN9C102_MODULE_VERSION_CODE  KERNEL_VERSION(1, 0, 22)
 
 enum sn9c102_bridge {
 	BRIDGE_SN9C101 = 0x01,
@@ -109,6 +110,10 @@
 	sn9c102_sof_header_t frame_header;
 };
 
+struct sn9c102_module_param {
+	u8 force_munmap;
+};
+
 static DECLARE_MUTEX(sn9c102_sysfs_lock);
 static DECLARE_RWSEM(sn9c102_disconnect);
 
@@ -137,6 +142,8 @@
 	struct sn9c102_sysfs_attr sysfs;
 	sn9c102_sof_header_t sof_header;
 	u16 reg[32];
+
+	struct sn9c102_module_param module_param;
 
 	enum sn9c102_dev_state state;
 	u8 users;
diff -Nru a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c
--- a/drivers/usb/media/sn9c102_core.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/media/sn9c102_core.c	2005-01-13 17:08:38 -08:00
@@ -1,7 +1,7 @@
 /***************************************************************************
  * V4L2 driver for SN9C10x PC Camera Controllers                           *
  *                                                                         *
- * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
  *                                                                         *
  * 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    *
@@ -37,6 +37,7 @@
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
 #include <linux/page-flags.h>
+#include <linux/byteorder/generic.h>
 #include <asm/page.h>
 #include <asm/uaccess.h>
 
@@ -65,6 +66,20 @@
                  "\none and for every other camera."
                  "\n");
 
+static short force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] = 
+                               SN9C102_FORCE_MUNMAP};
+module_param_array(force_munmap, bool, NULL, 0444);
+MODULE_PARM_DESC(force_munmap,
+                 "\n<0|1[,...]> Force the application to unmap previously "
+                 "\nmapped buffer memory before calling any VIDIOC_S_CROP or "
+                 "\nVIDIOC_S_FMT ioctl's. Not all the applications support "
+                 "\nthis feature. This parameter is specific for each "
+                 "\ndetected camera."
+                 "\n 0 = do not force memory unmapping"
+                 "\n 1 = force memory unmapping (save memory)"
+                 "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
+                 "\n");
+
 #ifdef SN9C102_DEBUG
 static unsigned short debug = SN9C102_DEBUG_LEVEL;
 module_param(debug, ushort, 0644);
@@ -141,10 +156,16 @@
 }
 
 
-static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count)
+static u32 
+sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, 
+                        enum sn9c102_io_method io)
 {
 	struct v4l2_pix_format* p = &(cam->sensor->pix_format);
-	const size_t imagesize = (p->width * p->height * p->priv)/8;
+	struct v4l2_rect* r = &(cam->sensor->cropcap.bounds);
+	const size_t imagesize = cam->module_param.force_munmap ||
+	                         io == IO_READ ?
+	                         (p->width * p->height * p->priv)/8 :
+	                         (r->width * r->height * p->priv)/8;
 	void* buff = NULL;
 	u32 i;
 
@@ -911,11 +932,6 @@
 		return -ENODEV;
 	}
 
-	if (!(cam->sensor->sysfs_ops & SN9C102_I2C_WRITE)) {
-		up(&sn9c102_sysfs_lock);
-		return -ENOSYS;
-	}
-
 	value = sn9c102_strtou8(buf, len, &count);
 	if (!count) {
 		up(&sn9c102_sysfs_lock);
@@ -1047,6 +1063,11 @@
 		return -ENODEV;
 	}
 
+	if (!(cam->sensor->sysfs_ops & SN9C102_I2C_WRITE)) {
+		up(&sn9c102_sysfs_lock);
+		return -ENOSYS;
+	}
+
 	value = sn9c102_strtou8(buf, len, &count);
 	if (!count) {
 		up(&sn9c102_sysfs_lock);
@@ -1514,7 +1535,7 @@
 	}
 
 	if (cam->io == IO_NONE) {
-		if (!sn9c102_request_buffers(cam, cam->nreadbuffers)) {
+		if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) {
 			DBG(1, "read() failed, not enough memory")
 			up(&cam->fileop_sem);
 			return -ENOMEM;
@@ -1594,7 +1615,7 @@
 	}
 
 	if (cam->io == IO_NONE) {
-		if (!sn9c102_request_buffers(cam, 2)) {
+		if (!sn9c102_request_buffers(cam, 2, IO_READ)) {
 			DBG(1, "poll() failed, not enough memory")
 			goto error;
 		}
@@ -1811,6 +1832,7 @@
 		return err;
 	}
 
+	case VIDIOC_S_CTRL_OLD:
 	case VIDIOC_S_CTRL:
 	{
 		struct sn9c102_sensor* s = cam->sensor;
@@ -1895,12 +1917,13 @@
 		if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 			return -EINVAL;
 
-		for (i = 0; i < cam->nbuffers; i++)
-			if (cam->frame[i].vma_use_count) {
-				DBG(3, "VIDIOC_S_CROP failed. "
-				       "Unmap the buffers first.")
-				return -EINVAL;
-			}
+		if (cam->module_param.force_munmap)
+			for (i = 0; i < cam->nbuffers; i++)
+				if (cam->frame[i].vma_use_count) {
+					DBG(3, "VIDIOC_S_CROP failed. "
+					       "Unmap the buffers first.")
+					return -EINVAL;
+				}
 
 		/* Preserve R,G or B origin */
 		rect->left = (s->_rect.left & 1L) ?
@@ -1947,7 +1970,8 @@
 			return -EFAULT;
 		}
 
-		sn9c102_release_buffers(cam);
+		if (cam->module_param.force_munmap)
+			sn9c102_release_buffers(cam);
 
 		err = sn9c102_set_crop(cam, rect);
 		if (s->set_crop)
@@ -1966,7 +1990,9 @@
 		s->pix_format.height = rect->height/scale;
 		memcpy(&(s->_rect), rect, sizeof(*rect));
 
-		if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) {
+		if (cam->module_param.force_munmap &&
+		    nbuffers != sn9c102_request_buffers(cam, nbuffers,
+		                                        cam->io)) {
 			cam->state |= DEV_MISCONFIGURED;
 			DBG(1, "VIDIOC_S_CROP failed because of not enough "
 			       "memory. To use the camera, close and open "
@@ -2103,12 +2129,13 @@
 			return 0;
 		}
 
-		for (i = 0; i < cam->nbuffers; i++)
-			if (cam->frame[i].vma_use_count) {
-				DBG(3, "VIDIOC_S_FMT failed. "
-				       "Unmap the buffers first.")
-				return -EINVAL;
-			}
+		if (cam->module_param.force_munmap)
+			for (i = 0; i < cam->nbuffers; i++)
+				if (cam->frame[i].vma_use_count) {
+					DBG(3, "VIDIOC_S_FMT failed. "
+					       "Unmap the buffers first.")
+					return -EINVAL;
+				}
 
 		if (cam->stream == STREAM_ON)
 			if ((err = sn9c102_stream_interrupt(cam)))
@@ -2119,7 +2146,8 @@
 			return -EFAULT;
 		}
 
-		sn9c102_release_buffers(cam);
+		if (cam->module_param.force_munmap)
+			sn9c102_release_buffers(cam);
 
 		err += sn9c102_set_pix_format(cam, pix);
 		err += sn9c102_set_crop(cam, &rect);
@@ -2140,7 +2168,9 @@
 		memcpy(pfmt, pix, sizeof(*pix));
 		memcpy(&(s->_rect), &rect, sizeof(rect));
 
-		if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) {
+		if (cam->module_param.force_munmap &&
+		    nbuffers != sn9c102_request_buffers(cam, nbuffers,
+		                                        cam->io)) {
 			cam->state |= DEV_MISCONFIGURED;
 			DBG(1, "VIDIOC_S_FMT failed because of not enough "
 			       "memory. To use the camera, close and open "
@@ -2228,7 +2258,8 @@
 
 		sn9c102_release_buffers(cam);
 		if (rb.count)
-			rb.count = sn9c102_request_buffers(cam, rb.count);
+			rb.count = sn9c102_request_buffers(cam, rb.count,
+			                                   IO_MMAP);
 
 		if (copy_to_user(arg, &rb, sizeof(rb))) {
 			sn9c102_release_buffers(cam);
@@ -2402,6 +2433,7 @@
 		return 0;
 	}
 
+	case VIDIOC_S_PARM_OLD:
 	case VIDIOC_S_PARM:
 	{
 		struct v4l2_streamparm sp;
@@ -2496,8 +2528,10 @@
 
 	n = sizeof(sn9c102_id_table)/sizeof(sn9c102_id_table[0]);
 	for (i = 0; i < n-1; i++)
-		if (le16_to_cpu(udev->descriptor.idVendor) == sn9c102_id_table[i].idVendor &&
-		    le16_to_cpu(udev->descriptor.idProduct) == sn9c102_id_table[i].idProduct)
+		if (le16_to_cpu(udev->descriptor.idVendor) == 
+		    sn9c102_id_table[i].idVendor &&
+		    le16_to_cpu(udev->descriptor.idProduct) ==
+		    sn9c102_id_table[i].idProduct)
 			break;
 	if (i == n-1)
 		return -ENODEV;
@@ -2595,6 +2629,10 @@
 	}
 
 	DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor)
+
+	cam->module_param.force_munmap = force_munmap[dev_nr];
+
+	dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
 
 	sn9c102_create_sysfs(cam);
 	DBG(2, "Optional device control through 'sysfs' interface ready")
diff -Nru a/drivers/usb/media/sn9c102_hv7131d.c b/drivers/usb/media/sn9c102_hv7131d.c
--- a/drivers/usb/media/sn9c102_hv7131d.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/media/sn9c102_hv7131d.c	2005-01-13 17:08:38 -08:00
@@ -2,7 +2,7 @@
  * Plug-in for HV7131D image sensor connected to the SN9C10x PC Camera     *
  * Controllers                                                             *
  *                                                                         *
- * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
  *                                                                         *
  * 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    *
diff -Nru a/drivers/usb/media/sn9c102_mi0343.c b/drivers/usb/media/sn9c102_mi0343.c
--- a/drivers/usb/media/sn9c102_mi0343.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/media/sn9c102_mi0343.c	2005-01-13 17:08:38 -08:00
@@ -2,7 +2,7 @@
  * Plug-in for MI-0343 image sensor connected to the SN9C10x PC Camera     *
  * Controllers                                                             *
  *                                                                         *
- * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
  *                                                                         *
  * 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    *
diff -Nru a/drivers/usb/media/sn9c102_pas106b.c b/drivers/usb/media/sn9c102_pas106b.c
--- a/drivers/usb/media/sn9c102_pas106b.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/media/sn9c102_pas106b.c	2005-01-13 17:08:38 -08:00
@@ -2,7 +2,7 @@
  * Plug-in for PAS106B image sensor connected to the SN9C10x PC Camera     *
  * Controllers                                                             *
  *                                                                         *
- * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
  *                                                                         *
  * 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    *
diff -Nru a/drivers/usb/media/sn9c102_sensor.h b/drivers/usb/media/sn9c102_sensor.h
--- a/drivers/usb/media/sn9c102_sensor.h	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/media/sn9c102_sensor.h	2005-01-13 17:08:38 -08:00
@@ -1,7 +1,7 @@
 /***************************************************************************
  * API for image sensors connected to the SN9C10x PC Camera Controllers    *
  *                                                                         *
- * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
  *                                                                         *
  * 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    *
diff -Nru a/drivers/usb/media/sn9c102_tas5110c1b.c b/drivers/usb/media/sn9c102_tas5110c1b.c
--- a/drivers/usb/media/sn9c102_tas5110c1b.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/media/sn9c102_tas5110c1b.c	2005-01-13 17:08:38 -08:00
@@ -2,7 +2,7 @@
  * Plug-in for TAS5110C1B image sensor connected to the SN9C10x PC Camera  *
  * Controllers                                                             *
  *                                                                         *
- * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
  *                                                                         *
  * 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    *
diff -Nru a/drivers/usb/media/sn9c102_tas5130d1b.c b/drivers/usb/media/sn9c102_tas5130d1b.c
--- a/drivers/usb/media/sn9c102_tas5130d1b.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/media/sn9c102_tas5130d1b.c	2005-01-13 17:08:38 -08:00
@@ -2,7 +2,7 @@
  * Plug-in for TAS5130D1B image sensor connected to the SN9C10x PC Camera  *
  * Controllers                                                             *
  *                                                                         *
- * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
  *                                                                         *
  * 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    *
diff -Nru a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
--- a/drivers/usb/misc/Kconfig	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/misc/Kconfig	2005-01-13 17:08:38 -08:00
@@ -123,6 +123,20 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called phidgetservo.
 
+config USB_IDMOUSE
+	tristate "Siemens ID USB Mouse Fingerprint sensor support"
+	depends on USB
+	help
+	  Say Y here if you want to use the fingerprint sensor on
+	  the Siemens ID Mouse. There is also a Siemens ID Mouse
+	  _Professional_, which has not been tested with this driver,
+	  but uses the same sensor and may therefore work.
+
+	  This driver creates an entry "/dev/idmouseX" or "/dev/usb/idmouseX",
+	  which can be used by, e.g.,"cat /dev/idmouse0 > fingerprint.pnm".
+
+	  See also <http://www.fs.tum.de/~echtler/idmouse/>.
+
 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	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/misc/Makefile	2005-01-13 17:08:38 -08:00
@@ -7,6 +7,7 @@
 obj-$(CONFIG_USB_CYTHERM)	+= cytherm.o
 obj-$(CONFIG_USB_EMI26)		+= emi26.o
 obj-$(CONFIG_USB_EMI62)		+= emi62.o
+obj-$(CONFIG_USB_IDMOUSE)	+= idmouse.o
 obj-$(CONFIG_USB_LCD)		+= usblcd.o
 obj-$(CONFIG_USB_LED)		+= usbled.o
 obj-$(CONFIG_USB_LEGOTOWER)	+= legousbtower.o
diff -Nru a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/usb/misc/idmouse.c	2005-01-13 17:08:38 -08:00
@@ -0,0 +1,442 @@
+/* Siemens ID Mouse driver v0.5
+
+  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.
+
+  Copyright (C) 2004-5 by Florian 'Floe' Echtler  <echtler@fs.tum.de>
+                      and Andreas  'ad'  Deresch <aderesch@fs.tum.de>
+
+  Derived from the USB Skeleton driver 1.1,
+  Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
+
+*/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
+#define WIDTH 225
+#define HEIGHT 288
+#define HEADER "P5 225 288 255 "
+#define IMGSIZE ((WIDTH * HEIGHT) + sizeof(HEADER)-1)
+
+/* Version Information */
+#define DRIVER_VERSION "0.5"
+#define DRIVER_SHORT   "idmouse"
+#define DRIVER_AUTHOR  "Florian 'Floe' Echtler <echtler@fs.tum.de>"
+#define DRIVER_DESC    "Siemens ID Mouse FingerTIP Sensor Driver"
+
+/* Siemens ID Mouse */
+#define USB_IDMOUSE_VENDOR_ID  0x0681
+#define USB_IDMOUSE_PRODUCT_ID 0x0005
+
+/* we still need a minor number */
+#define USB_IDMOUSE_MINOR_BASE 132
+
+static struct usb_device_id idmouse_table[] = {
+	{USB_DEVICE(USB_IDMOUSE_VENDOR_ID, USB_IDMOUSE_PRODUCT_ID)},
+	{} /* null entry at the end */
+};
+
+MODULE_DEVICE_TABLE(usb, idmouse_table);
+
+/* structure to hold all of our device specific stuff */
+struct usb_idmouse {
+
+	struct usb_device *udev; /* save off the usb device pointer */
+	struct usb_interface *interface; /* the interface for this device */
+
+	unsigned char *bulk_in_buffer; /* the buffer to receive data */
+	size_t bulk_in_size; /* the size of the receive buffer */
+	__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
+
+	int open; /* if the port is open or not */
+	int present; /* if the device is not disconnected */
+	struct semaphore sem; /* locks this structure */
+
+};
+
+/* local function prototypes */
+static ssize_t idmouse_read(struct file *file, char __user *buffer,
+				size_t count, loff_t * ppos);
+
+static int idmouse_open(struct inode *inode, struct file *file);
+static int idmouse_release(struct inode *inode, struct file *file);
+
+static int idmouse_probe(struct usb_interface *interface,
+				const struct usb_device_id *id);
+
+static void idmouse_disconnect(struct usb_interface *interface);
+
+/* file operation pointers */
+static struct file_operations idmouse_fops = {
+	.owner = THIS_MODULE,
+	.read = idmouse_read,
+	.open = idmouse_open,
+	.release = idmouse_release,
+};
+
+/* class driver information for devfs */
+static struct usb_class_driver idmouse_class = {
+	.name = "usb/idmouse%d",
+	.fops = &idmouse_fops,
+	.mode = S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH, /* filemode (char, 444) */
+	.minor_base = USB_IDMOUSE_MINOR_BASE,
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver idmouse_driver = {
+	.owner = THIS_MODULE,
+	.name = DRIVER_SHORT,
+	.probe = idmouse_probe,
+	.disconnect = idmouse_disconnect,
+	.id_table = idmouse_table,
+};
+
+// prevent races between open() and disconnect()
+static DECLARE_MUTEX(disconnect_sem);
+
+static int idmouse_create_image(struct usb_idmouse *dev)
+{
+	int bytes_read = 0;
+	int bulk_read = 0;
+	int result = 0;
+
+	if (dev->bulk_in_size < sizeof(HEADER))
+		return -ENOMEM;
+
+	memcpy(dev->bulk_in_buffer,HEADER,sizeof(HEADER)-1);
+	bytes_read += sizeof(HEADER)-1;
+
+	/* Dump the setup packets. Yes, they are uncommented, simply 
+	   because they were sniffed under Windows using SnoopyPro.
+	   I _guess_ that 0x22 is a kind of reset command and 0x21 
+	   means init..
+	*/
+	result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+				0x21, 0x42, 0x0001, 0x0002, NULL, 0, HZ);
+	if (result < 0)
+		return result;
+	result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+				0x20, 0x42, 0x0001, 0x0002, NULL, 0, HZ);
+	if (result < 0)
+		return result;
+	result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+				0x22, 0x42, 0x0000, 0x0002, NULL, 0, HZ);
+	if (result < 0)
+		return result;
+
+	result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+				0x21, 0x42, 0x0001, 0x0002, NULL, 0, HZ);
+	if (result < 0)
+		return result;
+	result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+				0x20, 0x42, 0x0001, 0x0002, NULL, 0, HZ);
+	if (result < 0)
+		return result;
+	result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+				0x20, 0x42, 0x0000, 0x0002, NULL, 0, HZ);
+	if (result < 0)
+		return result;
+
+	/* loop over a blocking bulk read to get data from the device */
+	while (bytes_read < IMGSIZE) {
+		result = usb_bulk_msg (dev->udev,
+				usb_rcvbulkpipe (dev->udev, dev->bulk_in_endpointAddr),
+				dev->bulk_in_buffer + bytes_read,
+				dev->bulk_in_size, &bulk_read, HZ * 5);
+		if (result < 0)
+			return result;
+		if (signal_pending(current))
+			return -EINTR;
+		bytes_read += bulk_read;
+	}
+
+	/* reset the device */
+	result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+				0x22, 0x42, 0x0000, 0x0002, NULL, 0, HZ);
+	if (result < 0)
+		return result;
+
+	/* should be IMGSIZE == 64815 */
+	dbg("read %d bytes fingerprint data", bytes_read);
+	return 0;
+}
+
+static inline void idmouse_delete(struct usb_idmouse *dev)
+{
+	kfree(dev->bulk_in_buffer);
+	kfree(dev);
+}
+
+static int idmouse_open(struct inode *inode, struct file *file)
+{
+	struct usb_idmouse *dev = NULL;
+	struct usb_interface *interface;
+	int result = 0;
+
+	/* prevent disconnects */
+	down(&disconnect_sem);
+
+	/* get the interface from minor number and driver information */
+	interface = usb_find_interface (&idmouse_driver, iminor (inode));
+	if (!interface) {
+		up(&disconnect_sem);
+		return -ENODEV;
+	}
+	/* get the device information block from the interface */
+	dev = usb_get_intfdata(interface);
+	if (!dev) {
+		up(&disconnect_sem);
+		return -ENODEV;
+	}
+
+	/* lock this device */
+	down(&dev->sem);
+
+	/* check if already open */
+	if (dev->open) {
+
+		/* already open, so fail */
+		result = -EBUSY;
+
+	} else {
+
+		/* create a new image and check for success */
+		result = idmouse_create_image (dev);
+		if (result)
+			goto error;
+
+		/* increment our usage count for the driver */
+		++dev->open;
+
+		/* save our object in the file's private structure */
+		file->private_data = dev;
+
+	} 
+
+error:
+
+	/* unlock this device */
+	up(&dev->sem);
+
+	/* unlock the disconnect semaphore */
+	up(&disconnect_sem);
+	return result;
+}
+
+static int idmouse_release(struct inode *inode, struct file *file)
+{
+	struct usb_idmouse *dev;
+
+	/* prevent a race condition with open() */
+	down(&disconnect_sem);
+
+	dev = (struct usb_idmouse *) file->private_data;
+
+	if (dev == NULL) {
+		up(&disconnect_sem);
+		return -ENODEV;
+	}
+
+	/* lock our device */
+	down(&dev->sem);
+
+	/* are we really open? */
+	if (dev->open <= 0) {
+		up(&dev->sem);
+		up(&disconnect_sem);
+		return -ENODEV;
+	}
+
+	--dev->open;
+
+	if (!dev->present) {
+		/* the device was unplugged before the file was released */
+		up(&dev->sem);
+		idmouse_delete(dev);
+		up(&disconnect_sem);
+		return 0;
+	}
+
+	up(&dev->sem);
+	up(&disconnect_sem);
+	return 0;
+}
+
+static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count,
+				loff_t * ppos)
+{
+	struct usb_idmouse *dev;
+	int result = 0;
+
+	dev = (struct usb_idmouse *) file->private_data;
+
+	// lock this object
+	down (&dev->sem);
+
+	// verify that the device wasn't unplugged
+	if (!dev->present) {
+		up (&dev->sem);
+		return -ENODEV;
+	}
+
+	if (*ppos >= IMGSIZE) {
+		up (&dev->sem);
+		return 0;
+	}
+
+	count = min ((loff_t)count, IMGSIZE - (*ppos));
+
+	if (copy_to_user (buffer, dev->bulk_in_buffer + *ppos, count)) {
+		result = -EFAULT;
+	} else {
+		result = count;
+		*ppos += count;
+	}
+
+	// unlock the device 
+	up(&dev->sem);
+	return result;
+}
+
+static int idmouse_probe(struct usb_interface *interface,
+				const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct usb_idmouse *dev = NULL;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	size_t buffer_size;
+	int result;
+
+	/* check if we have gotten the data or the hid interface */
+	iface_desc = &interface->altsetting[0];
+	if (iface_desc->desc.bInterfaceClass != 0x0A)
+		return -ENODEV;
+
+	/* allocate memory for our device state and initialize it */
+	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL)
+		return -ENOMEM;
+	memset(dev, 0x00, sizeof(*dev));
+
+	init_MUTEX(&dev->sem);
+	dev->udev = udev;
+	dev->interface = interface;
+
+	/* set up the endpoint information - use only the first bulk-in endpoint */
+	endpoint = &iface_desc->endpoint[0].desc;
+	if (!dev->bulk_in_endpointAddr
+		&& (endpoint->bEndpointAddress & USB_DIR_IN)
+		&& ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		USB_ENDPOINT_XFER_BULK)) {
+
+		/* we found a bulk in endpoint */
+		buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+		dev->bulk_in_size = buffer_size;
+		dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+		dev->bulk_in_buffer =
+			kmalloc(IMGSIZE + buffer_size, GFP_KERNEL);
+
+		if (!dev->bulk_in_buffer) {
+			err("Unable to allocate input buffer.");
+			idmouse_delete(dev);
+			return -ENOMEM;
+		}
+	}
+
+	if (!(dev->bulk_in_endpointAddr)) {
+		err("Unable to find bulk-in endpoint.");
+		idmouse_delete(dev);
+		return -ENODEV;
+	}
+	/* allow device read, write and ioctl */
+	dev->present = 1;
+
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata(interface, dev);
+	result = usb_register_dev(interface, &idmouse_class);
+	if (result) {
+		/* something prevented us from registering this device */
+		err("Unble to allocate minor number.");
+		usb_set_intfdata(interface, NULL);
+		idmouse_delete(dev);
+		return result;
+	}
+
+	/* be noisy */
+	dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC);
+
+	return 0;
+}
+
+static void idmouse_disconnect(struct usb_interface *interface)
+{
+	struct usb_idmouse *dev;
+
+	/* prevent races with open() */
+	down(&disconnect_sem);
+
+	/* get device structure */
+	dev = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+
+	/* lock it */
+	down(&dev->sem);
+
+	/* give back our minor */
+	usb_deregister_dev(interface, &idmouse_class);
+
+	/* prevent device read, write and ioctl */
+	dev->present = 0;
+
+	/* unlock */
+	up(&dev->sem);
+
+	/* if the device is opened, idmouse_release will clean this up */
+	if (!dev->open)
+		idmouse_delete(dev);
+
+	up(&disconnect_sem);
+
+	info("%s disconnected", DRIVER_DESC);
+}
+
+static int __init usb_idmouse_init(void)
+{
+	int result;
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+
+	/* register this driver with the USB subsystem */
+	result = usb_register(&idmouse_driver);
+	if (result)
+		err("Unable to register device (error %d).", result);
+
+	return result;
+}
+
+static void __exit usb_idmouse_exit(void)
+{
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&idmouse_driver);
+}
+
+module_init(usb_idmouse_init);
+module_exit(usb_idmouse_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	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/misc/usbtest.c	2005-01-13 17:08:38 -08:00
@@ -1204,7 +1204,7 @@
 	struct urb	*urb;
 
 	urb = simple_alloc_urb (testdev_to_usbdev (dev), 0, 512);
-	if (urb == 0)
+	if (urb == NULL)
 		return -ENOMEM;
 
 	if (dev->in_pipe) {
@@ -1862,7 +1862,7 @@
 	dev->intf = intf;
 
 	/* cacheline-aligned scratch for i/o */
-	if ((dev->buf = kmalloc (TBUF_SIZE, SLAB_KERNEL)) == 0) {
+	if ((dev->buf = kmalloc (TBUF_SIZE, SLAB_KERNEL)) == NULL) {
 		kfree (dev);
 		return -ENOMEM;
 	}
diff -Nru a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c
--- a/drivers/usb/net/pegasus.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/net/pegasus.c	2005-01-13 17:08:38 -08:00
@@ -28,6 +28,8 @@
  * 			is out of the interrupt routine.
  */
 
+#undef	DEBUG
+
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/init.h>
@@ -45,7 +47,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.5.12 (2003/06/06)"
+#define DRIVER_VERSION "v0.5.12 (2005/01/13)"
 #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
 #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
 
@@ -712,11 +714,11 @@
 {
 	pegasus_t *pegasus = urb->context;
 	struct net_device *net;
-	__u8 *d;
 	int status;
 
 	if (!pegasus)
 		return;
+	net = pegasus->net;
 
 	switch (urb->status) {
 	case 0:
@@ -726,36 +728,50 @@
 	case -ESHUTDOWN:
 		return;
 	default:
-		info("intr status %d", urb->status);
+		/* some Pegasus-I products report LOTS of data
+		 * toggle errors... avoid log spamming
+		 */
+		pr_debug("%s: intr status %d\n", net->name, urb->status);
 	}
 
-	d = urb->transfer_buffer;
-	net = pegasus->net;
-	if (d[0] & 0xfc) {
-		pegasus->stats.tx_errors++;
-		if (d[0] & TX_UNDERRUN)
-			pegasus->stats.tx_fifo_errors++;
-		if (d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT))
-			pegasus->stats.tx_aborted_errors++;
-		if (d[0] & LATE_COL)
-			pegasus->stats.tx_window_errors++;
-		if (d[5] & LINK_STATUS) {
-			netif_carrier_on(net);
-		} else {
-			pegasus->stats.tx_carrier_errors++;
-			netif_carrier_off(net);	
+	if (urb->actual_length >= 6) {
+		u8	* d = urb->transfer_buffer;
+
+		/* byte 0 == tx_status1, reg 2B */
+		if (d[0] & (TX_UNDERRUN|EXCESSIVE_COL
+					|LATE_COL|JABBER_TIMEOUT)) {
+			pegasus->stats.tx_errors++;
+			if (d[0] & TX_UNDERRUN)
+				pegasus->stats.tx_fifo_errors++;
+			if (d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT))
+				pegasus->stats.tx_aborted_errors++;
+			if (d[0] & LATE_COL)
+				pegasus->stats.tx_window_errors++;
 		}
+
+		/* d[5].LINK_STATUS lies on some adapters.
+		 * d[0].NO_CARRIER kicks in only with failed TX.
+		 * ... so monitoring with MII may be safest.
+		 */
+		if (d[0] & NO_CARRIER)
+			netif_carrier_off(net);	
+		else
+			netif_carrier_on(net);
+
+		/* bytes 3-4 == rx_lostpkt, reg 2E/2F */
+		pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4];
 	}
 
 	status = usb_submit_urb(urb, SLAB_ATOMIC);
 	if (status)
-		err("%s: can't resubmit interrupt urb, %d", net->name, status);
+		printk(KERN_ERR "%s: can't resubmit interrupt urb, %d\n",
+				net->name, status);
 }
 
 static void pegasus_tx_timeout(struct net_device *net)
 {
 	pegasus_t *pegasus = netdev_priv(net);
-	warn("%s: Tx timed out.", net->name);
+	printk(KERN_WARNING "%s: tx timeout\n", net->name);
 	pegasus->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
 	usb_unlink_urb(pegasus->tx_urb);
 	pegasus->stats.tx_errors++;
@@ -948,14 +964,57 @@
 	usb_make_path(pegasus->usb, info->bus_info, sizeof (info->bus_info));
 }
 
-#ifdef	CONFIG_MII
-static int pegasus_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+/* also handles three patterns of some kind in hardware */
+#define	WOL_SUPPORTED	(WAKE_MAGIC|WAKE_PHY)
+
+static void
+pegasus_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+	pegasus_t	*pegasus = netdev_priv(dev);
+
+	wol->supported = WAKE_MAGIC | WAKE_PHY;
+	wol->wolopts = pegasus->wolopts;
+}
+
+static int
+pegasus_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+	pegasus_t	*pegasus = netdev_priv(dev);
+	u8		reg78 = 0x04;
+	
+	if (wol->wolopts & ~WOL_SUPPORTED)
+		return -EINVAL;
+
+	if (wol->wolopts & WAKE_MAGIC)
+		reg78 |= 0x80;
+	if (wol->wolopts & WAKE_PHY)
+		reg78 |= 0x40;
+	if (wol->wolopts)
+		pegasus->eth_regs[0] |= 0x10;
+	else
+		pegasus->eth_regs[0] &= ~0x10;
+	pegasus->wolopts = wol->wolopts;
+	return set_register(pegasus, WakeupControl, reg78);
+}
+
+static inline void
+pegasus_reset_wol(struct net_device *dev)
+{
+	struct ethtool_wolinfo wol;
+	
+	memset(&wol, 0, sizeof wol);
+	(void) pegasus_set_wol(dev, &wol);
+}
+
+static int
+pegasus_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
 {
 	pegasus_t *pegasus = netdev_priv(dev);
 	mii_ethtool_gset(&pegasus->mii, ecmd);
 	return 0;
 }
-static int pegasus_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+static int
+pegasus_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
 {
 	pegasus_t *pegasus = netdev_priv(dev);
 	return mii_ethtool_sset(&pegasus->mii, ecmd);
@@ -975,19 +1034,14 @@
 
 static u32 pegasus_get_msglevel(struct net_device *dev)
 {
-	/*
-	 * pegasus_t *pegasus = netdev_priv(dev);
-	 * return pegasus->msg_enable; FIXME
-	 */
-	return 0;
+	pegasus_t *pegasus = netdev_priv(dev);
+	return pegasus->msg_level;
 }
 
 static void pegasus_set_msglevel(struct net_device *dev, u32 v)
 {
-	/*
-	 * pegasus_t *pegasus = netdev_priv(dev);
-	 * pegasus->msg_enable = edata.data; FIXME
-	 */
+	pegasus_t *pegasus = netdev_priv(dev);
+	pegasus->msg_level = v;
 }
 
 static struct ethtool_ops ops = {
@@ -998,58 +1052,10 @@
 	.get_link = pegasus_get_link,
 	.get_msglevel = pegasus_get_msglevel,
 	.set_msglevel = pegasus_set_msglevel,
+	.get_wol = pegasus_get_wol,
+	.set_wol = pegasus_set_wol,
 };
 
-#else
-
-static int pegasus_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
-{
-	pegasus_t *pegasus = netdev_priv(dev);
-	short lpa, bmcr;
-	u8 port;
-
-	ecmd->supported = (SUPPORTED_10baseT_Half |
-			  SUPPORTED_10baseT_Full |
-			  SUPPORTED_100baseT_Half |
-			  SUPPORTED_100baseT_Full |
-			  SUPPORTED_Autoneg |
-			  SUPPORTED_TP | SUPPORTED_MII);
-	get_registers(pegasus, Reg7b, 1, &port);
-	if (port == 0)
-		ecmd->port = PORT_MII;
-	else
-		ecmd->port = PORT_TP;
-	ecmd->transceiver = XCVR_INTERNAL;
-	ecmd->phy_address = pegasus->phy;
-	read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr);
-	read_mii_word(pegasus, pegasus->phy, MII_LPA, &lpa);
-	if (bmcr & BMCR_ANENABLE) {
-		ecmd->autoneg = AUTONEG_ENABLE;
-		ecmd->speed = lpa & (LPA_100HALF | LPA_100FULL) ?
-		    SPEED_100 : SPEED_10;
-		if (ecmd->speed == SPEED_100)
-			ecmd->duplex = lpa & LPA_100FULL ?
-			    DUPLEX_FULL : DUPLEX_HALF;
-		else
-			ecmd->duplex = lpa & LPA_10FULL ?
-			    DUPLEX_FULL : DUPLEX_HALF;
-	} else {
-		ecmd->autoneg = AUTONEG_DISABLE;
-		ecmd->speed = bmcr & BMCR_SPEED100 ?
-		    SPEED_100 : SPEED_10;
-		ecmd->duplex = bmcr & BMCR_FULLDPLX ?
-		    DUPLEX_FULL : DUPLEX_HALF;
-	}
-	return 0;
-}
-
-static struct ethtool_ops ops = {
-	.get_drvinfo = pegasus_get_drvinfo,
-	.get_settings = pegasus_get_settings,
-	.get_link = ethtool_op_get_link,
-};
-#endif
-
 static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
 {
 	__u16 *data = (__u16 *) & rq->ifr_ifru;
@@ -1081,12 +1087,12 @@
 
 	if (net->flags & IFF_PROMISC) {
 		pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS;
-		info("%s: Promiscuous mode enabled", net->name);
+		pr_info("%s: Promiscuous mode enabled.\n", net->name);
 	} else if ((net->mc_count > multicast_filter_limit) ||
 		   (net->flags & IFF_ALLMULTI)) {
 		pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST;
 		pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
-		info("%s set allmulti", net->name);
+		pr_info("%s: set allmulti\n", net->name);
 	} else {
 		pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST;
 		pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
@@ -1180,7 +1186,6 @@
 	net->hard_start_xmit = pegasus_start_xmit;
 	net->set_multicast_list = pegasus_set_multicast;
 	net->get_stats = pegasus_netdev_stats;
-	net->mtu = PEGASUS_MTU;
 	SET_ETHTOOL_OPS(net, &ops);
 	pegasus->mii.dev = net;
 	pegasus->mii.mdio_read = mdio_read;
@@ -1192,27 +1197,28 @@
 	pegasus->features = usb_dev_id[dev_index].private;
 	get_interrupt_interval(pegasus);
 	if (reset_mac(pegasus)) {
-		err("can't reset MAC");
+		dev_err(&intf->dev, "can't reset MAC\n");
 		res = -EIO;
 		goto out2;
 	}
 	set_ethernet_addr(pegasus);
 	fill_skb_pool(pegasus);
 	if (pegasus->features & PEGASUS_II) {
-		info("setup Pegasus II specific registers");
+		dev_info(&intf->dev, "setup Pegasus II specific registers\n");
 		setup_pegasus_II(pegasus);
 	}
 	pegasus->phy = mii_phy_probe(pegasus);
 	if (pegasus->phy == 0xff) {
-		warn("can't locate MII phy, using default");
+		dev_warn(&intf->dev, "can't locate MII phy, using default\n");
 		pegasus->phy = 1;
 	}
 	usb_set_intfdata(intf, pegasus);
 	SET_NETDEV_DEV(net, &intf->dev);
+	pegasus_reset_wol(net);
 	res = register_netdev(net);
 	if (res)
 		goto out3;
-	printk("%s: %s\n", net->name, usb_dev_id[dev_index].name);
+	pr_info("%s: %s\n", net->name, usb_dev_id[dev_index].name);
 	return 0;
 
 out3:
@@ -1247,16 +1253,34 @@
 	free_netdev(pegasus->net);
 }
 
+static int pegasus_suspend (struct usb_interface *intf, u32 state)
+{
+	struct pegasus *pegasus = usb_get_intfdata(intf);
+	
+	netif_device_detach (pegasus->net);
+	return 0;
+}
+
+static int pegasus_resume (struct usb_interface *intf)
+{
+	struct pegasus *pegasus = usb_get_intfdata(intf);
+
+	netif_device_attach (pegasus->net);
+	return 0;
+}
+
 static struct usb_driver pegasus_driver = {
 	.name = driver_name,
 	.probe = pegasus_probe,
 	.disconnect = pegasus_disconnect,
 	.id_table = pegasus_ids,
+	.suspend = pegasus_suspend,
+	.resume = pegasus_resume,
 };
 
 static int __init pegasus_init(void)
 {
-	info(DRIVER_VERSION ":" DRIVER_DESC);
+	pr_info("%s: %s, " DRIVER_DESC "\n", driver_name, DRIVER_VERSION);
 	return usb_register(&pegasus_driver);
 }
 
diff -Nru a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h
--- a/drivers/usb/net/pegasus.h	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/net/pegasus.h	2005-01-13 17:08:38 -08:00
@@ -76,6 +76,7 @@
 	EthTxStat0 = 0x2b,
 	EthTxStat1 = 0x2c,
 	EthRxStat = 0x2d,
+	WakeupControl = 0x78,
 	Reg7b = 0x7b,
 	Gpio0 = 0x7e,
 	Gpio1 = 0x7f,
@@ -90,6 +91,8 @@
 	struct mii_if_info	mii;
 	unsigned		flags;
 	unsigned		features;
+	u32			msg_level;
+	u32			wolopts;
 	int			dev_index;
 	int			intr_interval;
 	struct tasklet_struct	rx_tl;
diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
--- a/drivers/usb/net/usbnet.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/net/usbnet.c	2005-01-13 17:08:38 -08:00
@@ -745,7 +745,7 @@
 	dev->out = usb_sndbulkpipe(dev->udev, 2);
 
 	// allocate irq urb
-	if ((data->int_urb = usb_alloc_urb (0, GFP_KERNEL)) == 0) {
+	if ((data->int_urb = usb_alloc_urb (0, GFP_KERNEL)) == NULL) {
 		dbg ("%s: cannot allocate interrupt URB",
 			dev->net->name);
 		return -ENOMEM;
@@ -2265,14 +2265,34 @@
 	.unbind =	cdc_unbind,
 	.tx_fixup = 	zaurus_tx_fixup,
 };
+#define	ZAURUS_STRONGARM_INFO	((unsigned long)&zaurus_sl5x00_info)
+
 static const struct driver_info	zaurus_pxa_info = {
 	.description =	"Sharp Zaurus, PXA-2xx based",
 	.flags =	FLAG_FRAMING_Z,
 	.check_connect = always_connected,
+	.bind =		generic_cdc_bind,
+	.unbind =	cdc_unbind,
 	.tx_fixup = 	zaurus_tx_fixup,
+};
+#define	ZAURUS_PXA_INFO		((unsigned long)&zaurus_pxa_info)
 
-	.in = 1, .out = 2,
+static const struct driver_info	olympus_mxl_info = {
+	.description =	"Olympus R1000",
+	.flags =	FLAG_FRAMING_Z,
+	.check_connect = always_connected,
+	.bind =		generic_cdc_bind,
+	.unbind =	cdc_unbind,
+	.tx_fixup = 	zaurus_tx_fixup,
 };
+#define	OLYMPUS_MXL_INFO	((unsigned long)&olympus_mxl_info)
+
+#else
+
+/* blacklist all those devices */
+#define	ZAURUS_STRONGARM_INFO	0
+#define	ZAURUS_PXA_INFO		0
+#define	OLYMPUS_MXL_INFO	0
 
 #endif
 
@@ -2384,7 +2404,7 @@
 #endif
 		size = (sizeof (struct ethhdr) + dev->net->mtu);
 
-	if ((skb = alloc_skb (size, flags)) == 0) {
+	if ((skb = alloc_skb (size, flags)) == NULL) {
 		devdbg (dev, "no rx skb");
 		defer_kevent (dev, EVENT_RX_MEMORY);
 		usb_free_urb (urb);
@@ -2741,7 +2761,7 @@
 	if (test_bit (EVENT_TX_HALT, &dev->flags)) {
 		unlink_urbs (dev, &dev->txq);
 		status = usb_clear_halt (dev->udev, dev->out);
-		if (status < 0)
+		if (status < 0 && status != -EPIPE)
 			deverr (dev, "can't clear tx halt, status %d",
 				status);
 		else {
@@ -2752,7 +2772,7 @@
 	if (test_bit (EVENT_RX_HALT, &dev->flags)) {
 		unlink_urbs (dev, &dev->rxq);
 		status = usb_clear_halt (dev->udev, dev->in);
-		if (status < 0)
+		if (status < 0 && status != -EPIPE)
 			deverr (dev, "can't clear rx halt, status %d",
 				status);
 		else {
@@ -2769,7 +2789,7 @@
 			urb = usb_alloc_urb (0, GFP_KERNEL);
 		else
 			clear_bit (EVENT_RX_MEMORY, &dev->flags);
-		if (urb != 0) {
+		if (urb != NULL) {
 			clear_bit (EVENT_RX_MEMORY, &dev->flags);
 			rx_submit (dev, urb, GFP_KERNEL);
 			tasklet_schedule (&dev->bh);
@@ -2996,7 +3016,8 @@
 
 			// don't refill the queue all at once
 			for (i = 0; i < 10 && dev->rxq.qlen < qlen; i++) {
-				if ((urb = usb_alloc_urb (0, GFP_ATOMIC)) != 0)
+				urb = usb_alloc_urb (0, GFP_ATOMIC);
+				if (urb != NULL)
 					rx_submit (dev, urb, GFP_ATOMIC);
 			}
 			if (temp != dev->rxq.qlen)
@@ -3354,12 +3375,15 @@
 }, 
 #endif
 
-#ifdef	CONFIG_USB_ZAURUS
+#if	defined(CONFIG_USB_ZAURUS) || defined(CONFIG_USB_CDCETHER)
 /*
  * SA-1100 based Sharp Zaurus ("collie"), or compatible.
  * Same idea as above, but different framing.
  *
  * PXA-2xx based models are also lying-about-cdc.
+ *
+ * NOTE:  These entries do double-duty, serving as blacklist entries
+ * whenever Zaurus support isn't enabled, but CDC Ethernet is.
  */
 {
 	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
@@ -3370,82 +3394,79 @@
 	.bInterfaceClass	= USB_CLASS_COMM,
 	.bInterfaceSubClass	= 6 /* Ethernet model */,
 	.bInterfaceProtocol	= 0,
-	.driver_info =  (unsigned long) &zaurus_sl5x00_info,
+	.driver_info = ZAURUS_STRONGARM_INFO,
 }, {
 	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
 			  | USB_DEVICE_ID_MATCH_DEVICE, 
 	.idVendor		= 0x04DD,
 	.idProduct		= 0x8005,	/* A-300 */
-	.bInterfaceClass	= 0x02,
-	.bInterfaceSubClass	= 0x0a,
+	.bInterfaceClass	= USB_CLASS_COMM,
+	.bInterfaceSubClass	= 6 /* Ethernet model */,
 	.bInterfaceProtocol	= 0x00,
-	.driver_info =  (unsigned long) &zaurus_pxa_info,
+	.driver_info = ZAURUS_PXA_INFO,
 }, {
 	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
 			  | USB_DEVICE_ID_MATCH_DEVICE, 
 	.idVendor		= 0x04DD,
 	.idProduct		= 0x8006,	/* B-500/SL-5600 */
-	.bInterfaceClass	= 0x02,
-	.bInterfaceSubClass	= 0x0a,
+	.bInterfaceClass	= USB_CLASS_COMM,
+	.bInterfaceSubClass	= 6 /* Ethernet model */,
 	.bInterfaceProtocol	= 0x00,
-	.driver_info =  (unsigned long) &zaurus_pxa_info,
+	.driver_info = ZAURUS_PXA_INFO,
 }, {
 	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
 	          | USB_DEVICE_ID_MATCH_DEVICE,
 	.idVendor		= 0x04DD,
 	.idProduct		= 0x8007,	/* C-700 */
-	.bInterfaceClass    = 0x02,
-	.bInterfaceSubClass = 0x0a,
+	.bInterfaceClass	= USB_CLASS_COMM,
+	.bInterfaceSubClass	= 6 /* Ethernet model */,
 	.bInterfaceProtocol = 0x00,
-	.driver_info =  (unsigned long) &zaurus_pxa_info,
+	.driver_info = ZAURUS_PXA_INFO,
 }, {
 	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
 		 | USB_DEVICE_ID_MATCH_DEVICE,
 	.idVendor               = 0x04DD,
 	.idProduct              = 0x9031,	/* C-750 C-760 */
-	.bInterfaceClass        = 0x02,
-	.bInterfaceSubClass     = 0x0a,
+	.bInterfaceClass	= USB_CLASS_COMM,
+	.bInterfaceSubClass	= 6 /* Ethernet model */,
 	.bInterfaceProtocol     = 0x00,
-	.driver_info =  (unsigned long) &zaurus_pxa_info,
+	.driver_info = ZAURUS_PXA_INFO,
 }, {
 	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
 		 | USB_DEVICE_ID_MATCH_DEVICE,
 	.idVendor               = 0x04DD,
 	.idProduct              = 0x9032,	/* SL-6000 */
-	.bInterfaceClass        = 0x02,
-	.bInterfaceSubClass     = 0x0a,
+	.bInterfaceClass	= USB_CLASS_COMM,
+	.bInterfaceSubClass	= 6 /* Ethernet model */,
 	.bInterfaceProtocol     = 0x00,
-	.driver_info =  (unsigned long) &zaurus_pxa_info,
+	.driver_info = ZAURUS_PXA_INFO,
 }, {
 	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
 		 | USB_DEVICE_ID_MATCH_DEVICE,
 	.idVendor               = 0x04DD,
 	.idProduct              = 0x9050,	/* C-860 */
-	.bInterfaceClass        = 0x02,
-	.bInterfaceSubClass     = 0x0a,
+	.bInterfaceClass	= USB_CLASS_COMM,
+	.bInterfaceSubClass	= 6 /* Ethernet model */,
 	.bInterfaceProtocol     = 0x00,
-	.driver_info =  (unsigned long) &zaurus_pxa_info,
+	.driver_info = ZAURUS_PXA_INFO,
 },
-#endif
 
-#ifdef	CONFIG_USB_CDCETHER
-
-#ifndef	CONFIG_USB_ZAURUS
-	/* if we couldn't whitelist Zaurus, we must blacklist it */
+/* Olympus has some models with a Zaurus-compatible option.
+ * R-1000 uses a FreeScale i.MXL cpu (ARMv4T)
+ */
 {
-	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
-			  | USB_DEVICE_ID_MATCH_DEVICE, 
-	.idVendor		= 0x04DD,
-	.idProduct		= 0x8004,
-	/* match the master interface */
+	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
+		 | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor               = 0x07B4,
+	.idProduct              = 0x0F02,	/* R-1000 */
 	.bInterfaceClass	= USB_CLASS_COMM,
 	.bInterfaceSubClass	= 6 /* Ethernet model */,
-	.bInterfaceProtocol	= 0,
-	.driver_info 		= 0, /* BLACKLIST */
+	.bInterfaceProtocol     = 0x00,
+	.driver_info = OLYMPUS_MXL_INFO,
 },
-	// FIXME blacklist the other Zaurus models too, sigh
 #endif
 
+#ifdef	CONFIG_USB_CDCETHER
 {
 	/* CDC Ether uses two interfaces, not necessarily consecutive.
 	 * We match the main interface, ignoring the optional device
diff -Nru a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
--- a/drivers/usb/serial/cypress_m8.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/serial/cypress_m8.c	2005-01-13 17:08:38 -08:00
@@ -18,6 +18,12 @@
  *
  *
  *  Lonnie Mendez <dignome@gmail.com>
+ *  12-15-2004
+ *	Incorporated write buffering from pl2303 driver.  Fixed bug with line
+ *	handling so both lines are raised in cypress_open. (was dropping rts)
+ *      Various code cleanups made as well along with other misc bug fixes.
+ *
+ *  Lonnie Mendez <dignome@gmail.com>
  *  04-10-2004
  *	Driver modified to support dynamic line settings.  Various improvments
  *      and features.
@@ -30,9 +36,11 @@
  * Long Term TODO:
  *	Improve transfer speeds - both read/write are somewhat slow
  *   at this point.
+ *      Improve debugging.  Show modem line status with debug output and
+ *   implement filtering for certain data as a module parameter.
  */
 
-/* Neil Whelchel wrote the cypress m8 implementation */
+/* Thanks to Neil Whelchel for writing the first cypress m8 implementation for linux. */
 /* Thanks to cypress for providing references for the hid reports. */
 /* Thanks to Jiang Zhang for providing links and for general help. */
 /* Code originates and was built up from ftdi_sio, belkin, pl2303 and others. */
@@ -53,24 +61,28 @@
 #include <linux/usb.h>
 #include <linux/serial.h>
 
+#include "usb-serial.h"
+#include "cypress_m8.h"
+
+
 #ifdef CONFIG_USB_SERIAL_DEBUG
 	static int debug = 1;
 #else
 	static int debug;
 #endif
-
 static int stats;
 
-#include "usb-serial.h"
-#include "cypress_m8.h"
-
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.06"
+#define DRIVER_VERSION "v1.08"
 #define DRIVER_AUTHOR "Lonnie Mendez <dignome@gmail.com>, Neil Whelchel <koyama@firstlight.net>"
 #define DRIVER_DESC "Cypress USB to Serial Driver"
 
+/* write buffer size defines */
+#define CYPRESS_BUF_SIZE	1024
+#define CYPRESS_CLOSING_WAIT	(30*HZ)
+
 static struct usb_device_id id_table_earthmate [] = {
 	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
 	{ }						/* Terminating entry */
@@ -103,6 +115,8 @@
 	int bytes_out;			   /* used for statistics */
 	int cmd_count;			   /* used for statistics */
 	int cmd_ctrl;			   /* always set this to 1 before issuing a command */
+	struct cypress_buf *buf;	   /* write buffer */
+	int write_urb_in_use;		   /* write urb in use indicator */
 	int termios_initialized;
 	__u8 line_control;	   	   /* holds dtr / rts value */
 	__u8 current_status;	   	   /* received from last read - info on dsr,cts,cd,ri,etc */
@@ -115,8 +129,15 @@
 	char prev_status, diff_status;	   /* used for TIOCMIWAIT */
 	/* we pass a pointer to this as the arguement sent to cypress_set_termios old_termios */
 	struct termios tmp_termios; 	   /* stores the old termios settings */
-	int write_interval;		   /* interrupt out write interval, as obtained from interrupt_out_urb */
-	int writepipe;			   /* used for clear halt, if necessary */
+	char calledfromopen;		   /* used when issuing lines on open - fixes rts drop bug */
+};
+
+/* write buffer structure */
+struct cypress_buf {
+	unsigned int	buf_size;
+	char		*buf_buf;
+	char		*buf_get;
+	char		*buf_put;
 };
 
 /* function prototypes for the Cypress USB to serial device */
@@ -126,6 +147,7 @@
 static int  cypress_open		(struct usb_serial_port *port, struct file *filp);
 static void cypress_close		(struct usb_serial_port *port, struct file *filp);
 static int  cypress_write		(struct usb_serial_port *port, const unsigned char *buf, int count);
+static void cypress_send		(struct usb_serial_port *port);
 static int  cypress_write_room		(struct usb_serial_port *port);
 static int  cypress_ioctl		(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
 static void cypress_set_termios		(struct usb_serial_port *port, struct termios * old);
@@ -136,8 +158,20 @@
 static void cypress_unthrottle		(struct usb_serial_port *port);
 static void cypress_read_int_callback	(struct urb *urb, struct pt_regs *regs);
 static void cypress_write_int_callback	(struct urb *urb, struct pt_regs *regs);
-static int  mask_to_rate		(unsigned mask);
-static unsigned rate_to_mask		(int rate);
+/* baud helper functions */
+static int	 mask_to_rate		(unsigned mask);
+static unsigned  rate_to_mask		(int rate);
+/* write buffer functions */
+static struct cypress_buf *cypress_buf_alloc(unsigned int size);
+static void 		  cypress_buf_free(struct cypress_buf *cb);
+static void		  cypress_buf_clear(struct cypress_buf *cb);
+static unsigned int	  cypress_buf_data_avail(struct cypress_buf *cb);
+static unsigned int	  cypress_buf_space_avail(struct cypress_buf *cb);
+static unsigned int	  cypress_buf_put(struct cypress_buf *cb, const char *buf,
+					  unsigned int count);
+static unsigned int	  cypress_buf_get(struct cypress_buf *cb, char *buf,
+					  unsigned int count);
+
 
 static struct usb_serial_device_type cypress_earthmate_device = {
 	.owner =			THIS_MODULE,
@@ -394,25 +428,19 @@
 
 	memset(priv, 0x00, sizeof (struct cypress_private));
 	spin_lock_init(&priv->lock);
+	priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE);
+	if (priv->buf == NULL) {
+		kfree(priv);
+		return -ENOMEM;
+	}
 	init_waitqueue_head(&priv->delta_msr_wait);
-	priv->writepipe = serial->port[0]->interrupt_out_urb->pipe;
 	
-	/* free up interrupt_out buffer / urb allocated by usbserial
- 	 * for this port as we use our own urbs for writing */
-	if (serial->port[0]->interrupt_out_buffer) {
-		kfree(serial->port[0]->interrupt_out_buffer);
-		serial->port[0]->interrupt_out_buffer = NULL;
-	}
-	if (serial->port[0]->interrupt_out_urb) {
-		priv->write_interval = serial->port[0]->interrupt_out_urb->interval;
-		usb_free_urb(serial->port[0]->interrupt_out_urb);
-		serial->port[0]->interrupt_out_urb = NULL;
-	} else /* still need a write interval */
-		priv->write_interval = 10;
-
+	usb_reset_configuration (serial->dev);
+	
 	priv->cmd_ctrl = 0;
 	priv->line_control = 0;
 	priv->termios_initialized = 0;
+	priv->calledfromopen = 0;
 	priv->rx_flags = 0;
 	usb_set_serial_port_data(serial->port[0], priv);
 	
@@ -467,6 +495,7 @@
 	priv = usb_get_serial_port_data(serial->port[0]);
 
 	if (priv) {
+		cypress_buf_free(priv->buf);
 		kfree(priv);
 		usb_set_serial_port_data(serial->port[0], NULL);
 	}
@@ -482,37 +511,39 @@
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
+	/* clear halts before open */
+	usb_clear_halt(serial->dev, 0x00);
+	usb_clear_halt(serial->dev, 0x81);
+	usb_clear_halt(serial->dev, 0x02);
+
 	spin_lock_irqsave(&priv->lock, flags);
 	/* reset read/write statistics */
 	priv->bytes_in = 0;
 	priv->bytes_out = 0;
 	priv->cmd_count = 0;
-
-	/* turn on dtr / rts since we are not flow controlling by default */
-	priv->line_control = CONTROL_DTR | CONTROL_RTS; /* sent in status byte */
+	priv->rx_flags = 0;
 	spin_unlock_irqrestore(&priv->lock, flags);
-	priv->cmd_ctrl = 1;
-	result = cypress_write(port, NULL, 0);
-	
+
+	/* setting to zero could cause data loss */
 	port->tty->low_latency = 1;
 
-	/* termios defaults are set by usb_serial_init */
-	
-	cypress_set_termios(port, &priv->tmp_termios);
+	/* raise both lines and set termios */
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->line_control = CONTROL_DTR | CONTROL_RTS;
+	priv->calledfromopen = 1;
+	priv->cmd_ctrl = 1;
+	spin_unlock_irqrestore(&priv->lock, flags);
+	result = cypress_write(port, NULL, 0);
 
 	if (result) {
 		dev_err(&port->dev, "%s - failed setting the control lines - error %d\n", __FUNCTION__, result);
 		return result;
 	} else
-		dbg("%s - success setting the control lines", __FUNCTION__);
+		dbg("%s - success setting the control lines", __FUNCTION__);	
 
-	/* throttling off */
-	spin_lock_irqsave(&priv->lock, flags);
-	priv->rx_flags = 0;
-	spin_unlock_irqrestore(&priv->lock, flags);
+	cypress_set_termios(port, &priv->tmp_termios);
 
-	/* setup the port and
-	 * start reading from the device */
+	/* setup the port and start reading from the device */
 	if(!port->interrupt_in_urb){
 		err("%s - interrupt_in_urb is empty!", __FUNCTION__);
 		return(-1);
@@ -537,9 +568,46 @@
 	struct cypress_private *priv = usb_get_serial_port_data(port);
 	unsigned int c_cflag;
 	unsigned long flags;
+	int bps;
+	long timeout;
+	wait_queue_t wait;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
+	/* wait for data to drain from buffer */
+	spin_lock_irqsave(&priv->lock, flags);
+	timeout = CYPRESS_CLOSING_WAIT;
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(&port->tty->write_wait, &wait);
+	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (cypress_buf_data_avail(priv->buf) == 0
+		|| timeout == 0 || signal_pending(current)
+		|| !usb_get_intfdata(port->serial->interface))
+			break;
+		spin_unlock_irqrestore(&priv->lock, flags);
+		timeout = schedule_timeout(timeout);
+		spin_lock_irqsave(&priv->lock, flags);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&port->tty->write_wait, &wait);
+	/* clear out any remaining data in the buffer */
+	cypress_buf_clear(priv->buf);
+	spin_unlock_irqrestore(&priv->lock, flags);
+	
+	/* wait for characters to drain from device */
+	bps = tty_get_baud_rate(port->tty);
+	if (bps > 1200)
+		timeout = max((HZ*2560)/bps,HZ/10);
+	else
+		timeout = 2*HZ;
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(timeout);
+
+	dbg("%s - stopping urbs", __FUNCTION__);
+	usb_kill_urb (port->interrupt_in_urb);
+	usb_kill_urb (port->interrupt_out_urb);
+
 	if (port->tty) {
 		c_cflag = port->tty->termios->c_cflag;
 		if (c_cflag & HUPCL) {
@@ -553,11 +621,6 @@
 		}
 	}
 
-	if (port->interrupt_in_urb) {
-		dbg("%s - stopping read urb", __FUNCTION__);
-		usb_kill_urb (port->interrupt_in_urb);
-	}
-
 	if (stats)
 		dev_info (&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n",
 		          priv->bytes_in, priv->bytes_out, priv->cmd_count);
@@ -568,116 +631,145 @@
 {
 	struct cypress_private *priv = usb_get_serial_port_data(port);
 	unsigned long flags;
-	struct urb *urb;
-	int status, s_pos = 0;
-	__u8 transfer_size = 0;
-	__u8 *buffer;
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
+	
+	dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
 
-	spin_lock_irqsave(&priv->lock, flags);
-	if (count == 0 && !priv->cmd_ctrl) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		dbg("%s - write request of 0 bytes", __FUNCTION__);
-		return 0;
+	/* line control commands, which need to be executed immediately,
+	   are not put into the buffer for obvious reasons.
+	 */
+	if (priv->cmd_ctrl) {
+		count = 0;
+		goto finish;
 	}
-
-	if (priv->cmd_ctrl)
-		++priv->cmd_count;
-	priv->cmd_ctrl = 0;
+	
+	if (!count)
+		return count;
+	
+	spin_lock_irqsave(&priv->lock, flags);
+	count = cypress_buf_put(priv->buf, buf, count);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
-	dbg("%s - interrupt out size is %d", __FUNCTION__, port->interrupt_out_size);
-	dbg("%s - count is %d", __FUNCTION__, count);
+finish:
+	cypress_send(port);
 
-	/* Allocate buffer and urb */
-	buffer = kmalloc (port->interrupt_out_size, GFP_ATOMIC);
-	if (!buffer) {
-		dev_err(&port->dev, "ran out of memory for buffer\n");
-		return -ENOMEM;
-	}
+	return count;
+} /* cypress_write */
 
-	urb = usb_alloc_urb (0, GFP_ATOMIC);
-	if (!urb) {
-		dev_err(&port->dev, "failed allocating write urb\n");
-		kfree (buffer);
-		return -ENOMEM;
+
+static void cypress_send(struct usb_serial_port *port)
+{
+	int count = 0, result, offset, actual_size;
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	
+	dbg("%s - port %d", __FUNCTION__, port->number);
+	dbg("%s - interrupt out size is %d", __FUNCTION__, port->interrupt_out_size);
+	
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->write_urb_in_use) {
+		dbg("%s - can't write, urb in use", __FUNCTION__);
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return;
 	}
+	spin_unlock_irqrestore(&priv->lock, flags);
 
-	memset(buffer, 0, port->interrupt_out_size); // test if this is needed... probably not since loop removed
+	/* clear buffer */
+	memset(port->interrupt_out_urb->transfer_buffer, 0, port->interrupt_out_size);
 
 	spin_lock_irqsave(&priv->lock, flags);
 	switch (port->interrupt_out_size) {
 		case 32:
 			// this is for the CY7C64013...
-			transfer_size = min (count, 30);
-			buffer[0] = priv->line_control;
-			buffer[1] = transfer_size;
-			s_pos = 2;
+			offset = 2;
+			port->interrupt_out_buffer[0] = priv->line_control;
 			break;
 		case 8:
 			// this is for the CY7C63743...
-			transfer_size = min (count, 7);
-			buffer[0] = priv->line_control | transfer_size;
-			s_pos = 1;
+			offset = 1;
+			port->interrupt_out_buffer[0] = priv->line_control;
 			break;
 		default:
 			dbg("%s - wrong packet size", __FUNCTION__);
 			spin_unlock_irqrestore(&priv->lock, flags);
-			kfree (buffer);
-			usb_free_urb (urb);
-			return -1;
+			return;
 	}
 
 	if (priv->line_control & CONTROL_RESET)
 		priv->line_control &= ~CONTROL_RESET;
-	spin_unlock_irqrestore(&priv->lock, flags);
-
-	/* copy data to offset position in urb transfer buffer */
-	memcpy (&buffer[s_pos], buf, transfer_size);
 
-	usb_serial_debug_data (debug, &port->dev, __FUNCTION__, port->interrupt_out_size, buffer);
+	if (priv->cmd_ctrl) {
+		priv->cmd_count++;
+		dbg("%s - line control command being issued", __FUNCTION__);
+		spin_unlock_irqrestore(&priv->lock, flags);
+		goto send;
+	} else
+		spin_unlock_irqrestore(&priv->lock, flags);
 
-	/* build up the urb */
-	usb_fill_int_urb (urb, port->serial->dev,
-			  usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
-			  buffer, port->interrupt_out_size,
-			  cypress_write_int_callback, port, priv->write_interval);
+	count = cypress_buf_get(priv->buf, &port->interrupt_out_buffer[offset],
+				port->interrupt_out_size-offset);
 
-	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (count == 0) {
+		return;
+	}
 
-	if (status) {
-		dev_err(&port->dev, "%s - usb_submit_urb (write interrupt) failed with status %d\n",
-			__FUNCTION__, status);
-		transfer_size = status;
-		kfree (buffer);
-		goto exit;
+	switch (port->interrupt_out_size) {
+		case 32:
+			port->interrupt_out_buffer[1] = count;
+			break;
+		case 8:
+			port->interrupt_out_buffer[0] |= count;
 	}
 
+	dbg("%s - count is %d", __FUNCTION__, count);
+
+send:
 	spin_lock_irqsave(&priv->lock, flags);
-	priv->bytes_out += transfer_size;
+	priv->write_urb_in_use = 1;
 	spin_unlock_irqrestore(&priv->lock, flags);
 
-exit:
-	/* buffer free'd in callback */
-	usb_free_urb (urb);
+	if (priv->cmd_ctrl)
+		actual_size = 1;
+	else
+		actual_size = count + (port->interrupt_out_size == 32 ? 2 : 1);
+	
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, port->interrupt_out_size,
+			      port->interrupt_out_urb->transfer_buffer);
 
-	return transfer_size;
+	port->interrupt_out_urb->transfer_buffer_length = actual_size;
+	port->interrupt_out_urb->dev = port->serial->dev;
+	result = usb_submit_urb (port->interrupt_out_urb, GFP_ATOMIC);
+	if (result) {
+		dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__,
+			result);
+		priv->write_urb_in_use = 0;
+	}
 
-} /* cypress_write */
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->cmd_ctrl) {
+		priv->cmd_ctrl = 0;
+	}
+	priv->bytes_out += count; /* do not count the line control and size bytes */
+	spin_unlock_irqrestore(&priv->lock, flags);
 
+	schedule_work(&port->work);
+} /* cypress_send */
 
+
+/* returns how much space is available in the soft buffer */
 static int cypress_write_room(struct usb_serial_port *port)
 {
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	int room = 0;
+	unsigned long flags;
+
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	/*
-	 * We really can take anything the user throw at us
-	 * but let's pick a nice big number to tell the tty
-	 * layer that we have lots of free space
-	 */	
+	spin_lock_irqsave(&priv->lock, flags);
+	room = cypress_buf_space_avail(priv->buf);
+	spin_unlock_irqrestore(&priv->lock, flags);
 
-	return 2048;
+	dbg("%s - returns %d", __FUNCTION__, room);
+	return room;
 }
 
 
@@ -816,6 +908,8 @@
 	int data_bits, stop_bits, parity_type, parity_enable;
 	unsigned cflag, iflag, baud_mask;
 	unsigned long flags;
+	__u8 oldlines;
+	int linechange;
 	
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
@@ -880,6 +974,7 @@
 		data_bits = 3;
 
 	spin_lock_irqsave(&priv->lock, flags);
+	oldlines = priv->line_control;
 	if ((cflag & CBAUD) == B0) {
 		/* drop dtr and rts */
 		dbg("%s - dropping the lines, baud rate 0bps", __FUNCTION__);
@@ -902,11 +997,13 @@
 		}
 		priv->line_control |= CONTROL_DTR;
 		
-		/* this is probably not what I think it is... check into it */
-		if (cflag & CRTSCTS)
-			priv->line_control |= CONTROL_RTS;
-		else
-			priv->line_control &= ~CONTROL_RTS;
+		/* toggle CRTSCTS? - don't do this if being called from cypress_open */
+		if (!priv->calledfromopen) {
+			if (cflag & CRTSCTS)
+				priv->line_control |= CONTROL_RTS;
+			else
+				priv->line_control &= ~CONTROL_RTS;
+		}
 	}
 	spin_unlock_irqrestore(&priv->lock, flags);
 	
@@ -925,7 +1022,7 @@
 
 	/* Here we can define custom tty settings for devices
          *
-         * the main tty base comes from empeg.c
+         * the main tty termios flag base comes from empeg.c
          */
 
 	spin_lock_irqsave(&priv->lock, flags);	
@@ -959,33 +1056,37 @@
 
 		// Software app handling it for device...	
 
-	} else {
-		
-		/* do something here */
-
 	}
+	linechange = (priv->line_control != oldlines);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
-	/* set lines */
-	priv->cmd_ctrl = 1;
-	cypress_write(port, NULL, 0);
+	/* if necessary, set lines */
+	if (!priv->calledfromopen && linechange) {
+		priv->cmd_ctrl = 1;
+		cypress_write(port, NULL, 0);
+	}
+
+	if (priv->calledfromopen)
+		priv->calledfromopen = 0;
 	
-	return;
 } /* cypress_set_termios */
 
-
+ 
+/* returns amount of data still left in soft buffer */
 static int cypress_chars_in_buffer(struct usb_serial_port *port)
 {
-	dbg("%s - port %d", __FUNCTION__, port->number);
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	int chars = 0;
+	unsigned long flags;
 
-	/*
-	 * We can't really account for how much data we
-	 * have sent out, but hasn't made it through to the
-	 * device, so just tell the tty layer that everything
-	 * is flushed.
-	 */
+	dbg("%s - port %d", __FUNCTION__, port->number);
+	
+	spin_lock_irqsave(&priv->lock, flags);
+	chars = cypress_buf_data_avail(priv->buf);
+	spin_unlock_irqrestore(&priv->lock, flags);
 
-	return 0;
+	dbg("%s - returns %d", __FUNCTION__, chars);
+	return chars;
 }
 
 
@@ -1032,10 +1133,11 @@
 	struct tty_struct *tty;
 	unsigned char *data = urb->transfer_buffer;
 	unsigned long flags;
-	char tty_flag = TTY_NORMAL;
-	int bytes=0;
+	char tty_flag = TTY_NORMAL;	
+	int havedata = 0;
+	int bytes = 0;
 	int result;
-	int i=0;
+	int i = 0;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
@@ -1046,6 +1148,7 @@
 
 	spin_lock_irqsave(&priv->lock, flags);
 	if (priv->rx_flags & THROTTLED) {
+		dbg("%s - now throttling", __FUNCTION__);
 		priv->rx_flags |= ACTUALLY_THROTTLED;
 		spin_unlock_irqrestore(&priv->lock, flags);
 		return;
@@ -1058,8 +1161,6 @@
 		return;
 	}
 
-	usb_serial_debug_data (debug, &port->dev, __FUNCTION__, urb->actual_length, data);
-	
 	spin_lock_irqsave(&priv->lock, flags);
 	switch(urb->actual_length) {
 		case 32:
@@ -1067,12 +1168,16 @@
 			priv->current_status = data[0] & 0xF8;
 			bytes = data[1]+2;
 			i=2;
+			if (bytes > 2)
+				havedata = 1;
 			break;
 		case 8:
 			// This is for the CY7C63743...
 			priv->current_status = data[0] & 0xF8;
 			bytes = (data[0] & 0x07)+1;
 			i=1;
+			if (bytes > 1)
+				havedata = 1;
 			break;
 		default:
 			dbg("%s - wrong packet size - received %d bytes", __FUNCTION__, urb->actual_length);
@@ -1081,6 +1186,8 @@
 	}
 	spin_unlock_irqrestore(&priv->lock, flags);
 
+	usb_serial_debug_data (debug, &port->dev, __FUNCTION__, urb->actual_length, data);
+
 	spin_lock_irqsave(&priv->lock, flags);
 	/* check to see if status has changed */
 	if (priv != NULL) {
@@ -1115,7 +1222,7 @@
 	/* process read if there is data other than line status */
 	if (tty && (bytes > i)) {
 		for (; i < bytes ; ++i) {
-			dbg("pushing byte number %d - %d",i,data[i]);
+			dbg("pushing byte number %d - %d - %c",i,data[i],data[i]);
 			if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
 				tty_flip_buffer_push(tty);
 			}
@@ -1151,21 +1258,229 @@
 static void cypress_write_int_callback(struct urb *urb, struct pt_regs *regs)
 {
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
-
-	/* free up the transfer buffer, as usb_free_urb() does not do this */
-	kfree (urb->transfer_buffer);
+	struct cypress_private *priv = usb_get_serial_port_data(port);
+	int result;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 	
-	if (urb->status) {
-		dbg("%s - nonzero write status received: %d", __FUNCTION__, urb->status);
-		return;
+	switch (urb->status) {
+		case 0:
+			/* success */
+			break;
+		case -ECONNRESET:
+		case -ENOENT:
+		case -ESHUTDOWN:
+			/* this urb is terminated, clean up */
+			dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+			priv->write_urb_in_use = 0;
+			return;
+		default:
+			/* error in the urb, so we have to resubmit it */
+			dbg("%s - Overflow in write", __FUNCTION__);
+			dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+			port->interrupt_out_urb->transfer_buffer_length = 1;
+			port->interrupt_out_urb->dev = port->serial->dev;
+			result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
+			if (result)
+				dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n",
+					__FUNCTION__, result);
+			else
+				return;
 	}
+	
+	priv->write_urb_in_use = 0;
+	
+	/* send any buffered data */
+	cypress_send(port);
+}
+
+
+/*****************************************************************************
+ * Write buffer functions - buffering code from pl2303 used
+ *****************************************************************************/
+
+/*
+ * cypress_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+
+static struct cypress_buf *cypress_buf_alloc(unsigned int size)
+{
+
+	struct cypress_buf *cb;
+
+
+	if (size == 0)
+		return NULL;
+
+	cb = (struct cypress_buf *)kmalloc(sizeof(struct cypress_buf), GFP_KERNEL);
+	if (cb == NULL)
+		return NULL;
+
+	cb->buf_buf = kmalloc(size, GFP_KERNEL);
+	if (cb->buf_buf == NULL) {
+		kfree(cb);
+		return NULL;
+	}
+
+	cb->buf_size = size;
+	cb->buf_get = cb->buf_put = cb->buf_buf;
+
+	return cb;
 
-	schedule_work(&port->work);
 }
 
 
+/*
+ * cypress_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+
+static void cypress_buf_free(struct cypress_buf *cb)
+{
+	if (cb != NULL) {
+		if (cb->buf_buf != NULL)
+			kfree(cb->buf_buf);
+		kfree(cb);
+	}
+}
+
+
+/*
+ * cypress_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+
+static void cypress_buf_clear(struct cypress_buf *cb)
+{
+	if (cb != NULL)
+		cb->buf_get = cb->buf_put;
+		/* equivalent to a get of all data available */
+}
+
+
+/*
+ * cypress_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+
+static unsigned int cypress_buf_data_avail(struct cypress_buf *cb)
+{
+	if (cb != NULL)
+		return ((cb->buf_size + cb->buf_put - cb->buf_get) % cb->buf_size);
+	else
+		return 0;
+}
+
+
+/*
+ * cypress_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+
+static unsigned int cypress_buf_space_avail(struct cypress_buf *cb)
+{
+	if (cb != NULL)
+		return ((cb->buf_size + cb->buf_get - cb->buf_put - 1) % cb->buf_size);
+	else
+		return 0;
+}
+
+
+/*
+ * cypress_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+
+static unsigned int cypress_buf_put(struct cypress_buf *cb, const char *buf,
+	unsigned int count)
+{
+
+	unsigned int len;
+
+
+	if (cb == NULL)
+		return 0;
+
+	len  = cypress_buf_space_avail(cb);
+	if (count > len)
+		count = len;
+
+	if (count == 0)
+		return 0;
+
+	len = cb->buf_buf + cb->buf_size - cb->buf_put;
+	if (count > len) {
+		memcpy(cb->buf_put, buf, len);
+		memcpy(cb->buf_buf, buf+len, count - len);
+		cb->buf_put = cb->buf_buf + count - len;
+	} else {
+		memcpy(cb->buf_put, buf, count);
+		if (count < len)
+			cb->buf_put += count;
+		else /* count == len */
+			cb->buf_put = cb->buf_buf;
+	}
+
+	return count;
+
+}
+
+
+/*
+ * cypress_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+
+static unsigned int cypress_buf_get(struct cypress_buf *cb, char *buf,
+	unsigned int count)
+{
+
+	unsigned int len;
+
+
+	if (cb == NULL)
+		return 0;
+
+	len = cypress_buf_data_avail(cb);
+	if (count > len)
+		count = len;
+
+	if (count == 0)
+		return 0;
+
+	len = cb->buf_buf + cb->buf_size - cb->buf_get;
+	if (count > len) {
+		memcpy(buf, cb->buf_get, len);
+		memcpy(buf+len, cb->buf_buf, count - len);
+		cb->buf_get = cb->buf_buf + count - len;
+	} else {
+		memcpy(buf, cb->buf_get, count);
+		if (count < len)
+			cb->buf_get += count;
+		else /* count == len */
+			cb->buf_get = cb->buf_buf;
+	}
+
+	return count;
+
+}
+
 /*****************************************************************************
  * Module functions
  *****************************************************************************/
@@ -1214,6 +1529,7 @@
 
 MODULE_AUTHOR( DRIVER_AUTHOR );
 MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_VERSION( DRIVER_VERSION );
 MODULE_LICENSE("GPL");
 
 module_param(debug, bool, S_IRUGO | S_IWUSR);
diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
--- a/drivers/usb/serial/ftdi_sio.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/serial/ftdi_sio.c	2005-01-13 17:08:38 -08:00
@@ -372,6 +372,7 @@
 	{ USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0, 0x3ff) },
 	{ USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0, 0x3ff) },
 	{ USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0, 0x3ff) },
+	{ USB_DEVICE_VER(FTDI_VID, EVER_ECO_PRO_CDS, 0, 0x3ff) },
 	{ }						/* Terminating entry */
 };
 
@@ -486,6 +487,7 @@
 	{ USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0x400, 0xffff) },
+	{ USB_DEVICE_VER(FTDI_VID, EVER_ECO_PRO_CDS, 0x400, 0xffff) },
 	{ }						/* Terminating entry */
 };
 
@@ -608,6 +610,7 @@
 	{ USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) },
 	{ USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) },
 	{ USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) },
+	{ USB_DEVICE(FTDI_VID, EVER_ECO_PRO_CDS) },
 	{ }						/* Terminating entry */
 };
 
diff -Nru a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
--- a/drivers/usb/serial/ftdi_sio.h	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/serial/ftdi_sio.h	2005-01-13 17:08:38 -08:00
@@ -240,6 +240,12 @@
 #define FTDI_RM_VID		0x0403	/* Vendor  Id */
 #define FTDI_RMCANVIEW_PID	0xfd60	/* Product Id */
 
+/*
+ * EVER Eco Pro UPS (http://www.ever.com.pl/)
+ */
+
+#define	EVER_ECO_PRO_CDS	0xe520	/* RS-232 converter */
+
 /* Commands */
 #define FTDI_SIO_RESET 		0 /* Reset the port */
 #define FTDI_SIO_MODEM_CTRL 	1 /* Set the modem control register */
diff -Nru a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
--- a/drivers/usb/serial/garmin_gps.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/serial/garmin_gps.c	2005-01-13 17:08:38 -08:00
@@ -316,7 +316,7 @@
 		garmin_data_p->flags |= FLAGS_QUEUING;
 		pkt = kmalloc(sizeof(struct garmin_packet)+data_length,
 		              GFP_ATOMIC);
-		if (pkt == 0) {
+		if (pkt == NULL) {
 			dev_err(&garmin_data_p->port->dev, "out of memory\n");
 			return 0;
 		}
@@ -739,7 +739,7 @@
 {
 	struct garmin_packet *pkt = NULL;
 
-	while ((pkt = pkt_pop(garmin_data_p)) != 0) {
+	while ((pkt = pkt_pop(garmin_data_p)) != NULL) {
 		dbg("%s - next pkt: %d", __FUNCTION__, pkt->seq);
 		if (gsp_send(garmin_data_p, pkt->data, pkt->size) > 0) {
 			kfree(pkt);
@@ -877,7 +877,7 @@
 
 	struct usb_serial_port *port = garmin_data_p->port;
 
-	if (port != 0 && garmin_data_p->flags & FLAGS_APP_RESP_SEEN) {
+	if (port != NULL && garmin_data_p->flags & FLAGS_APP_RESP_SEEN) {
 		/* send a terminate command */
 		status = garmin_write_bulk(port, GARMIN_STOP_TRANSFER_REQ,
 		                           sizeof(GARMIN_STOP_TRANSFER_REQ));
@@ -1366,7 +1366,7 @@
 
 	if ((garmin_data_p->flags & FLAGS_THROTTLED) == 0) {
 		pkt = pkt_pop(garmin_data_p);
-		if (pkt != 0) {
+		if (pkt != NULL) {
 
 			send_to_tty(garmin_data_p->port, pkt->data, pkt->size);
 			kfree(pkt);
diff -Nru a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
--- a/drivers/usb/serial/keyspan.c	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/serial/keyspan.c	2005-01-13 17:08:38 -08:00
@@ -374,7 +374,7 @@
 		flip = p_priv->out_flip;
 	
 		/* Check we have a valid urb/endpoint before we use it... */
-		if ((this_urb = p_priv->out_urbs[flip]) == 0) {
+		if ((this_urb = p_priv->out_urbs[flip]) == NULL) {
 			/* no bulk out, so return 0 bytes written */
 			dbg("%s - no output urb :(", __FUNCTION__);
 			return count;
@@ -1020,11 +1020,11 @@
 	flip = p_priv->out_flip;
 
 	/* Check both endpoints to see if any are available. */
-	if ((this_urb = p_priv->out_urbs[flip]) != 0) {
+	if ((this_urb = p_priv->out_urbs[flip]) != NULL) {
 		if (this_urb->status != -EINPROGRESS)
 			return (data_len);
 		flip = (flip + 1) & d_details->outdat_endp_flip;        
-		if ((this_urb = p_priv->out_urbs[flip]) != 0) 
+		if ((this_urb = p_priv->out_urbs[flip]) != NULL) 
 			if (this_urb->status != -EINPROGRESS)
 				return (data_len);
 	}
diff -Nru a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
--- a/drivers/usb/storage/unusual_devs.h	2005-01-13 17:08:38 -08:00
+++ b/drivers/usb/storage/unusual_devs.h	2005-01-13 17:08:38 -08:00
@@ -865,6 +865,14 @@
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_FIX_INQUIRY ),
 
+/* Submitted by Daniel Drake <dsd@gentoo.org>
+ * Reported by dayul on the Gentoo Forums */
+UNUSUAL_DEV(  0x0ea0, 0x2168, 0x0110, 0x0110,
+		"Ours Technology",
+		"Flash Disk",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_IGNORE_RESIDUE ),
+
 /* Reported by Rastislav Stanik <rs_kernel@yahoo.com> */
 UNUSUAL_DEV(  0x0ea0, 0x6828, 0x0110, 0x0110,
 		"USB",