bk://kernel.bkbits.net/gregkh/linux/usb-2.6
olh@suse.de|ChangeSet|20040713190717|50924 olh

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2004/07/13 13:11:31-07:00 akpm@bix.(none) 
#   Merge
# 
# drivers/usb/net/kaweth.c
#   2004/07/13 13:11:28-07:00 akpm@bix.(none) +0 -1
#   SCCS merged
# 
# drivers/usb/misc/auerswald.c
#   2004/07/13 13:11:19-07:00 akpm@bix.(none) +0 -1
#   SCCS merged
# 
# drivers/usb/media/pwc-if.c
#   2004/07/13 13:11:11-07:00 akpm@bix.(none) +0 -2
#   SCCS merged
# 
# drivers/usb/image/mdc800.c
#   2004/07/13 13:10:55-07:00 akpm@bix.(none) +0 -3
#   SCCS merged
# 
# drivers/usb/gadget/ether.c
#   2004/07/13 13:10:25-07:00 akpm@bix.(none) +6 -8
#   SCCS merged
# 
# drivers/usb/class/usb-midi.c
#   2004/07/13 13:08:41-07:00 akpm@bix.(none) +0 -8
#   SCCS merged
# 
# include/linux/videodev.h
#   2004/07/13 13:07:29-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# drivers/usb/storage/sddr09.c
#   2004/07/13 13:07:29-07:00 akpm@bix.(none) +0 -1
#   Auto merged
# 
# drivers/usb/serial/kobil_sct.c
#   2004/07/13 13:07:29-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# drivers/usb/serial/keyspan.c
#   2004/07/13 13:07:29-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# drivers/usb/net/usbnet.c
#   2004/07/13 13:07:29-07:00 akpm@bix.(none) +0 -13
#   Auto merged
# 
# drivers/usb/misc/usbtest.c
#   2004/07/13 13:07:29-07:00 akpm@bix.(none) +0 -12
#   Auto merged
# 
# drivers/usb/misc/speedtch.c
#   2004/07/13 13:07:29-07:00 akpm@bix.(none) +0 -1
#   Auto merged
# 
# drivers/usb/media/pwc-ctrl.c
#   2004/07/13 13:07:29-07:00 akpm@bix.(none) +0 -1
#   Auto merged
# 
# drivers/usb/media/ov511.c
#   2004/07/13 13:07:29-07:00 akpm@bix.(none) +0 -1
#   Auto merged
# 
# drivers/usb/input/powermate.c
#   2004/07/13 13:07:29-07:00 akpm@bix.(none) +0 -1
#   Auto merged
# 
# drivers/usb/input/aiptek.c
#   2004/07/13 13:07:29-07:00 akpm@bix.(none) +0 -5
#   Auto merged
# 
# drivers/usb/image/microtek.c
#   2004/07/13 13:07:29-07:00 akpm@bix.(none) +0 -1
#   Auto merged
# 
# drivers/usb/host/ohci-q.c
#   2004/07/13 13:07:29-07:00 akpm@bix.(none) +0 -7
#   Auto merged
# 
# drivers/usb/host/ohci-mem.c
#   2004/07/13 13:07:28-07:00 akpm@bix.(none) +0 -3
#   Auto merged
# 
# drivers/usb/host/ohci-hub.c
#   2004/07/13 13:07:28-07:00 akpm@bix.(none) +0 -3
#   Auto merged
# 
# drivers/usb/host/ohci-hcd.c
#   2004/07/13 13:07:28-07:00 akpm@bix.(none) +0 -5
#   Auto merged
# 
# drivers/usb/host/ohci-dbg.c
#   2004/07/13 13:07:28-07:00 akpm@bix.(none) +0 -3
#   Auto merged
# 
# drivers/usb/host/ehci-sched.c
#   2004/07/13 13:07:28-07:00 akpm@bix.(none) +0 -6
#   Auto merged
# 
# drivers/usb/host/ehci-dbg.c
#   2004/07/13 13:07:28-07:00 akpm@bix.(none) +0 -1
#   Auto merged
# 
# drivers/usb/gadget/zero.c
#   2004/07/13 13:07:28-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# drivers/usb/gadget/inode.c
#   2004/07/13 13:07:28-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# drivers/usb/core/devio.c
#   2004/07/13 13:07:28-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# CREDITS
#   2004/07/13 13:07:28-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2004/07/13 12:07:17-07:00 olh@suse.de 
#   [PATCH] USB: fix SN9C10[12] driver for ia64
#   
#    On Sun, Jul 11, Luca Risolia wrote:
#   
#   > This single patch contains some updates and cleanups for
#   > the W996[87]CF driver and a new experimental V4L2 driver
#   > for SONiX SN9C10[12] PC Camera Controllers connected to various
#   > image sensors. I have not divided the patch in two logical
#   > sub-patches becouse of two independent changes in one common
#   > file, KConfigure. Since this mailing list refused the patch,
#   > due to its size, I have uploaded it here, as
#   > Documentation/SubmittingPatches suggests:
#   
#   I need this patch for x86_64.
#   
#   
#     CC [M]  drivers/usb/media/sn9c102_core.o
#   In file included from drivers/usb/media/sn9c102.h:34,
#                    from drivers/usb/media/sn9c102_core.c:43:
#   include/asm/rwsem.h:55: error: redefinition of `struct rw_semaphore'
#   In file included from drivers/usb/media/sn9c102.h:34,
#                    from drivers/usb/media/sn9c102_core.c:43:
#   include/asm/rwsem.h:79:1: warning: "__RWSEM_INITIALIZER" redefined
#   In file included from include/linux/rwsem.h:25,
#                    from include/asm/semaphore.h:43,
#                    from include/linux/sched.h:18,
#                    from include/linux/module.h:10,
#                    from drivers/usb/media/sn9c102_core.c:21:
#   include/linux/rwsem-spinlock.h:49:1: warning: this is the location of the previous definition
#   include/asm/rwsem.h:87: warning: static declaration for `init_rwsem' follows non-static
#   include/asm/rwsem.h:100: warning: `__down_read' declared inline after being called
#   include/asm/rwsem.h:100: warning: static declaration for `__down_read' follows non-static
#   include/asm/rwsem.h:122: warning: `__down_read_trylock' declared inline after being called
#   include/asm/rwsem.h:122: warning: static declaration for `__down_read_trylock' follows non-static
#   include/asm/rwsem.h:146: warning: `__down_write' declared inline after being called
#   include/asm/rwsem.h:146: warning: static declaration for `__down_write' follows non-static
#   include/asm/rwsem.h:171: warning: `__down_write_trylock' declared inline after being called
#   include/asm/rwsem.h:171: warning: static declaration for `__down_write_trylock' follows non-static
#   include/asm/rwsem.h:184: warning: `__up_read' declared inline after being called
#   include/asm/rwsem.h:184: warning: static declaration for `__up_read' follows non-static
#   include/asm/rwsem.h:208: warning: `__up_write' declared inline after being called
#   include/asm/rwsem.h:208: warning: static declaration for `__up_write' follows non-static
#   include/asm/rwsem.h:233: warning: `__downgrade_write' declared inline after being called
#   include/asm/rwsem.h:233: warning: static declaration for `__downgrade_write' follows non-static
#   make[3]: *** [drivers/usb/media/sn9c102_core.o] Error 1
#   
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/media/sn9c102.h
#   2004/07/13 10:53:30-07:00 olh@suse.de +1 -2
#   USB: fix SN9C10[12] driver for ia64
# 
# ChangeSet
#   2004/07/13 11:56:06-07:00 greg@kroah.com 
#   USB: sort the order in which the usb-serial drivers get built
#   
#   all other usb drivers get built in alphabetical order, why not these?
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/Makefile
#   2004/07/13 11:55:44-07:00 greg@kroah.com +16 -15
#   USB: sort the order in which the usb-serial drivers get built
#   
#   all other usb drivers get built in alphabetical order, why not these?
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# ChangeSet
#   2004/07/13 11:54:31-07:00 greg@kroah.com 
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/whiteheat.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +5 -14
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/visor.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +8 -12
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/usb-serial.h
#   2004/07/13 11:53:46-07:00 greg@kroah.com +9 -9
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/usb-serial.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +2 -11
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/safe_serial.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +3 -7
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/pl2303.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +4 -11
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/omninet.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +2 -8
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/mct_u232.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +4 -10
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/kobil_sct.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +3 -11
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/kl5kusb105.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +5 -10
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/keyspan_pda.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +1 -6
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/keyspan.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +2 -10
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/ir-usb.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +7 -10
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/ipaq.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +3 -9
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/io_ti.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +11 -17
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/io_edgeport.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +9 -24
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/generic.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +6 -11
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/ftdi_sio.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +4 -8
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/ezusb.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +5 -12
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/empeg.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +4 -9
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/digi_acceleport.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +2 -8
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/cyberjack.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +5 -12
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/bus.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +0 -7
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/belkin_sa.c
#   2004/07/13 11:53:46-07:00 greg@kroah.com +3 -8
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/Kconfig
#   2004/07/13 11:53:46-07:00 greg@kroah.com +0 -7
#   USB: remove CONFIG_USB_SERIAL_DEBUG
#   
#   This involved reworking the usb_serial_debug_data() function too.
#   Based on a request from SuSE, and numerous user confusions with how
#   to enable this option.  Now that all usb-serial drivers are using
#   module_param() it can be set from the boot command line if the drivers
#   are built into the kernel.
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# ChangeSet
#   2004/07/13 09:23:43-07:00 greg@kroah.com 
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/whiteheat.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +2 -2
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/safe_serial.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +11 -10
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/omninet.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +1 -2
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/mct_u232.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +2 -2
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/kobil_sct.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +1 -1
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/kl5kusb105.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +1 -5
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/keyspan_pda.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +1 -1
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/keyspan.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +1 -1
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/ir-usb.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +3 -3
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/ipaq.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +4 -4
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/io_ti.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +2 -2
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/io_edgeport.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +1 -2
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/ftdi_sio.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +1 -1
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/empeg.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +1 -2
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/digi_acceleport.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +1 -2
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/cyberjack.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +1 -2
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/belkin_sa.c
#   2004/07/13 09:22:55-07:00 greg@kroah.com +1 -2
#   USB: change all usbserial drivers to use module_param()
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# ChangeSet
#   2004/07/12 16:54:06-07:00 luca.risolia@studio.unibo.it 
#   [PATCH] Updates for W99[87]CF and new SN9C10[12] driver
#   
#   This single patch contains some updates and cleanups for
#   the W996[87]CF driver and a new experimental V4L2 driver
#   for SONiX SN9C10[12] PC Camera Controllers connected to various
#   image sensors. I have not divided the patch in two logical
#   sub-patches becouse of two independent changes in one common
#   file, KConfigure.
#   
#   More informations about the SN9C10[12] can be found below in the
#   documentation. The driver is marked as "EXPERIMENTAL", meaning
#   that there are no known bugs, but further testing is necessary
#   before considering it stable. This the first driver using the new
#   SBGGR8 video format, which has been recently added to the mainline
#   kernel, so there are no available user application at the moment:
#   this is one more reason why it should be in the kernel now.
#   
#   Changes in W996[87]CF:
#   - remove w9968cf_externaldef.h now that ovcamchip.h is in the kernel;
#   - mark user pointers with __user in a cleaner way to avoid sparse
#     warnings;
#   - use appropriate exclusive wait macro during open();
#   - replace info(), err(), warn() with dev_info(), dev_err(), dev_warn(),
#     pr_debug(), pr_info();
#   - replace usb_unlink_urb() + wait_for_completion() with usb_kill_urb();
#   - fix memory offsets for buffers in the chip to be used with generic
#     image sensors;
#   - 'vppmod_load', 'debug', 'specific_debug' and 'simcams' module
#     parameters are now writeable by default;
#   - fix possible race conditions between disconnect() and open();
#   - add automatic 'ovcamchip' module loading option with 'ovmod_load'
#     module parameter;
#   - get rid of deprecated intermodule communication routines and use the
#     correct module registration/unregistration approach;
#   - remove period at the end of kernel messages;
#   - fix several typos;
#   - use MODULE_VERSION() macro;
#   - other small internal cleanups;
#   - documentation updates.
#   
#   Signed-off-by: Luca Risolia <luca.risolia@studio.unibo.it>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/media/w9968cf_vpp.h
#   2004/07/12 16:53:42-07:00 luca.risolia@studio.unibo.it +43 -0
# 
# include/linux/videodev.h
#   2004/07/09 12:39:03-07:00 luca.risolia@studio.unibo.it +2 -1
#   Updates for W99[87]CF and new SN9C10[12] driver
# 
# drivers/usb/media/w9968cf_vpp.h
#   2004/07/12 16:53:42-07:00 luca.risolia@studio.unibo.it +0 -0
#   BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/media/w9968cf_vpp.h
# 
# drivers/usb/media/w9968cf.h
#   2004/07/09 11:30:22-07:00 luca.risolia@studio.unibo.it +72 -43
#   Updates for W99[87]CF and new SN9C10[12] driver
# 
# drivers/usb/media/w9968cf.c
#   2004/07/09 11:27:21-07:00 luca.risolia@studio.unibo.it +538 -436
#   Updates for W99[87]CF and new SN9C10[12] driver
# 
# drivers/usb/media/sn9c102_tas5130d1b.c
#   2004/07/12 16:53:42-07:00 luca.risolia@studio.unibo.it +120 -0
# 
# drivers/usb/media/sn9c102_tas5110c1b.c
#   2004/07/12 16:53:41-07:00 luca.risolia@studio.unibo.it +98 -0
# 
# drivers/usb/media/sn9c102_sensor.h
#   2004/07/12 16:53:41-07:00 luca.risolia@studio.unibo.it +270 -0
# 
# drivers/usb/media/sn9c102_pas106b.c
#   2004/07/12 16:53:41-07:00 luca.risolia@studio.unibo.it +209 -0
# 
# drivers/usb/media/sn9c102_core.c
#   2004/07/12 16:53:41-07:00 luca.risolia@studio.unibo.it +2439 -0
# 
# drivers/usb/media/sn9c102.h
#   2004/07/12 16:53:41-07:00 luca.risolia@studio.unibo.it +182 -0
# 
# drivers/usb/media/Makefile
#   2004/07/09 21:48:06-07:00 luca.risolia@studio.unibo.it +2 -0
#   Updates for W99[87]CF and new SN9C10[12] driver
# 
# drivers/usb/media/Kconfig
#   2004/07/09 22:04:43-07:00 luca.risolia@studio.unibo.it +26 -14
#   Updates for W99[87]CF and new SN9C10[12] driver
# 
# Documentation/usb/w9968cf.txt
#   2004/07/09 13:12:30-07:00 luca.risolia@studio.unibo.it +70 -52
#   Updates for W99[87]CF and new SN9C10[12] driver
# 
# CREDITS
#   2004/07/09 12:36:47-07:00 luca.risolia@studio.unibo.it +2 -0
#   Updates for W99[87]CF and new SN9C10[12] driver
# 
# drivers/usb/media/sn9c102_tas5130d1b.c
#   2004/07/12 16:53:41-07:00 luca.risolia@studio.unibo.it +0 -0
#   BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/media/sn9c102_tas5130d1b.c
# 
# drivers/usb/media/sn9c102_tas5110c1b.c
#   2004/07/12 16:53:41-07:00 luca.risolia@studio.unibo.it +0 -0
#   BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/media/sn9c102_tas5110c1b.c
# 
# drivers/usb/media/sn9c102_sensor.h
#   2004/07/12 16:53:41-07:00 luca.risolia@studio.unibo.it +0 -0
#   BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/media/sn9c102_sensor.h
# 
# drivers/usb/media/sn9c102_pas106b.c
#   2004/07/12 16:53:41-07:00 luca.risolia@studio.unibo.it +0 -0
#   BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/media/sn9c102_pas106b.c
# 
# drivers/usb/media/sn9c102_core.c
#   2004/07/12 16:53:41-07:00 luca.risolia@studio.unibo.it +0 -0
#   BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/media/sn9c102_core.c
# 
# drivers/usb/media/sn9c102.h
#   2004/07/12 16:53:41-07:00 luca.risolia@studio.unibo.it +0 -0
#   BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/media/sn9c102.h
# 
# Documentation/usb/sn9c102.txt
#   2004/07/12 16:53:38-07:00 luca.risolia@studio.unibo.it +276 -0
# 
# Documentation/usb/sn9c102.txt
#   2004/07/12 16:53:38-07:00 luca.risolia@studio.unibo.it +0 -0
#   BitKeeper file /home/greg/linux/BK/usb-2.6/Documentation/usb/sn9c102.txt
# 
# BitKeeper/deleted/.del-w9968cf_externaldef.h~f278f2e511fe7f5e
#   2004/07/12 16:53:38-07:00 luca.risolia@studio.unibo.it +0 -0
#   Delete: drivers/usb/media/w9968cf_externaldef.h
# 
# ChangeSet
#   2004/07/12 16:47:49-07:00 david-b@pacbell.net 
#   [PATCH] USB: usb ethernet gadget, minor fixes + basic OTG support
#   
#   Update CDC Ethernet/RNDIS gadget driver to the latest:
#   
#       -	Basics of OTG support: providing the OTG descriptor
#   	in each configuration (as needed).   No HNP yet.
#   
#       -	Stop issuing partial-packet reads.  There's some hardware that
#   	only counts reads in packets, not bytes, so let's not bother.
#   	There are still software checks to catch framing gone wild.
#   
#       -	Fix a small bug that crept in with a memory leak fix: after
#           RNDIS requests, ep0 responses would use the RNDIS completion
#   	handler even for non-RNDIS requests.
#   
#   Signed-off-by:	David Brownell <dbrownell@users.sourceforge.net>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/gadget/ether.c
#   2004/07/10 05:41:00-07:00 david-b@pacbell.net +70 -34
#   USB: usb ethernet gadget, minor fixes + basic OTG support
# 
# ChangeSet
#   2004/07/12 16:47:26-07:00 david-b@pacbell.net 
#   [PATCH] USB: usb gadget zero, basic OTG updates
#   
#   This patch teaches "gadget zero" enough about OTG to pass simple USBCV
#   tests, mostly by including OTG descriptors in each configuration.  It
#   tests and reports OTG status, as reported by the USB controller driver.
#   
#   It also adds an option to build gadget zero to act as the designated
#   OTG "HNP Test Device", which exists primarily to trigger HNP.  However,
#   it won't currently request HNP.
#   
#   Includes other minor tweaks:  delete a timer on disconnect, reset
#   the req->zero flag, don't autoresume after disconnect.
#   
#   Signed-off-by:	David Brownell <dbrownell@users.sourceforge.net>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/gadget/zero.c
#   2004/07/10 05:40:59-07:00 david-b@pacbell.net +41 -4
#   USB: usb gadget zero, basic OTG updates
# 
# drivers/usb/gadget/Kconfig
#   2004/07/10 05:40:59-07:00 david-b@pacbell.net +10 -0
#   USB: usb gadget zero, basic OTG updates
# 
# ChangeSet
#   2004/07/12 16:47:01-07:00 david-b@pacbell.net 
#   [PATCH] USB: usb gadget API updates
#   
#   Gadget API updates, including new features:
#   
#       -	Adds several new optional calls that can be made to the
#   	USB peripheral controller:
#   
#   	    * VBUS session reporting, for use mostly by external
#   	      transcievers (such as isp1301).  Detection of VBUS
#   	      power is the first step in enumeration, and usually
#   	      corresponds to a device being plugged into a hub.
#   
#   	    * "Soft Connect" feature, wherein the D+ pullup is under
#   	      gadget driver control.  This is a second step during
#   	      enumeration, which lets the hub see the new device.
#   	      (Based on a patch by Alex Sanks <alex@netchip.com>.)
#   
#   	    * Control over VBUS current draw.  So for example this is
#   	      what a gadget driver uses during SET_CONFIGURATION to
#   	      say "it's OK to draw 300 mA from VBUS to recharge".
#   
#       -	Basic interfaces to support device-side USB OTG.  Feature
#   	flags and descriptors are in <linux/usb_ch9.h> already.
#   
#   	    * Reports whether the device has a Mini-AB port, so
#   	      that OTG support (desriptors etc) is required.
#   
#   	    * Reports OTG device feature flags for HNP.  (The OTG
#   	      spec requires user interfaces to report this stuff.)
#   	      Say if this is a B-Peripheral or an A-Peripheral.
#   
#   	    * Says that usb_gadget_wakeup() is how to access SRP,
#   	      and usb_gadget_disconnect() is how to access HNP.
#   
#       -	Minor updates/cleanups to comments (Linux 2.5-->2.6 etc)
#   
#   Currently there's no support for SRP-only OTG devices.
#   
#   Signed-off-by:	David Brownell <dbrownell@users.sourceforge.net>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# include/linux/usb_gadget.h
#   2004/07/10 05:40:59-07:00 david-b@pacbell.net +146 -7
#   USB: usb gadget API updates
# 
# ChangeSet
#   2004/07/12 16:46:34-07:00 david-b@pacbell.net 
#   [PATCH] USB: usb gadgetfs, handle omap_udc
#   
#   Tell gadgetfs about omap_udc.
#   Add some missing __user annotations.
#   
#   Signed-off-by:	David Brownell <dbrownell@users.sourceforge.net>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/gadget/inode.c
#   2004/07/10 05:40:59-07:00 david-b@pacbell.net +8 -4
#   USB: usb gadgetfs, handle omap_udc
# 
# ChangeSet
#   2004/07/12 16:46:07-07:00 david-b@pacbell.net 
#   [PATCH] USB: usb serial gadget, add omap_udc
#   
#   This lets the serial gadget work with another controller.
#   
#   Tell serial about omap_udc.  This driver still needs updating to
#   use the endpoint autoconfig suppport; like Gadget Zero, it
#   really shouldn't need _any_ hardware-specific #ifdeffery.
#   
#   Signed-off-by:	David Brownell <dbrownell@users.sourceforge.net>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/gadget/serial.c
#   2004/07/10 05:41:00-07:00 david-b@pacbell.net +14 -0
#   USB: usb serial gadget, add omap_udc
# 
# ChangeSet
#   2004/07/12 16:45:42-07:00 david-b@pacbell.net 
#   [PATCH] USB: misc ohci tweaks
#   
#   Various minor OHCI tweaks;
#   
#       - Fix osdl bugid=2503 by:
#   	* Change needlessly-scarey message (WARN_ON dumps stack)
#   	* Tries cleaning up, as if it's just IRQ lossage.
#   
#       - Force IRQs off when shutting down a controller that was
#         already stopped ... just in case.
#   
#       - Allow suspending OHCI during driver initialization, to
#         support more aggressive power management.
#   
#       - Fix some misleading/wrong debug messages.
#   
#   Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/host/ohci-q.c
#   2004/07/10 05:41:00-07:00 david-b@pacbell.net +1 -1
#   USB: misc ohci tweaks
# 
# drivers/usb/host/ohci-mem.c
#   2004/07/10 05:40:59-07:00 david-b@pacbell.net +1 -0
#   USB: misc ohci tweaks
# 
# drivers/usb/host/ohci-hub.c
#   2004/07/10 05:40:59-07:00 david-b@pacbell.net +2 -2
#   USB: misc ohci tweaks
# 
# drivers/usb/host/ohci-hcd.c
#   2004/07/10 05:40:59-07:00 david-b@pacbell.net +7 -1
#   USB: misc ohci tweaks
# 
# ChangeSet
#   2004/07/12 16:45:11-07:00 david-b@pacbell.net 
#   [PATCH] USB: usbnet, Sitecom LN-029
#   
#   ID for another AX8817x based usb2 Ethernet adapter.
#   
#   From:          Tim Chick <Tim.Chick@Conexant.com>
#   Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/net/usbnet.c
#   2004/07/09 12:11:15-07:00 david-b@pacbell.net +4 -0
#   USB: usbnet, Sitecom LN-029
# 
# ChangeSet
#   2004/07/12 16:44:40-07:00 dhollis@davehollis.com 
#   [PATCH] USB: ax8817x_unbind does not free the interrupt URB after unlinking
#   
#   ax8817x_unbind does not free the interrupt URB after unlinking.
#   
#   Noticed that the net->status already has a flag for link so my
#   private structure variable for link was redundant.  Worked around
#   this and was able to kill off the unique ax8817x_get_link() function
#   in the process.
#   
#   Signed-off-by: David Hollis <dhollis@davehollis.com>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/net/usbnet.c
#   2004/07/09 12:28:54-07:00 dhollis@davehollis.com +9 -17
#   USB: ax8817x_unbind does not free the interrupt URB after unlinking
# 
# ChangeSet
#   2004/07/09 15:27:20-07:00 dhollis@davehollis.com 
#   [PATCH] USB: usbnet:ax8817x - use interrupt URB for link detection
#   
#   This patch uses the interrupt URB on the ax8817x for link detection.
#   This allows the driver to notify userspace when link drops/comes back
#   so it can take action such as run dhclient, etc.
#   
#   I was also able to reduce the bind function by using some of the stock
#   mii_xxx calls as well as my own for handling initial link negotiation.
#   
#   Signed-off-by: David Hollis <dhollis@davehollis.com>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/net/usbnet.c
#   2004/07/09 08:04:13-07:00 dhollis@davehollis.com +84 -38
#   USB: usbnet:ax8817x - use interrupt URB for link detection
# 
# ChangeSet
#   2004/07/09 15:26:22-07:00 stern@rowland.harvard.edu 
#   [PATCH] USB: Allow NULL argument in usb_unlink_urb() and usb_kill_urb()
#   
#   It makes sense for APIs involved in cleanup activities (like kfree()) to
#   accept NULL arguments.  Doing so frees drivers from the responsibility of
#   checking whether each resource was actually acquired before trying to
#   release it.  Accordingly, this patch makes usb_unlink_urb() and
#   usb_kill_urb() accept a NULL pointer (which used to be acceptable until I
#   changed it) and notes explicitly in the kerneldoc that such arguments are
#   permitted.
#   
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/core/urb.c
#   2004/07/09 02:25:46-07:00 stern@rowland.harvard.edu +9 -5
#   USB: Allow NULL argument in usb_unlink_urb() and usb_kill_urb()
# 
# ChangeSet
#   2004/07/08 15:38:39-07:00 lcapitulino@prefeitura.sp.gov.br 
#   [PATCH] USB: usb/core/file.c::usb_major_init() cleanup.
#   
#    This patch does a cleanup for usb/core/file.c::usb_major_init(), which
#    is:
#   
#   *) in error condition, returns the error code from register_chrdev(),
#      insted returning -EBUSY;
#   
#   *) adds missing audit for class_register();
#   
#   *) only calls devfs_mk_dir() if the prior calls have success.
#   
#   
#   Signed-off-by: Luiz Capitulino <lcapitulino@prefeitura.sp.gov.br>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/core/file.c
#   2004/06/30 13:11:36-07:00 lcapitulino@prefeitura.sp.gov.br +15 -4
#   USB: usb/core/file.c::usb_major_init() cleanup.
# 
# ChangeSet
#   2004/07/08 13:50:30-07:00 ddstreet@ieee.org 
#   [PATCH] USB: fix usbfs mount options ignored bug
#   
#   Ok here is a patch to make the mount options work.  In addition to
#   implementing the remount function, it removes the parse_options() call
#   from usb_fill_super and adds a "ignore" flag around the mounting that gets
#   done in create_special_files.  The parse_options call in usb_fill_super is
#   removed because it is not needed when remount is implemented.  The
#   ignore_mount flag is needed because the simple_pin_fs function calls
#   remount with no mount options - i.e. it's not a real mount.  So the mount
#   options would be cleared out.  The ignore_mount flag causes the remount
#   function to only act on real mounts.
#   
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/core/inode.c
#   2004/07/08 12:59:04-07:00 ddstreet@ieee.org +103 -5
#   USB: fix usbfs mount options ignored bug
# 
# ChangeSet
#   2004/07/08 12:49:42-07:00 greg@kroah.com 
#   [PATCH] USB: more sparse fixups that found a real bug in the se401 driver
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/media/se401.c
#   2004/07/08 12:26:02-07:00 greg@kroah.com +1 -1
#   USB: more sparse fixups that found a real bug in the se401 driver
# 
# ChangeSet
#   2004/07/08 12:49:03-07:00 stern@rowland.harvard.edu 
#   [PATCH] USB: Fix endianness bug in UHCI driver
#   
#   This patch fixes a byte-swapping error in the UHCI driver.  It has been
#   present since 2.6.6 and only got tracked down just now!  Thanks a lot to
#   Michel Roelofs for all his help and testing.
#   
#   This should be pushed through to Linus in time to appear in 2.6.8, if
#   possible.
#   
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/host/uhci-hcd.c
#   2004/07/08 03:57:48-07:00 stern@rowland.harvard.edu +1 -1
#   USB: Fix endianness bug in UHCI driver
# 
# ChangeSet
#   2004/07/08 12:48:12-07:00 mika@osdl.org 
#   [PATCH] USB: Trivial fix to include/linux/usb.h
#   
#   Hi Greg!
#   
#   I am compiling allmodconfig with some extra gcc warnings enabled
#   (-W -Wno-unused -Wno-sign-compare -Winline -Wundef) and got this one:
#   
#     CC [M]  security/root_plug.o
#   In file included from security/root_plug.c:30:
#   include/linux/usb.h:358: warning: `inline' is not at beginning of declaration
#   
#   Patch is trivial:
#   
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# include/linux/usb.h
#   2004/07/07 15:00:22-07:00 mika@osdl.org +1 -1
#   USB: Trivial fix to include/linux/usb.h
# 
# ChangeSet
#   2004/07/08 12:47:40-07:00 olh@suse.de 
#   [PATCH] USB: fix lockup with 2.6 keyspan_pda driver
#   
#   I suggest to put the pointer to port instead of the pointer to the
#   pointer to port to the workqueue data.
#   Did that driver ever work with 2.6?
#   
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/serial/keyspan_pda.c
#   2004/07/08 03:26:38-07:00 olh@suse.de +1 -1
#   USB: fix lockup with 2.6 keyspan_pda driver
# 
# ChangeSet
#   2004/07/07 17:58:26-07:00 greg@kroah.com 
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/storage/sddr09.c
#   2004/07/07 17:57:40-07:00 greg@kroah.com +1 -1
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/net/usbnet.c
#   2004/07/07 17:57:40-07:00 greg@kroah.com +13 -13
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/net/kaweth.c
#   2004/07/07 17:57:41-07:00 greg@kroah.com +1 -1
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/misc/usbtest.c
#   2004/07/07 17:57:41-07:00 greg@kroah.com +12 -12
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/misc/speedtch.c
#   2004/07/07 17:57:41-07:00 greg@kroah.com +1 -1
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/misc/auerswald.c
#   2004/07/07 17:57:41-07:00 greg@kroah.com +1 -1
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/media/pwc-if.c
#   2004/07/07 17:57:40-07:00 greg@kroah.com +2 -1
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/media/pwc-ctrl.c
#   2004/07/07 17:57:40-07:00 greg@kroah.com +1 -1
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/media/ov511.c
#   2004/07/07 17:57:41-07:00 greg@kroah.com +1 -1
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/input/powermate.c
#   2004/07/07 17:57:40-07:00 greg@kroah.com +1 -1
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/input/hid-core.c
#   2004/07/07 17:57:40-07:00 greg@kroah.com +8 -2
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/input/aiptek.c
#   2004/07/07 17:57:40-07:00 greg@kroah.com +5 -5
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/image/microtek.c
#   2004/07/07 17:57:40-07:00 greg@kroah.com +1 -1
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/image/mdc800.c
#   2004/07/07 17:57:41-07:00 greg@kroah.com +12 -14
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/host/ohci-q.c
#   2004/07/07 17:57:40-07:00 greg@kroah.com +7 -7
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/host/ohci-mem.c
#   2004/07/07 17:57:40-07:00 greg@kroah.com +3 -3
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/host/ohci-hub.c
#   2004/07/07 17:57:41-07:00 greg@kroah.com +3 -3
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/host/ohci-hcd.c
#   2004/07/07 17:57:41-07:00 greg@kroah.com +5 -5
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/host/ohci-dbg.c
#   2004/07/07 17:57:41-07:00 greg@kroah.com +3 -3
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/host/ehci-sched.c
#   2004/07/07 17:57:41-07:00 greg@kroah.com +6 -6
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/host/ehci-dbg.c
#   2004/07/07 17:57:40-07:00 greg@kroah.com +1 -1
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/class/usb-midi.c
#   2004/07/07 17:57:40-07:00 greg@kroah.com +8 -8
#   USB: more sparse cleanups (all pretty much NULL usages.)
#   
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# ChangeSet
#   2004/07/07 17:13:59-07:00 greg@kroah.com 
#   USB: fix up the wording in the emi26 firmware file to match the other kernel firmware files.
# 
# drivers/usb/misc/emi26_fw.h
#   2004/07/07 17:13:16-07:00 greg@kroah.com +4 -0
#   USB: fix up the wording in the emi26 firmware file to match the other kernel firmware files.
# 
# ChangeSet
#   2004/07/07 15:43:01-07:00 stern@rowland.harvard.edu 
#   [PATCH] USB: Interpret down_trylock() result code correctly in
#   
#   As Andrew Morton has already spotted, I messed up the interpretation of
#   the result codes from various _trylock() routines.  I didn't notice that
#   down_trylock() and down_read_trylock() use opposite conventions for
#   indicating success!  This patch fixes the incorrect interpretation of
#   down_trylock().  That error may well be responsible for some of the
#   problems cropping up recently with OHCI controllers.  Please apply.
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/core/usb.c
#   2004/07/06 05:00:32-07:00 stern@rowland.harvard.edu +1 -1
#   USB: Interpret down_trylock() result code correctly in
# 
# ChangeSet
#   2004/07/05 20:02:01-07:00 akpm@osdl.org 
#   [PATCH] USB: usb deadlock fix
#   
#   usb_trylock_device() returns non-zero on success.
#   
#   Signed-off-by: Andrew Morton <akpm@osdl.org>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/host/ohci-hub.c
#   2004/07/03 00:18:41-07:00 akpm@osdl.org +1 -2
#   USB: usb deadlock fix
# 
# ChangeSet
#   2004/07/02 15:57:47-07:00 stern@rowland.harvard.edu 
#   [PATCH] USB: Remove hub's children upon unbinding
#   
#   This patch fixes a logical hole in the hub driver.  It's possible for the
#   driver to be unbound from a hub without physically unplugging the hub.
#   For example, writing 0 into the bConfigurationValue attribute file will
#   have this effect.  When this happens, we need to make sure that all the
#   child devices of the hub are logically disconnected and their ports
#   disabled.
#   
#   That's what this patch does.  It's a little bit tricky because we can't
#   simply call usb_disconnect() from within the hub driver's disconnect()
#   routine.  While that routine is running it holds the usb bus writelock,
#   but usb_disconnect() would try to acquire it again.  Instead
#   schedule_work() is used, so after a brief delay the children will be
#   removed.
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/core/hub.c
#   2004/07/01 07:53:20-07:00 stern@rowland.harvard.edu +41 -2
#   USB: Remove hub's children upon unbinding
# 
# ChangeSet
#   2004/07/02 15:56:49-07:00 stern@rowland.harvard.edu 
#   [PATCH] USB: Store pointer to usb_device in private hub structure
#   
#   This patch adds a pointer to the hub's usb_device into the usb_hub private
#   structure.  It's a small change, and permits a small amount of
#   simplification in a few spots, i.e., avoid calling interface_to_usbdev().
#   This doesn't really do much in itself, but it's a prerequisite for the
#   next patch.  (A situation arises where we can't use the interface pointer
#   to find the usb_device because the interface might not exist.)
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/core/hub.h
#   2004/07/01 04:48:09-07:00 stern@rowland.harvard.edu +1 -0
#   USB: Store pointer to usb_device in private hub structure
# 
# drivers/usb/core/hub.c
#   2004/07/01 07:26:05-07:00 stern@rowland.harvard.edu +17 -18
#   USB: Store pointer to usb_device in private hub structure
# 
# ChangeSet
#   2004/07/02 15:54:11-07:00 stern@rowland.harvard.edu 
#   [PATCH] USB: Disallow probing etc. for suspended devices
#   
#   One of the few points from the recent discussion that all participants
#   agreed on was that we should not allow probing or config changes to be
#   attempted on suspended devices.  This patch implements that policy, and it
#   also prevents attempts to change altsettings.  That's just for
#   consistency's sake; even without the patch an attempted altsetting change
#   on a suspended device would simply fail without doing any harm.  By
#   contrast, a configuration change would unbind all the existing drivers
#   before encountering an error, something we don't want to happen.
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/core/usb.c
#   2004/06/30 06:44:26-07:00 stern@rowland.harvard.edu +2 -0
#   USB: Disallow probing etc. for suspended devices
# 
# drivers/usb/core/message.c
#   2004/06/30 06:48:12-07:00 stern@rowland.harvard.edu +9 -0
#   USB: Disallow probing etc. for suspended devices
# 
# ChangeSet
#   2004/07/02 15:25:25-07:00 stern@rowland.harvard.edu 
#   [PATCH] USB: Don't ask for string descriptor lengths
#   
#   Okay, here's a revised patch (as332b).  This tries first to ask for 255
#   bytes, and if that fails then it asks for the length and the full
#   descriptor.  Hopefully nobody will object to applying this version...
#   
#   You know, it occurs to me that the have_langid field in usb_device could
#   easily be eliminated.  Just set string_langid to -1 during initialization
#   and test for whether or not it is >= 0.  I'll do that some other time.
#   
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/core/message.c
#   2004/06/25 08:04:06-07:00 stern@rowland.harvard.edu +121 -101
#   USB: Don't ask for string descriptor lengths
# 
# ChangeSet
#   2004/07/02 15:24:44-07:00 stern@rowland.harvard.edu 
#   [PATCH] USB: Don't track endpoint halts in usbcore
#   
#   The current mechanism for keeping track of which endpoints in a device are
#   halted is both flawed and unnecessary.  It's flawed because devices are
#   free to change the endpoint status whenever they want without telling us,
#   and it's unnecessary because an URB submitted for a halted endpoint will
#   quickly receive an error indication.
#   
#   This patch removes the code associated with tracking halts: the halted[]
#   member of usb_device, the usb_endpoint_halt, usb_endpoint_halted, and
#   usb_endpoint_running macros, all the places where they are used or
#   checked, and the places in the host controller drivers where they get set.
#   
#   This is part of my ongoing program for cleaning up usbcore.  Please apply.
#   
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# include/linux/usb.h
#   2004/06/24 09:44:16-07:00 stern@rowland.harvard.edu +0 -6
#   USB: Don't track endpoint halts in usbcore
# 
# drivers/usb/storage/transport.c
#   2004/06/24 09:44:16-07:00 stern@rowland.harvard.edu +1 -3
#   USB: Don't track endpoint halts in usbcore
# 
# drivers/usb/image/microtek.c
#   2004/06/24 09:44:16-07:00 stern@rowland.harvard.edu +2 -2
#   USB: Don't track endpoint halts in usbcore
# 
# drivers/usb/host/uhci-hcd.c
#   2004/06/24 09:44:16-07:00 stern@rowland.harvard.edu +0 -4
#   USB: Don't track endpoint halts in usbcore
# 
# drivers/usb/host/ohci-q.c
#   2004/06/24 09:44:16-07:00 stern@rowland.harvard.edu +0 -6
#   USB: Don't track endpoint halts in usbcore
# 
# drivers/usb/host/hc_simple.c
#   2004/06/24 09:44:15-07:00 stern@rowland.harvard.edu +0 -5
#   USB: Don't track endpoint halts in usbcore
# 
# drivers/usb/host/ehci-q.c
#   2004/06/24 09:44:16-07:00 stern@rowland.harvard.edu +2 -10
#   USB: Don't track endpoint halts in usbcore
# 
# drivers/usb/core/urb.c
#   2004/06/24 09:44:16-07:00 stern@rowland.harvard.edu +0 -7
#   USB: Don't track endpoint halts in usbcore
# 
# drivers/usb/core/message.c
#   2004/06/24 09:44:15-07:00 stern@rowland.harvard.edu +7 -17
#   USB: Don't track endpoint halts in usbcore
# 
# drivers/usb/core/hub.c
#   2004/06/24 09:44:16-07:00 stern@rowland.harvard.edu +1 -5
#   USB: Don't track endpoint halts in usbcore
# 
# drivers/usb/core/hcd.h
#   2004/06/24 09:44:16-07:00 stern@rowland.harvard.edu +0 -2
#   USB: Don't track endpoint halts in usbcore
# 
# drivers/usb/core/hcd.c
#   2004/06/24 09:44:16-07:00 stern@rowland.harvard.edu +2 -5
#   USB: Don't track endpoint halts in usbcore
# 
# drivers/usb/core/devices.c
#   2004/06/24 09:44:16-07:00 stern@rowland.harvard.edu +1 -2
#   USB: Don't track endpoint halts in usbcore
# 
# drivers/isdn/hisax/st5481_usb.c
#   2004/06/24 09:44:15-07:00 stern@rowland.harvard.edu +0 -3
#   USB: Don't track endpoint halts in usbcore
# 
# ChangeSet
#   2004/07/02 15:23:56-07:00 stern@rowland.harvard.edu 
#   [PATCH] USB: Implement usb_lock_device_for_reset()
#   
#   This patch creates a special locking routine intended specifically for use
#   when calling usb_reset_device().  A special routine like this is needed to
#   avoid deadlocks with disconnect().  Symbolically:
#   
#   	Khubd				Driver
#   	-----				------
#   					Wants to reset a device, so calls
#   					its own do_reset() function
#   	Receives connect-change
#   	notification and calls
#   	usb_disconnect()
#   
#   	Locks the device and calls
#   	driver->disconnect()
#   					do_reset() tries to lock the
#   					device for the reset, and blocks
#   	disconnect() can't return
#   	until do_reset() is finished
#   
#   One simple-minded way out would be for the driver's do_reset routine to
#   use usb_trylock_device().  I don't like this because it will fail if the
#   device happens to be locked for some innocuous reason, like somebody
#   reading its entry in /proc/bus/usb/devices.
#   
#   Instead, usb_lock_device_for_reset() calls usb_trylock_device()
#   repeatedly, in a polling loop.  It is careful to check that the device
#   state isn't NOTATTACHED and that the driver's interface isn't being
#   unbound; this will allow the lock-and-reset sequence to fail in scenarios
#   like the one above.
#   
#   For the new routine to be able to tell when an interface is being unbound,
#   I had to add an additional field to struct usb_interface.  It's a simple
#   enumeration with four possible values:
#   
#   	USB_INTERFACE_UNBOUND, USB_INTERFACE_BINDING,
#   	USB_INTERFACE_BOUND, USB_INTERFACE_UNBINDING
#   
#   The extra overhead of the new field is not large enough to matter.
#   
#   A quick summary:
#   
#   	Rename __usb_reset_device to usb_reset_device and get rid of
#   	the alternate entry point.
#   
#   	Notify the HCD that endpoint-0's maxpacket size may change
#   	during a device reset (should have been written earlier).
#   
#   	Add the new usb_interface_condition enumeration field to
#   	struct usb_interface.
#   
#   	Set the new field appropriately as interfaces are bound,
#   	unbound, claimed, and released.
#   
#   	Add usb_lock_device_for_reset().
#   
#   	Change the usb-storage, microtek, and stir4200 IRDA drivers
#   	to make them use the new locking routine.
#   
#   With this patch applied, and using an altered gadget driver that forces
#   usb-storage to request a device reset, I've been able to go through a
#   large number of different tests successfully.  Reset during probe(),
#   reset after probe(), disconnect during reset(), reset with device
#   descriptor changing...  They all work beautifully.
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# include/linux/usb.h
#   2004/06/24 07:09:11-07:00 stern@rowland.harvard.edu +13 -2
#   USB: Implement usb_lock_device_for_reset()
# 
# drivers/usb/storage/scsiglue.c
#   2004/06/24 07:09:11-07:00 stern@rowland.harvard.edu +11 -3
#   USB: Implement usb_lock_device_for_reset()
# 
# drivers/usb/image/microtek.h
#   2004/06/24 07:09:11-07:00 stern@rowland.harvard.edu +1 -0
#   USB: Implement usb_lock_device_for_reset()
# 
# drivers/usb/image/microtek.c
#   2004/06/24 07:09:11-07:00 stern@rowland.harvard.edu +9 -2
#   USB: Implement usb_lock_device_for_reset()
# 
# drivers/usb/core/usb.c
#   2004/06/24 07:09:11-07:00 stern@rowland.harvard.edu +53 -0
#   USB: Implement usb_lock_device_for_reset()
# 
# drivers/usb/core/hub.c
#   2004/06/24 07:09:11-07:00 stern@rowland.harvard.edu +10 -13
#   USB: Implement usb_lock_device_for_reset()
# 
# drivers/usb/core/devio.c
#   2004/06/24 07:09:11-07:00 stern@rowland.harvard.edu +1 -1
#   USB: Implement usb_lock_device_for_reset()
# 
# drivers/net/irda/stir4200.c
#   2004/06/24 07:09:11-07:00 stern@rowland.harvard.edu +10 -0
#   USB: Implement usb_lock_device_for_reset()
# 
# ChangeSet
#   2004/07/02 15:22:54-07:00 stern@rowland.harvard.edu 
#   [PATCH] USB: Implement usb_lock_device() and friends
#   
#   This patch plugs a major hole in USB device locking.  Whenever a new
#   driver is loaded and the matching unbound interfaces are probed, no
#   devices are locked!  Similarly, when a driver is unloaded and its
#   interfaces are unbound, nothing is locked.
#   
#   The patch fixes the problem by introducing a new rwsem and encapsulating
#   the locking procedures.  To lock a single device, the new routine
#   usb_lock_device() gets a readlock on the rwsem and a lock on the device's
#   ->serialize.  When drivers are loaded or removed, all devices are
#   effectively locked by getting a writelock on the rwsem.  The rwsem is
#   also writelocked in one other oddball circumstance: when telling the
#   driver-model core to reprobe all the unbound interfaces on a bus.
#   
#   Details of the patch:
#   
#   	Implement usb_lock_device(), usb_unlock_device(),
#   	usb_lock_all_devices(), usb_unlock_all_devices(), and
#   	usb_trylock_device().
#   
#   	Call usb_(un)lock_all_devices() when registering and
#   	deregistering USB drivers and when calling bus_rescan_devices().
#   
#   	Convert all existing places that access usbdev->serialize
#   	directly to make them call the appropriate locking routine.
#   
#   	Add comments warning about the need to use the new routines,
#   	and change the terminology in existing comments to talk about
#   	locking a device rather than owning usbdev->serialize.
#   
#   	Add proper locking to usb_find_device() and match_device(),
#   	to protect against topology changes while scanning the
#   	device tree.
#   
#   With this change, the USB device locking model is almost complete.  There
#   remains only the need for appropriate locking for routines that call
#   usb_reset_device().  That patch will come next.
#   
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# include/linux/usb.h
#   2004/06/24 03:41:12-07:00 stern@rowland.harvard.edu +16 -0
#   USB: Implement usb_lock_device() and friends
# 
# drivers/usb/host/ohci-pci.c
#   2004/06/24 03:41:12-07:00 stern@rowland.harvard.edu +4 -4
#   USB: Implement usb_lock_device() and friends
# 
# drivers/usb/host/ohci-hub.c
#   2004/06/24 03:41:12-07:00 stern@rowland.harvard.edu +5 -5
#   USB: Implement usb_lock_device() and friends
# 
# drivers/usb/host/ehci-hub.c
#   2004/06/24 03:41:12-07:00 stern@rowland.harvard.edu +1 -1
#   USB: Implement usb_lock_device() and friends
# 
# drivers/usb/core/usb.h
#   2004/06/24 03:41:12-07:00 stern@rowland.harvard.edu +3 -0
#   USB: Implement usb_lock_device() and friends
# 
# drivers/usb/core/usb.c
#   2004/06/24 03:41:12-07:00 stern@rowland.harvard.edu +88 -7
#   USB: Implement usb_lock_device() and friends
# 
# drivers/usb/core/sysfs.c
#   2004/06/24 03:41:12-07:00 stern@rowland.harvard.edu +2 -2
#   USB: Implement usb_lock_device() and friends
# 
# drivers/usb/core/message.c
#   2004/06/24 03:41:12-07:00 stern@rowland.harvard.edu +7 -7
#   USB: Implement usb_lock_device() and friends
# 
# drivers/usb/core/hub.c
#   2004/06/24 03:41:12-07:00 stern@rowland.harvard.edu +7 -7
#   USB: Implement usb_lock_device() and friends
# 
# drivers/usb/core/hcd.c
#   2004/06/24 03:41:12-07:00 stern@rowland.harvard.edu +4 -4
#   USB: Implement usb_lock_device() and friends
# 
# drivers/usb/core/devio.c
#   2004/06/24 03:41:12-07:00 stern@rowland.harvard.edu +13 -9
#   USB: Implement usb_lock_device() and friends
# 
# drivers/usb/core/devices.c
#   2004/06/24 03:41:12-07:00 stern@rowland.harvard.edu +5 -5
#   USB: Implement usb_lock_device() and friends
# 
# ChangeSet
#   2004/07/02 15:22:14-07:00 stern@rowland.harvard.edu 
#   [PATCH] USB: Make hub driver use usb_kill_urb()
#   
#   This is a rerun of as278, updated to match the current source.  It changes
#   the hub driver, replacing calls to synchronous usb_unlink_urb() with
#   usb_kill_urb() and removing the machinery formerly needed to synchronize
#   the status URB handler with the rest of the driver.
#   
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# drivers/usb/core/hub.h
#   2004/06/23 07:02:05-07:00 stern@rowland.harvard.edu +0 -2
#   USB: Make hub driver use usb_kill_urb()
# 
# drivers/usb/core/hub.c
#   2004/06/23 07:07:26-07:00 stern@rowland.harvard.edu +12 -28
#   USB: Make hub driver use usb_kill_urb()
# 
# ChangeSet
#   2004/07/02 15:21:04-07:00 stern@rowland.harvard.edu 
#   [PATCH] USB: Add usb_kill_urb()
#   
#   This patch is a slightly revised version of as277c, updated to match the
#   current source.  The only difference from the older version is that this
#   makes urb->use_count into an atomic_t, to avoid the overhead of an extra
#   locking step each time an URB is submitted and given back.  The important
#   features of this patch are:
#   
#   	-EPERM added to Documentation/usb/error-codes.txt.
#   
#   	Failure to use URB_ASYNC_UNLINK with usb_unlink_urb() is
#   	deprecated in the documentation.
#   
#   	New ->reject and ->use_count fields added to struct urb.
#   	The reject field is protected by urb->lock, and locking is
#   	required only in usb_kill_urb() which doesn't have to be fast.
#   
#   	Single wait_queue used for all processes waiting inside
#   	usb_kill_urb().  The wait queue is woken up only when an URB
#   	is given back with ->reject set.
#   
#   	usb_rh_status_dequeue() changed to return int.  It looks like
#   	this function should be declared static; it's not used outside
#   	the hcd.c file.
#   
#   	Prototype for unlink_urb() in struct usb_operations is changed
#   	to include a status code argument.  This is necessary so that
#   	the different unlink paths can return -ENOENT and -ECONNRESET
#   	as appropriate.
#   
#   	Support for synchronous usb_unlink_urb() has been removed;
#   	such calls are passed to usb_kill_urb().
#   
#   	Kerneldoc for usb_unlink_urb() is updated.
#   
#   	usb_kill_urb() added to urb.c.
#   
#   	hc_simple() host driver is partially updated -- it should
#   	compile but it won't really work right.
#   
#   
#   Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
#   Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
# 
# include/linux/usb.h
#   2004/06/23 08:00:50-07:00 stern@rowland.harvard.edu +7 -2
#   USB: Add usb_kill_urb()
# 
# drivers/usb/host/hc_simple.c
#   2004/06/23 08:00:50-07:00 stern@rowland.harvard.edu +10 -34
#   USB: Add usb_kill_urb()
# 
# drivers/usb/core/urb.c
#   2004/06/23 08:00:50-07:00 stern@rowland.harvard.edu +56 -18
#   USB: Add usb_kill_urb()
# 
# drivers/usb/core/hcd.h
#   2004/06/23 08:00:50-07:00 stern@rowland.harvard.edu +3 -2
#   USB: Add usb_kill_urb()
# 
# drivers/usb/core/hcd.c
#   2004/06/23 08:00:50-07:00 stern@rowland.harvard.edu +43 -96
#   USB: Add usb_kill_urb()
# 
# Documentation/usb/error-codes.txt
#   2004/06/23 08:00:50-07:00 stern@rowland.harvard.edu +2 -0
#   USB: Add usb_kill_urb()
# 
# ChangeSet
#   2004/06/30 10:48:26-07:00 greg@kroah.com 
#   [PATCH] USB: add 3 Phidget device ids to the HID blacklist.
# 
# drivers/usb/input/hid-core.c
#   2004/06/30 10:25:19-07:00 greg@kroah.com +8 -0
#   USB: add 3 Phidget device ids to the HID blacklist.
# 
diff -Nru a/CREDITS b/CREDITS
--- a/CREDITS	2004-07-13 13:12:22 -07:00
+++ b/CREDITS	2004-07-13 13:12:22 -07:00
@@ -2703,7 +2703,9 @@
 
 N: Luca Risolia
 E: luca.risolia@studio.unibo.it
+P: 1024D/FCE635A4 88E8 F32F 7244 68BA 3958  5D40 99DA 5D2A FCE6 35A4
 D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chips
+D: V4L2 driver for SN9C10[12] PC Camera Controllers
 S: Via Liberta' 41/A
 S: Osio Sotto, 24046, Bergamo
 S: Italy
diff -Nru a/Documentation/usb/error-codes.txt b/Documentation/usb/error-codes.txt
--- a/Documentation/usb/error-codes.txt	2004-07-13 13:12:22 -07:00
+++ b/Documentation/usb/error-codes.txt	2004-07-13 13:12:22 -07:00
@@ -47,6 +47,8 @@
 -ESHUTDOWN	The host controller has been disabled due to some
 		problem that could not be worked around.
 
+-EPERM		Submission failed because urb->reject was set.
+
 
 **************************************************************************
 *                   Error codes returned by in urb->status               *
diff -Nru a/Documentation/usb/sn9c102.txt b/Documentation/usb/sn9c102.txt
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/Documentation/usb/sn9c102.txt	2004-07-13 13:12:22 -07:00
@@ -0,0 +1,276 @@
+
+                        SN9C10[12] PC Camera Controllers
+                                Driver for Linux
+                        ================================
+
+                               - Documentation -
+
+
+Index
+=====
+1.  Copyright
+2.  License
+3.  Overview
+4.  Module dependencies
+5.  Module loading
+6.  Module parameters
+7.  Device control through "sysfs"
+8.  Supported devices
+9.  How to add support for new image sensors
+10. Note for V4L2 developers
+11. Contact information
+12. Credits
+
+
+1. Copyright
+============
+Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>
+
+SONiX is a trademark of SONiX Technology Company Limited, inc.
+This driver is not sponsored or developed by SONiX.
+
+
+2. License
+==========
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+3. Overview
+===========
+This driver attempts to support the video streaming capabilities of the devices
+mounting the SONiX SN9C101 or SONiX SN9C102 PC Camera Controllers.
+
+- It's worth to note that SONiX has never collaborated with me during the
+development of this project, despite of several requests for enough detailed
+specifications of the register tables, compression engine and video data format
+of the above chips -
+
+Up to 64 cameras can be handled at the same time. They can be connected and
+disconnected from the host many times without turning off the computer, if
+your system supports the hotplug facility.
+
+The driver relies on the Video4Linux2 and USB core modules. It has been
+designed to run properly on SMP systems as well.
+
+The latest version of the SN9C10[12] driver can be found at the following URL:
+http://go.lamarinapunto.com/
+
+
+4. Module dependencies
+======================
+For it to work properly, the driver needs kernel support for Video4Linux and
+USB.
+
+The following options of the kernel configuration file must be enabled and
+corresponding modules must be compiled:
+
+	# Multimedia devices
+	#
+	CONFIG_VIDEO_DEV=m
+
+	# USB support
+	#
+	CONFIG_USB=m
+
+In addition, depending on the hardware being used, the modules below are
+necessary:
+
+	# USB Host Controller Drivers
+	#
+	CONFIG_USB_EHCI_HCD=m
+	CONFIG_USB_UHCI_HCD=m
+	CONFIG_USB_OHCI_HCD=m
+
+And finally:
+
+	# USB Multimedia devices
+	#
+	CONFIG_USB_SN9C102=m
+
+
+5. Module loading
+=================
+To use the driver, it is necessary to load the "sn9c102" module into memory
+after every other module required: "videodev", "usbcore" and, depending on
+the USB host controller you have, "ehci-hcd", "uhci-hcd" or "ohci-hcd".
+
+Loading can be done as shown below:
+
+	[root@localhost home]# modprobe usbcore
+	[root@localhost home]# modprobe sn9c102
+
+At this point the devices should be recognized. You can invoke "dmesg" to
+analyze kernel messages and verify that the loading process has gone well:
+
+	[user@localhost home]$ dmesg
+
+
+6. Module parameters
+====================
+Module parameters are listed below:
+-------------------------------------------------------------------------------
+Name:           video_nr
+Type:           int array (min = 0, max = 32)
+Syntax:         <-1|n[,...]> 
+Description:    Specify V4L2 minor mode number:
+                -1 = use next available
+                 n = use minor number n
+                You can specify up to 32 cameras this way.
+                For example:
+                video_nr=-1,2,-1 would assign minor number 2 to the second
+                recognized camera and use auto for the first one and for every
+                other camera.
+Default:        -1
+-------------------------------------------------------------------------------
+Name:           debug
+Type:           int
+Syntax:         <n> 
+Description:    Debugging information level, from 0 to 3:
+                0 = none (use carefully)
+                1 = critical errors
+                2 = significant informations
+                3 = more verbose messages
+                Level 3 is useful for testing only, when just one device
+                is used.
+Default:        2
+-------------------------------------------------------------------------------
+
+
+7. Device control through "sysfs"
+=================================
+It is possible to read and write both the SN9C10[12] and the image sensor
+registers by using the "sysfs" filesystem interface.
+
+Every time a supported device is recognized, read-only files named "redblue"
+and "green" are created in the /sys/class/video4linux/videoX directory. You can
+set the red, blue and green channel's gain by writing the desired value to
+them. The value may range from 0 to 15 for each channel; this means that
+"redblue" accepts 8-bit values, where the low 4 bits are reserved for red and
+the others for blue.
+
+There are other four entries in the directory above for each registered camera:
+"reg", "val", "i2c_reg" and "i2c_val". The first two files control the
+SN9C10[12] bridge, while the other two control the sensor chip. "reg" and
+"i2c_reg" hold the values of the current register index where the following
+reading/writing operations are addressed at through "val" and "i2c_val". Their
+use is not intended for end-users, unless you know what you are doing. Note
+that "i2c_reg" and "i2c_val" won't be created if the sensor does not actually
+support the standard I2C protocol. Also, remember that you must be logged in as
+root before writing to them.
+
+As an example, suppose we were to want to read the value contained in the
+register number 1 of the sensor register table - which usually is the product
+identifier - of the camera registered as "/dev/video0":
+
+	[root@localhost #] cd /sys/class/video4linux/video0
+	[root@localhost #] echo 1 > i2c_reg
+	[root@localhost #] cat i2c_val
+
+Now let's set the green gain's register of the SN9C10[12] chip to 2:
+
+	[root@localhost #] echo 0x11 > reg
+	[root@localhost #] echo 2 > val
+
+Note that the SN9C10[12] always returns 0 when some of its registers are read.
+To avoid race conditions, all the I/O accesses to the files are serialized.
+
+
+8. Supported devices
+====================
+- I won't mention any of the names of the companies as well as their products
+here. They have never collaborated with me, so no advertising -
+
+From the point of view of a driver, what unambiguously identify a device are
+its vendor and product USB identifiers. Below is a list of known identifiers of
+devices mounting the SN9C10[12] PC camera controllers:
+
+Vendor ID  Product ID
+---------  ----------
+0xc45      0x6001
+0xc45      0x6005
+0xc45      0x6009
+0xc45      0x600d
+0xc45      0x6024
+0xc45      0x6025
+0xc45      0x6028
+0xc45      0x6029
+0xc45      0x602a
+0xc45      0x602c
+0xc45      0x8001
+
+The list above does NOT imply that all those devices work with this driver: up
+until now only the ones that mount the following image sensors are supported.
+Kernel messages will always tell you whether this is the case:
+
+Model       Manufacturer
+-----       ------------
+PAS106B     PixArt Imaging Inc.
+TAS5110C1B  Taiwan Advanced Sensor Corporation
+TAS5130D1B  Taiwan Advanced Sensor Corporation
+
+If you think your camera is based on the above hardware and is not actually
+listed in the above table, you may try to add the specific USB VendorID and
+ProductID identifiers to the sn9c102_id_table[] in the file "sn9c102_sensor.h";
+then compile, load the module again and look at the kernel output.
+If this works, please send an email to me reporting the kernel messages, so
+that I will add a new entry in the list of supported devices.
+
+Donations of new models for further testing and support would be much
+appreciated. I won't add official support for hardware that I don't actually
+have.
+
+
+9. How to add support for new image sensors
+===========================================
+It should be easy to write code for new sensors by using the small API that I
+have created for this purpose, which is present in "sn9c102_sensor.h"
+(documentation is included there). As an example, have a look at the code in
+"sn9c102_pas106b.c", which uses the mentioned interface.
+
+At the moment, not yet supported image sensors are: PAS202B (VGA),
+HV7131[D|E1] (VGA), MI03 (VGA), OV7620 (VGA).
+
+
+10. Note for V4L2 developers
+============================
+This driver follows the V4L2 API specifications. In particular, it enforces two
+rules:
+
+1) Exactly one I/O method, either "mmap" or "read", is associated with each
+file descriptor. Once it is selected, the application must close and reopen the
+device to switch to the other I/O method.
+
+2) 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. In case,
+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 them again before any I/O attempts.
+
+
+11. Contact information
+=======================
+I may be contacted by e-mail at <luca.risolia@studio.unibo.it>.
+
+I can accept GPG/PGP encrypted e-mail. My GPG key ID is 'FCE635A4'.
+My public 1024-bit key should be available at any keyserver; the fingerprint
+is: '88E8 F32F 7244 68BA 3958  5D40 99DA 5D2A FCE6 35A4'.
+
+
+12. Credits
+===========
+I would thank the following persons:
+
+- Stefano Mozzi, who donated 45 EU;
+- Luca Capello for the donation of a webcam;
+- Mizuno Takafumi for the donation of a webcam.
diff -Nru a/Documentation/usb/w9968cf.txt b/Documentation/usb/w9968cf.txt
--- a/Documentation/usb/w9968cf.txt	2004-07-13 13:12:22 -07:00
+++ b/Documentation/usb/w9968cf.txt	2004-07-13 13:12:22 -07:00
@@ -23,6 +23,9 @@
 ============
 Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it>
 
+Winbond is a trademark of Winbond Electronics Corporation.
+This driver is not sponsored or developed by Winbond.
+
 
 2. License
 ==========
@@ -44,8 +47,8 @@
 3. Overview
 ===========
 This driver supports the video streaming capabilities of the devices mounting
-Winbond W9967CF and Winbond W9968CF JPEG USB Dual Mode Camera Chips, when they
-are being commanded by USB. OV681 based cameras should be supported as well.
+Winbond W9967CF and Winbond W9968CF JPEG USB Dual Mode Camera Chips. OV681
+based cameras should be supported as well.
 
 The driver is divided into two modules: the basic one, "w9968cf", is needed for
 the supported devices to work; the second one, "w9968cf-vpp", is an optional
@@ -58,7 +61,8 @@
 performance purposes. However it is always recommended to download and install
 the latest and complete release of the driver, replacing the existing one, if
 present: it will be still even possible not to load the "w9968cf-vpp" module at
-all, if you ever want to.
+all, if you ever want to. Another important missing feature of the version in
+the official Linux 2.4 kernels is the writeable /proc filesystem interface.
 
 The latest and full-featured version of the W996[87]CF driver can be found at:
 http://go.lamarinapunto.com/ . Please refer to the documentation included in
@@ -68,38 +72,37 @@
 disconnected from the host many times without turning off the computer, if
 your system supports the hotplug facility.
 
-To change the default settings for each camera, many paramaters can be passed
+To change the default settings for each camera, many parameters can be passed
 through command line when the module is loaded into memory.
 
-The driver relies on the Video4Linux, USB and I2C core modules of the official
-Linux kernels. It has been designed to run properly on SMP systems as well.
-At the moment, an additional module, "ovcamchip", is mandatory; it provides
-support for some OmniVision CMOS sensors connected to the W996[87]CF chips.
-
-The "ovcamchip" module is part of the OV511 driver, version 2.27, which can be
-downloaded from internet:
-http://alpha.dyndns.org/ov511/
-To know how to compile it, read the documentation included in the OV511 
-package.
+The driver relies on the Video4Linux, USB and I2C core modules. It has been
+designed to run properly on SMP systems as well. An additional module,
+"ovcamchip", is mandatory; it provides support for some OmniVision image
+sensors connected to the W996[87]CF chips; if found in the system, the module
+will be automatically loaded by default (provided that the kernel has been
+compiled with the automatic module loading option).
 
 
 4. Supported devices
 ====================
 At the moment, known W996[87]CF and OV681 based devices are:
-- Aroma Digi Pen ADG-5000 Refurbished
-- AVerTV USB
-- Creative Labs Video Blaster WebCam Go
-- Creative Labs Video Blaster WebCam Go Plus
-- Die Lebon LDC-D35A Digital Kamera
-- Ezonics EZ-802 EZMega Cam
-- OPCOM Digi Pen VGA Dual Mode Pen Camera
+- Aroma Digi Pen VGA Dual Mode ADG-5000 (unknown image sensor)
+- AVerMedia AVerTV USB (SAA7111A, Philips FI1216Mk2 tuner, PT2313L audio chip)
+- Creative Labs Video Blaster WebCam Go (OmniVision OV7610 sensor)
+- Creative Labs Video Blaster WebCam Go Plus (OmniVision OV7620 sensor)
+- Lebon LDC-035A (unknown image sensor)
+- Ezonics EZ-802 EZMega Cam (OmniVision OV8610C sensor)
+- OmniVision OV8610-EDE (OmniVision OV8610 sensor)
+- OPCOM Digi Pen VGA Dual Mode Pen Camera (unknown image sensor)
+- Pretec Digi Pen-II (OmniVision OV7620 sensor)
+- Pretec DigiPen-480 (OmniVision OV8610 sensor)
 
 If you know any other W996[87]CF or OV681 based cameras, please contact me.
 
 The list above does NOT imply that all those devices work with this driver: up
-until now only webcams that have a CMOS sensor supported by the "ovcamchip"
+until now only webcams that have an image sensor supported by the "ovcamchip"
 module work. 
-For a list of supported CMOS sensors, please visit the author's homepage on
+For a list of supported image sensors, please visit the author's homepage on
 this module: http://alpha.dyndns.org/ov511/
 
 Possible external microcontrollers of those webcams are not supported: this
@@ -112,8 +115,10 @@
 
 5. Module dependencies
 ======================
-For it to work properly, the driver needs kernel support for Video4Linux, 
-USB and I2C, and a third-party module for the CMOS sensor.
+For it to work properly, the driver needs kernel support for Video4Linux, USB
+and I2C, and the "ovcamchip" module for the image sensor. Make sure you are not
+actually using any external "ovcamchip" module, given that the W996[87]CF 
+driver depends on the version of the module present in the official kernels.
 
 The following options of the kernel configuration file must be enabled and
 corresponding modules must be compiled:
@@ -128,6 +133,10 @@
 
 The I2C core module can be compiled statically in the kernel as well.
 
+	# OmniVision Camera Chip support
+	#
+	CONFIG_VIDEO_OVCAMCHIP=m
+
 	# USB support
 	#
 	CONFIG_USB=m
@@ -141,19 +150,12 @@
 	CONFIG_USB_UHCI_HCD=m
 	CONFIG_USB_OHCI_HCD=m
 
-Also, make sure "Enforce bandwidth allocation" is NOT enabled.
-
 And finally:
 
 	# USB Multimedia devices
 	#
 	CONFIG_USB_W9968CF=m
 
-The last module we need is "ovcamchip.o". To obtain it, you have to download
-the OV511 package, version 2.27 - don't use other versions - and compile it
-according to its documentation. 
-The package is available at http://alpha.dyndns.org/ov511/ .
-
 
 6. Module loading
 =================
@@ -164,11 +166,10 @@
 
 	[root@localhost home]# modprobe usbcore
 	[root@localhost home]# modprobe i2c-core
-	[root@localhost ov511-x.xx]# insmod ./ovcamchip.ko
 	[root@localhost home]# modprobe w9968cf
 
-At this point the devices should be recognized: "dmesg" can be used to analyze
-kernel messages:
+At this point the pertinent devices should be recognized: "dmesg" can be used
+to analyze kernel messages:
 
 	[user@localhost home]$ dmesg
 
@@ -180,9 +181,22 @@
 	[root@locahost home]# modinfo w9968cf
 
 
-7. Module paramaters
+7. Module parameters
 ====================
-Module paramaters are listed below:
+Module parameters are listed below:
+-------------------------------------------------------------------------------
+Name:            ovmod_load
+Type:            bool
+Syntax:          <0|1>
+Description:     Automatic 'ovcamchip' module loading: 0 disabled, 1 enabled.
+                 If enabled, 'insmod' searches for the required 'ovcamchip'
+                 module in the system, according to its configuration, and
+                 loads that module automatically. This action is performed as
+                 once soon as the 'w9968cf' module is loaded into memory.
+Default:         1
+Note:            The kernel must be compiled with the CONFIG_KMOD option
+                 enabled for the 'ovcamchip' module to be loaded and for
+                 this parameter to be present.
 -------------------------------------------------------------------------------
 Name:           vppmod_load
 Type:           bool
@@ -191,10 +205,14 @@
                 If enabled, every time an application attempts to open a
                 camera, 'insmod' searches for the video post-processing module
                 in the system and loads it automatically (if present).
-                The 'w9968cf-vpp' module adds extra image manipulation 
+                The optional 'w9968cf-vpp' module adds extra image manipulation
                 capabilities to the 'w9968cf' module,like software up-scaling,
-                colour conversions and video decoding.
+                colour conversions and video decompression for very high frame
+                rates.
 Default:        1
+Note:           The kernel must be compiled with the CONFIG_KMOD option
+                enabled for the 'w9968cf-vpp' module to be loaded and for
+                this parameter to be present.
 -------------------------------------------------------------------------------
 Name:           simcams 
 Type:           int 
@@ -237,7 +255,7 @@
 Description:    Hardware double buffering: 0 disabled, 1 enabled.
                 It should be enabled if you want smooth video output: if you
                 obtain out of sync. video, disable it, or try to
-                decrease the 'clockdiv' module paramater value.
+                decrease the 'clockdiv' module parameter value.
 Default:        1 for every device.
 -------------------------------------------------------------------------------
 Name:           clamping
@@ -252,7 +270,7 @@
 Description:    Video filter type.
                 0 none, 1 (1-2-1) 3-tap filter, 2 (2-3-6-3-2) 5-tap filter.
                 The filter is used to reduce noise and aliasing artifacts
-                produced by the CCD or CMOS sensor.
+                produced by the CCD or CMOS image sensor.
 Default:        0 for every device.
 -------------------------------------------------------------------------------
 Name:           largeview
@@ -269,7 +287,7 @@
                 Disable it if you have a slow CPU or you don't have enough
                 memory.
 Default:        0 for every device.
-Note:           If 'w9968cf-vpp' is not loaded, this paramater is set to 0.
+Note:           If 'w9968cf-vpp' is not present, this parameter is set to 0.
 -------------------------------------------------------------------------------
 Name:           decompression
 Type:           int array (min = 0, max = 32)
@@ -284,8 +302,8 @@
                 YUV420P/YUV420 in any resolutions where width and height are
                 multiples of 16.
 Default:        2 for every device.
-Note:           If 'w9968cf-vpp' is not loaded, forcing decompression is not
-                allowed; in this case this paramater is set to 2.
+Note:           If 'w9968cf-vpp' is not present, forcing decompression is not
+                allowed; in this case this parameter is set to 2.
 -------------------------------------------------------------------------------
 Name:           force_palette
 Type:           int array (min = 0, max = 32)
@@ -304,9 +322,9 @@
                  3 = RGB565  16 bpp - Software conversion from UYVY
                  4 = RGB24   24 bpp - Software conversion from UYVY
                  5 = RGB32   32 bpp - Software conversion from UYVY
-                When not 0, this paramater will override 'decompression'.
+                When not 0, this parameter will override 'decompression'.
 Default:        0 for every device. Initial palette is 9 (UYVY).
-Note:           If 'w9968cf-vpp' is not loaded, this paramater is set to 9.
+Note:           If 'w9968cf-vpp' is not present, this parameter is set to 9.
 -------------------------------------------------------------------------------
 Name:           force_rgb
 Type:           bool array (min = 0, max = 32)
@@ -320,14 +338,14 @@
 Name:           autobright
 Type:           bool array (min = 0, max = 32)
 Syntax:         <0|1[,...]>
-Description:    CMOS sensor automatically changes brightness:
+Description:    Image sensor automatically changes brightness:
                 0 = no, 1 = yes
 Default:        0 for every device.
 -------------------------------------------------------------------------------
 Name:           autoexp
 Type:           bool array (min = 0, max = 32)
 Syntax:         <0|1[,...]>
-Description:    CMOS sensor automatically changes exposure:
+Description:    Image sensor automatically changes exposure:
                 0 = no, 1 = yes
 Default:        1 for every device.
 -------------------------------------------------------------------------------
@@ -354,7 +372,7 @@
 Description:    Force pixel clock divisor to a specific value (for experts):
                 n may vary from 0 to 127.
                 -1 for automatic value.
-                See also the 'double_buffer' module paramater.
+                See also the 'double_buffer' module parameter.
 Default:        -1 for every device.
 -------------------------------------------------------------------------------
 Name:           backlight
@@ -374,7 +392,7 @@
 Name:           monochrome
 Type:           bool array (min = 0, max = 32)
 Syntax:         <0|1[,...]> 
-Description:    The CMOS sensor is monochrome:
+Description:    The image sensor is monochrome:
                 0 = no, 1 = yes
 Default:        0 for every device.
 -------------------------------------------------------------------------------
@@ -420,7 +438,7 @@
                 4 = warnings
                 5 = called functions
                 6 = function internals
-                Level 5 and 6 are useful for testing only, when just one
+                Level 5 and 6 are useful for testing only, when only one
                 device is used.
 Default:        2
 -------------------------------------------------------------------------------
@@ -449,7 +467,7 @@
 the source code of other drivers and without the help of several persons; in
 particular:
 
-- the I2C interface to kernel and high-level CMOS sensor control routines have
+- the I2C interface to kernel and high-level image sensor control routines have
   been taken from the OV511 driver by Mark McClelland;
 
 - memory management code has been copied from the bttv driver by Ralph Metzler,
diff -Nru a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c
--- a/drivers/isdn/hisax/st5481_usb.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/isdn/hisax/st5481_usb.c	2004-07-13 13:12:22 -07:00
@@ -143,9 +143,6 @@
 	if (ctrl_msg->dr.bRequest == USB_REQ_CLEAR_FEATURE) {
 	        /* Special case handling for pipe reset */
 		le16_to_cpus(&ctrl_msg->dr.wIndex);
-		usb_endpoint_running(adapter->usb_dev,
-				     ctrl_msg->dr.wIndex & ~USB_DIR_IN, 
-				     (ctrl_msg->dr.wIndex & USB_DIR_IN) == 0);
 
 		/* toggle is reset on clear */
 		usb_settoggle(adapter->usb_dev, 
diff -Nru a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c
--- a/drivers/net/irda/stir4200.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/net/irda/stir4200.c	2004-07-13 13:12:22 -07:00
@@ -168,6 +168,7 @@
 
 struct stir_cb {
         struct usb_device *usbdev;      /* init: probe_irda */
+	struct usb_interface *usbintf;
         struct net_device *netdev;      /* network layer */
         struct irlap_cb   *irlap;       /* The link layer we are binded to */
         struct net_device_stats stats;	/* network statistics */
@@ -508,6 +509,7 @@
 {
 	int i, err;
 	__u8 mode;
+	int rc;
 
 	for (i = 0; i < ARRAY_SIZE(stir_modes); ++i) {
 		if (speed == stir_modes[i].speed)
@@ -521,7 +523,14 @@
 	pr_debug("speed change from %d to %d\n", stir->speed, speed);
 
 	/* sometimes needed to get chip out of stuck state */
+	rc = usb_lock_device_for_reset(stir->usbdev, stir->usbintf);
+	if (rc < 0) {
+		err = rc;
+		goto out;
+	}
 	err = usb_reset_device(stir->usbdev);
+	if (rc)
+		usb_unlock_device(stir->usbdev);
 	if (err)
 		goto out;
 
@@ -1066,6 +1075,7 @@
 	stir = net->priv;
 	stir->netdev = net;
 	stir->usbdev = dev;
+	stir->usbintf = intf;
 
 	ret = usb_reset_configuration(dev);
 	if (ret != 0) {
diff -Nru a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
--- a/drivers/usb/core/devices.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/core/devices.c	2004-07-13 13:12:22 -07:00
@@ -283,9 +283,8 @@
 
 /* TBD:
  * 0. TBDs
- * 1. marking active config and ifaces (code lists all, but should mark
+ * 1. marking active interface altsettings (code lists all, but should mark
  *    which ones are active, if any)
- * 2. add <halted> status to each endpoint line
  */
 
 static char *usb_dump_config_descriptor(char *start, char *end, const struct usb_config_descriptor *desc, int active)
@@ -452,7 +451,7 @@
  * nbytes - the maximum number of bytes to write
  * skip_bytes - the number of bytes to skip before writing anything
  * file_offset - the offset into the devices file on completion
- * The caller must own the usbdev->serialize semaphore.
+ * The caller must have locked the device.
  */
 static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset,
 				struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count)
@@ -556,10 +555,10 @@
 		struct usb_device *childdev = usbdev->children[chix];
 
 		if (childdev) {
-			down(&childdev->serialize);
+			usb_lock_device(childdev);
 			ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, childdev,
 					bus, level + 1, chix, ++cnt);
-			up(&childdev->serialize);
+			usb_unlock_device(childdev);
 			if (ret == -EFAULT)
 				return total_written;
 			total_written += ret;
@@ -591,9 +590,9 @@
 		/* recurse through all children of the root hub */
 		if (!bus->root_hub)
 			continue;
-		down(&bus->root_hub->serialize);
+		usb_lock_device(bus->root_hub);
 		ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0);
-		up(&bus->root_hub->serialize);
+		usb_unlock_device(bus->root_hub);
 		if (ret < 0) {
 			up(&usb_bus_list_lock);
 			return ret;
diff -Nru a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
--- a/drivers/usb/core/devio.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/core/devio.c	2004-07-13 13:12:22 -07:00
@@ -111,7 +111,7 @@
 	int i;
 
 	pos = *ppos;
-	down(&dev->serialize);
+	usb_lock_device(dev);
 	if (!connected(dev)) {
 		ret = -ENODEV;
 		goto err;
@@ -173,7 +173,7 @@
 	}
 
 err:
-	up(&dev->serialize);
+	usb_unlock_device(dev);
 	return ret;
 }
 
@@ -514,7 +514,7 @@
 	struct usb_device *dev = ps->dev;
 	unsigned int ifnum;
 
-	down(&dev->serialize);
+	usb_lock_device(dev);
 	list_del_init(&ps->list);
 
 	if (connected(dev)) {
@@ -523,7 +523,7 @@
 				releaseintf(ps, ifnum);
 		destroy_all_async(ps);
 	}
-	up(&dev->serialize);
+	usb_unlock_device(dev);
 	usb_put_dev(dev);
 	ps->dev = NULL;
 	kfree(ps);
@@ -722,7 +722,7 @@
 
 static int proc_resetdevice(struct dev_state *ps)
 {
-	return __usb_reset_device(ps->dev);
+	return usb_reset_device(ps->dev);
 
 }
 
@@ -1012,9 +1012,9 @@
 			break;
 		if (signal_pending(current))
 			break;
-		up(&dev->serialize);
+		usb_unlock_device(dev);
 		schedule();
-		down(&dev->serialize);
+		usb_lock_device(dev);
 	}
 	remove_wait_queue(&ps->wait, &wait);
 	set_current_state(TASK_RUNNING);
@@ -1137,7 +1137,11 @@
 
 	/* let kernel drivers try to (re)bind to the interface */
 	case USBDEVFS_CONNECT:
+		usb_unlock_device(ps->dev);
+		usb_lock_all_devices();
 		bus_rescan_devices(intf->dev.bus);
+		usb_unlock_all_devices();
+		usb_lock_device(ps->dev);
 		break;
 
 	/* talk directly to the interface's driver */
@@ -1180,9 +1184,9 @@
 
 	if (!(file->f_mode & FMODE_WRITE))
 		return -EPERM;
-	down(&dev->serialize);
+	usb_lock_device(dev);
 	if (!connected(dev)) {
-		up(&dev->serialize);
+		usb_unlock_device(dev);
 		return -ENODEV;
 	}
 
@@ -1282,7 +1286,7 @@
 		ret = proc_ioctl(ps, p);
 		break;
 	}
-	up(&dev->serialize);
+	usb_unlock_device(dev);
 	if (ret >= 0)
 		inode->i_atime = CURRENT_TIME;
 	return ret;
diff -Nru a/drivers/usb/core/file.c b/drivers/usb/core/file.c
--- a/drivers/usb/core/file.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/core/file.c	2004-07-13 13:12:22 -07:00
@@ -79,14 +79,25 @@
 
 int usb_major_init(void)
 {
-	if (register_chrdev(USB_MAJOR, "usb", &usb_fops)) {
+	int error;
+
+	error = register_chrdev(USB_MAJOR, "usb", &usb_fops);
+	if (error) {
 		err("unable to get major %d for usb devices", USB_MAJOR);
-		return -EBUSY;
+		goto out;
+	}
+
+	error = class_register(&usb_class);
+	if (error) {
+		err("class_register failed for usb devices");
+		unregister_chrdev(USB_MAJOR, "usb");
+		goto out;
 	}
 
 	devfs_mk_dir("usb");
-	class_register(&usb_class);
-	return 0;
+
+out:
+	return error;
 }
 
 void usb_major_cleanup(void)
diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
--- a/drivers/usb/core/hcd.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/core/hcd.c	2004-07-13 13:12:22 -07:00
@@ -102,6 +102,9 @@
 /* used when updating hcd data */
 static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
 
+/* wait queue for synchronous unlinks */
+DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue);
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -569,7 +572,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-void usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)
+int usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)
 {
 	unsigned long	flags;
 
@@ -581,6 +584,7 @@
 	urb->hcpriv = NULL;
 	usb_hcd_giveback_urb (hcd, urb, NULL);
 	local_irq_restore (flags);
+	return 0;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -791,9 +795,9 @@
 		return (retval < 0) ? retval : -EMSGSIZE;
 	}
 
-	down (&usb_dev->serialize);
+	usb_lock_device (usb_dev);
 	retval = usb_new_device (usb_dev);
-	up (&usb_dev->serialize);
+	usb_unlock_device (usb_dev);
 	if (retval) {
 		usb_dev->bus->root_hub = NULL;
 		dev_err (parent_dev, "can't register root hub for %s, %d\n",
@@ -1029,7 +1033,6 @@
 static void urb_unlink (struct urb *urb)
 {
 	unsigned long		flags;
-	struct usb_device	*dev;
 
 	/* Release any periodic transfer bandwidth */
 	if (urb->bandwidth)
@@ -1040,9 +1043,8 @@
 
 	spin_lock_irqsave (&hcd_data_lock, flags);
 	list_del_init (&urb->urb_list);
-	dev = urb->dev;
 	spin_unlock_irqrestore (&hcd_data_lock, flags);
-	usb_put_dev (dev);
+	usb_put_dev (urb->dev);
 }
 
 
@@ -1079,23 +1081,28 @@
 	// FIXME:  verify that quiescing hc works right (RH cleans up)
 
 	spin_lock_irqsave (&hcd_data_lock, flags);
-	if (HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_QUIESCING) {
+	if (unlikely (urb->reject))
+		status = -EPERM;
+	else if (HCD_IS_RUNNING (hcd->state) &&
+			hcd->state != USB_STATE_QUIESCING) {
 		usb_get_dev (urb->dev);
 		list_add_tail (&urb->urb_list, &dev->urb_list);
 		status = 0;
-	} else {
-		INIT_LIST_HEAD (&urb->urb_list);
+	} else
 		status = -ESHUTDOWN;
-	}
 	spin_unlock_irqrestore (&hcd_data_lock, flags);
-	if (status)
+	if (status) {
+		INIT_LIST_HEAD (&urb->urb_list);
 		return status;
+	}
 
 	/* increment urb's reference count as part of giving it to the HCD
 	 * (which now controls it).  HCD guarantees that it either returns
 	 * an error or calls giveback(), but not both.
 	 */
 	urb = usb_get_urb (urb);
+	atomic_inc (&urb->use_count);
+
 	if (urb->dev == hcd->self.root_hub) {
 		/* NOTE:  requirement on hub callers (usbfs and the hub
 		 * driver, for now) that URBs' urb->transfer_buffer be
@@ -1132,9 +1139,12 @@
 
 	status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
 done:
-	if (status) {
-		usb_put_urb (urb);
+	if (unlikely (status)) {
 		urb_unlink (urb);
+		atomic_dec (&urb->use_count);
+		if (urb->reject)
+			wake_up (&usb_kill_urb_queue);
+		usb_put_urb (urb);
 	}
 	return status;
 }
@@ -1157,60 +1167,39 @@
  * soon as practical.  we've already set up the urb's return status,
  * but we can't know if the callback completed already.
  */
-static void
+static int
 unlink1 (struct usb_hcd *hcd, struct urb *urb)
 {
+	int		value;
+
 	if (urb == (struct urb *) hcd->rh_timer.data)
-		usb_rh_status_dequeue (hcd, urb);
+		value = usb_rh_status_dequeue (hcd, urb);
 	else {
-		int		value;
 
-		/* failures "should" be harmless */
+		/* The only reason an HCD might fail this call is if
+		 * it has not yet fully queued the urb to begin with.
+		 * Such failures should be harmless. */
 		value = hcd->driver->urb_dequeue (hcd, urb);
-		if (value != 0)
-			dev_dbg (hcd->self.controller,
-				"dequeue %p --> %d\n",
-				urb, value);
 	}
-}
-
-struct completion_splice {		// modified urb context:
-	/* did we complete? */
-	struct completion	done;
-
-	/* original urb data */
-	usb_complete_t		complete;
-	void			*context;
-};
-
-static void unlink_complete (struct urb *urb, struct pt_regs *regs)
-{
-	struct completion_splice	*splice;
-
-	splice = (struct completion_splice *) urb->context;
 
-	/* issue original completion call */
-	urb->complete = splice->complete;
-	urb->context = splice->context;
-	urb->complete (urb, regs);
-
-	/* then let the synchronous unlink call complete */
-	complete (&splice->done);
+	if (value != 0)
+		dev_dbg (hcd->self.controller, "dequeue %p --> %d\n",
+				urb, value);
+	return value;
 }
 
 /*
- * called in any context; note ASYNC_UNLINK restrictions
+ * called in any context
  *
  * caller guarantees urb won't be recycled till both unlink()
  * and the urb's completion function return
  */
-static int hcd_unlink_urb (struct urb *urb)
+static int hcd_unlink_urb (struct urb *urb, int status)
 {
 	struct hcd_dev			*dev;
 	struct usb_hcd			*hcd = NULL;
 	struct device			*sys = NULL;
 	unsigned long			flags;
-	struct completion_splice	splice;
 	struct list_head		*tmp;
 	int				retval;
 
@@ -1262,8 +1251,6 @@
 
 	/* Any status except -EINPROGRESS means something already started to
 	 * unlink this URB from the hardware.  So there's no more work to do.
-	 *
-	 * FIXME use better explicit urb state
 	 */
 	if (urb->status != -EINPROGRESS) {
 		retval = -EBUSY;
@@ -1281,62 +1268,19 @@
 		hcd->saw_irq = 1;
 	}
 
-	/* maybe set up to block until the urb's completion fires.  the
-	 * lower level hcd code is always async, locking on urb->status
-	 * updates; an intercepted completion unblocks us.
-	 */
-	if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
-		if (in_interrupt ()) {
-			dev_dbg (hcd->self.controller, 
-				"non-async unlink in_interrupt");
-			retval = -EWOULDBLOCK;
-			goto done;
-		}
-		/* synchronous unlink: block till we see the completion */
-		init_completion (&splice.done);
-		splice.complete = urb->complete;
-		splice.context = urb->context;
-		urb->complete = unlink_complete;
-		urb->context = &splice;
-		urb->status = -ENOENT;
-	} else {
-		/* asynchronous unlink */
-		urb->status = -ECONNRESET;
-	}
+	urb->status = status;
+
 	spin_unlock (&hcd_data_lock);
 	spin_unlock_irqrestore (&urb->lock, flags);
 
-	// FIXME remove splicing, so this becomes unlink1 (hcd, urb);
-	if (urb == (struct urb *) hcd->rh_timer.data) {
-		usb_rh_status_dequeue (hcd, urb);
-		retval = 0;
-	} else {
-		retval = hcd->driver->urb_dequeue (hcd, urb);
-
-		/* hcds shouldn't really fail these calls, but... */
-		if (retval) {
-			dev_dbg (sys, "dequeue %p --> %d\n", urb, retval);
-			if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
-				spin_lock_irqsave (&urb->lock, flags);
-				urb->complete = splice.complete;
-				urb->context = splice.context;
-				spin_unlock_irqrestore (&urb->lock, flags);
-			}
-			goto bye;
-		}
-	}
-
-    	/* block till giveback, if needed */
-	if (urb->transfer_flags & URB_ASYNC_UNLINK)
-		return -EINPROGRESS;
-
-	wait_for_completion (&splice.done);
-	return 0;
+	retval = unlink1 (hcd, urb);
+	if (retval == 0)
+		retval = -EINPROGRESS;
+	return retval;
 
 done:
 	spin_unlock (&hcd_data_lock);
 	spin_unlock_irqrestore (&urb->lock, flags);
-bye:
 	if (retval != -EIDRM && sys && sys->driver)
 		dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval);
 	return retval;
@@ -1367,13 +1311,10 @@
 
 rescan:
 	/* (re)block new requests, as best we can */
-	if (endpoint & USB_DIR_IN) {
-		usb_endpoint_halt (udev, epnum, 0);
+	if (endpoint & USB_DIR_IN)
 		udev->epmaxpacketin [epnum] = 0;
-	} else {
-		usb_endpoint_halt (udev, epnum, 1);
+	else
 		udev->epmaxpacketout [epnum] = 0;
-	}
 
 	/* then kill any current requests */
 	spin_lock (&hcd_data_lock);
@@ -1536,6 +1477,9 @@
 
 	/* pass ownership to the completion handler */
 	urb->complete (urb, regs);
+	atomic_dec (&urb->use_count);
+	if (unlikely (urb->reject))
+		wake_up (&usb_kill_urb_queue);
 	usb_put_urb (urb);
 }
 EXPORT_SYMBOL (usb_hcd_giveback_urb);
@@ -1579,13 +1523,13 @@
 	unsigned		i;
 
 	/* hc's root hub is removed later removed in hcd->stop() */
-	down (&hub->serialize);
 	usb_set_device_state(hub, USB_STATE_NOTATTACHED);
+	usb_lock_device (hub);
 	for (i = 0; i < hub->maxchild; i++) {
 		if (hub->children [i])
 			usb_disconnect (&hub->children [i]);
 	}
-	up (&hub->serialize);
+	usb_unlock_device (hub);
 }
 
 /**
diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
--- a/drivers/usb/core/hcd.h	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/core/hcd.h	2004-07-13 13:12:22 -07:00
@@ -142,7 +142,7 @@
 	int (*deallocate)(struct usb_device *);
 	int (*get_frame_number) (struct usb_device *usb_dev);
 	int (*submit_urb) (struct urb *urb, int mem_flags);
-	int (*unlink_urb) (struct urb *urb);
+	int (*unlink_urb) (struct urb *urb, int status);
 
 	/* allocate dma-consistent buffer for URB_DMA_NOMAPPING */
 	void *(*buffer_alloc)(struct usb_bus *bus, size_t size,
@@ -207,7 +207,7 @@
 
 extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
 extern void usb_bus_init (struct usb_bus *bus);
-extern void usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb);
+extern int usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb);
 
 #ifdef CONFIG_PCI
 struct pci_dev;
@@ -359,14 +359,13 @@
 
 extern struct list_head usb_bus_list;
 extern struct semaphore usb_bus_list_lock;
+extern wait_queue_head_t usb_kill_urb_queue;
 
 extern struct usb_bus *usb_bus_get (struct usb_bus *bus);
 extern void usb_bus_put (struct usb_bus *bus);
 
 extern int usb_find_interface_driver (struct usb_device *dev,
 	struct usb_interface *interface);
-
-#define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep)))
 
 #define usb_endpoint_out(ep_dir)	(!((ep_dir) & USB_DIR_IN))
 
diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
--- a/drivers/usb/core/hub.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/core/hub.c	2004-07-13 13:12:22 -07:00
@@ -53,6 +53,8 @@
 module_param (blinkenlights, bool, S_IRUGO);
 MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs");
 
+static int hub_port_disable(struct usb_device *hdev, int port);
+
 
 #ifdef	DEBUG
 static inline char *portspeed (int portstatus)
@@ -138,7 +140,7 @@
 static void led_work (void *__hub)
 {
 	struct usb_hub		*hub = __hub;
-	struct usb_device	*hdev = interface_to_usbdev (hub->intf);
+	struct usb_device	*hdev = hub->hdev;
 	unsigned		i;
 	unsigned		changed = 0;
 	int			cursor = -1;
@@ -234,18 +236,11 @@
 	int i;
 	unsigned long bits;
 
-	spin_lock(&hub_event_lock);
-	hub->urb_active = 0;
-	if (hub->urb_complete) {	/* disconnect or rmmod */
-		complete(hub->urb_complete);
-		goto done;
-	}
-
 	switch (urb->status) {
 	case -ENOENT:		/* synchronous unlink */
 	case -ECONNRESET:	/* async unlink */
 	case -ESHUTDOWN:	/* hardware going away */
-		goto done;
+		return;
 
 	default:		/* presumably an error */
 		/* Cause a hub reset after 10 consecutive errors */
@@ -268,20 +263,17 @@
 	hub->nerrors = 0;
 
 	/* Something happened, let khubd figure it out */
+	spin_lock(&hub_event_lock);
 	if (list_empty(&hub->event_list)) {
 		list_add_tail(&hub->event_list, &hub_event_list);
 		wake_up(&khubd_wait);
 	}
+	spin_unlock(&hub_event_lock);
 
 resubmit:
 	if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
-			/* ENODEV means we raced disconnect() */
-			&& status != -ENODEV)
+			&& status != -ENODEV && status != -EPERM)
 		dev_err (&hub->intf->dev, "resubmit --> %d\n", status);
-	if (status == 0)
-		hub->urb_active = 1;
-done:
-	spin_unlock(&hub_event_lock);
 }
 
 /* USB 2.0 spec Section 11.24.2.3 */
@@ -308,7 +300,7 @@
 	while (!list_empty (&hub->tt.clear_list)) {
 		struct list_head	*temp;
 		struct usb_tt_clear	*clear;
-		struct usb_device	*hdev;
+		struct usb_device	*hdev = hub->hdev;
 		int			status;
 
 		temp = hub->tt.clear_list.next;
@@ -317,7 +309,6 @@
 
 		/* drop lock so HCD can concurrently report other TT errors */
 		spin_unlock_irqrestore (&hub->tt.lock, flags);
-		hdev = interface_to_usbdev (hub->intf);
 		status = hub_clear_tt_buffer (hdev, clear->devinfo, clear->tt);
 		spin_lock_irqsave (&hub->tt.lock, flags);
 
@@ -378,15 +369,14 @@
 
 static void hub_power_on(struct usb_hub *hub)
 {
-	struct usb_device *hdev;
 	int i;
 
 	/* if hub supports power switching, enable power on each port */
 	if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) < 2) {
 		dev_dbg(&hub->intf->dev, "enabling power on all ports\n");
-		hdev = interface_to_usbdev(hub->intf);
 		for (i = 0; i < hub->descriptor->bNbrPorts; i++)
-			set_port_feature(hdev, i + 1, USB_PORT_FEAT_POWER);
+			set_port_feature(hub->hdev, i + 1,
+					USB_PORT_FEAT_POWER);
 	}
 
 	/* Wait for power to be enabled */
@@ -396,10 +386,9 @@
 static int hub_hub_status(struct usb_hub *hub,
 		u16 *status, u16 *change)
 {
-	struct usb_device *hdev = interface_to_usbdev (hub->intf);
 	int ret;
 
-	ret = get_hub_status(hdev, &hub->status->hub);
+	ret = get_hub_status(hub->hdev, &hub->status->hub);
 	if (ret < 0)
 		dev_err (&hub->intf->dev,
 			"%s failed (err = %d)\n", __FUNCTION__, ret);
@@ -414,7 +403,7 @@
 static int hub_configure(struct usb_hub *hub,
 	struct usb_endpoint_descriptor *endpoint)
 {
-	struct usb_device *hdev = interface_to_usbdev (hub->intf);
+	struct usb_device *hdev = hub->hdev;
 	struct device *hub_dev = &hub->intf->dev;
 	u16 hubstatus, hubchange;
 	unsigned int pipe;
@@ -612,7 +601,6 @@
 		message = "couldn't submit status urb";
 		goto fail;
 	}
-	hub->urb_active = 1;
 
 	/* Wake up khubd */
 	wake_up(&khubd_wait);
@@ -635,26 +623,48 @@
 	return ret;
 }
 
+static void hub_remove_children_work(void *__hub)
+{
+	struct usb_hub		*hub = __hub;
+	struct usb_device	*hdev = hub->hdev;
+	int			i;
+
+	kfree(hub);
+
+	usb_lock_device(hdev);
+	for (i = 0; i < hdev->maxchild; ++i) {
+		if (hdev->children[i])
+			usb_disconnect(&hdev->children[i]);
+	}
+	usb_unlock_device(hdev);
+	usb_put_dev(hdev);
+}
+
 static unsigned highspeed_hubs;
 
 static void hub_disconnect(struct usb_interface *intf)
 {
 	struct usb_hub *hub = usb_get_intfdata (intf);
-	DECLARE_COMPLETION(urb_complete);
+	struct usb_device *hdev;
+	int i, n;
 
 	if (!hub)
 		return;
+	hdev = hub->hdev;
 
-	if (interface_to_usbdev(intf)->speed == USB_SPEED_HIGH)
+	if (hdev->speed == USB_SPEED_HIGH)
 		highspeed_hubs--;
 
 	usb_set_intfdata (intf, NULL);
-	spin_lock_irq(&hub_event_lock);
-	hub->urb_complete = &urb_complete;
 
-	/* Delete it and then reset it */
-	list_del_init(&hub->event_list);
+	if (hub->urb) {
+		usb_kill_urb(hub->urb);
+		usb_free_urb(hub->urb);
+		hub->urb = NULL;
+	}
 
+	spin_lock_irq(&hub_event_lock);
+	list_del_init(&hub->event_list);
 	spin_unlock_irq(&hub_event_lock);
 
 	/* assuming we used keventd, it must quiesce too */
@@ -663,14 +673,6 @@
 	if (hub->has_indicators || hub->tt.hub)
 		flush_scheduled_work ();
 
-	if (hub->urb) {
-		usb_unlink_urb(hub->urb);
-		if (hub->urb_active)
-			wait_for_completion(&urb_complete);
-		usb_free_urb(hub->urb);
-		hub->urb = NULL;
-	}
-
 	if (hub->descriptor) {
 		kfree(hub->descriptor);
 		hub->descriptor = NULL;
@@ -682,14 +684,32 @@
 	}
 
 	if (hub->buffer) {
-		usb_buffer_free(interface_to_usbdev(intf),
-				sizeof(*hub->buffer), hub->buffer,
+		usb_buffer_free(hdev, sizeof(*hub->buffer), hub->buffer,
 				hub->buffer_dma);
 		hub->buffer = NULL;
 	}
 
-	/* Free the memory */
-	kfree(hub);
+	/* If there are any children then this is unbind only, not a
+	 * physical disconnection.  The active ports must be disabled
+	 * and later on we must call usb_disconnect().  We can't call
+	 * it here because we already own the usb bus writelock.
+	 */
+	n = 0;
+	for (i = 0; i < hdev->maxchild; ++i) {
+		if (hdev->children[i]) {
+			++n;
+			hub_port_disable(hdev, i);
+		}
+	}
+
+	if (n == 0)
+		kfree(hub);
+	else {
+		/* Reuse hub->leds to disconnect the children */
+		INIT_WORK(&hub->leds, hub_remove_children_work, hub);
+		schedule_work(&hub->leds);
+		usb_get_dev(hdev);
+	}
 }
 
 static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -741,6 +761,7 @@
 
 	INIT_LIST_HEAD(&hub->event_list);
 	hub->intf = intf;
+	hub->hdev = hdev;
 	INIT_WORK(&hub->leds, led_work, hub);
 
 	usb_set_intfdata (intf, hub);
@@ -792,7 +813,7 @@
 
 static int hub_reset(struct usb_hub *hub)
 {
-	struct usb_device *hdev = interface_to_usbdev(hub->intf);
+	struct usb_device *hdev = hub->hdev;
 	int i;
 
 	/* Disconnect any attached devices */
@@ -803,7 +824,7 @@
 
 	/* Attempt to reset the hub */
 	if (hub->urb)
-		usb_unlink_urb(hub->urb);
+		usb_kill_urb(hub->urb);
 	else
 		return -1;
 
@@ -855,7 +876,7 @@
  * @udev: pointer to device whose state should be changed
  * @new_state: new state value to be stored
  *
- * udev->state is _not_ protected by the udev->serialize semaphore.  This
+ * udev->state is _not_ protected by the device lock.  This
  * is so that devices can be marked as disconnected as soon as possible,
  * without having to wait for the semaphore to be released.  Instead,
  * changes to the state must be protected by the device_state_lock spinlock.
@@ -946,7 +967,7 @@
 	/* lock the bus list on behalf of HCDs unregistering their root hubs */
 	if (!udev->parent)
 		down(&usb_bus_list_lock);
-	down(&udev->serialize);
+	usb_lock_device(udev);
 
 	dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum);
 
@@ -975,7 +996,7 @@
 	*pdev = NULL;
 	spin_unlock_irq(&device_state_lock);
 
-	up(&udev->serialize);
+	usb_unlock_device(udev);
 	if (!udev->parent)
 		up(&usb_bus_list_lock);
 
@@ -1467,8 +1488,6 @@
 				!= udev->descriptor.bMaxPacketSize0)) {
 		usb_disable_endpoint(udev, 0 + USB_DIR_IN);
 		usb_disable_endpoint(udev, 0 + USB_DIR_OUT);
-		usb_endpoint_running(udev, 0, 1);
-		usb_endpoint_running(udev, 0, 0);
 		udev->epmaxpacketin [0] = udev->descriptor.bMaxPacketSize0;
 		udev->epmaxpacketout[0] = udev->descriptor.bMaxPacketSize0;
 	}
@@ -1514,8 +1533,9 @@
 }
 
 static unsigned
-hub_power_remaining (struct usb_hub *hub, struct usb_device *hdev)
+hub_power_remaining (struct usb_hub *hub)
 {
+	struct usb_device *hdev = hub->hdev;
 	int remaining;
 	unsigned i;
 
@@ -1556,7 +1576,7 @@
 static void hub_port_connect_change(struct usb_hub *hub, int port,
 					u16 portstatus, u16 portchange)
 {
-	struct usb_device *hdev = interface_to_usbdev(hub->intf);
+	struct usb_device *hdev = hub->hdev;
 	struct device *hub_dev = &hub->intf->dev;
 	int status, i;
  
@@ -1673,7 +1693,7 @@
 		 * udev becomes globally accessible, although presumably
 		 * no one will look at it until hdev is unlocked.
 		 */
-		down (&udev->serialize);
+		usb_lock_device (udev);
 		status = 0;
 
 		/* We mustn't add new devices if the parent hub has
@@ -1697,11 +1717,11 @@
 			}
 		}
 
-		up (&udev->serialize);
+		usb_unlock_device (udev);
 		if (status)
 			goto loop;
 
-		status = hub_power_remaining(hub, hdev);
+		status = hub_power_remaining(hub);
 		if (status)
 			dev_dbg(hub_dev,
 				"%dmA power budget left\n",
@@ -1755,7 +1775,7 @@
 		list_del_init(tmp);
 
 		hub = list_entry(tmp, struct usb_hub, event_list);
-		hdev = interface_to_usbdev(hub->intf);
+		hdev = hub->hdev;
 		hub_dev = &hub->intf->dev;
 
 		usb_get_dev(hdev);
@@ -1763,7 +1783,7 @@
 
 		/* Lock the device, then check to see if we were
 		 * disconnected while waiting for the lock to succeed. */
-		down(&hdev->serialize);
+		usb_lock_device(hdev);
 		if (hdev->state != USB_STATE_CONFIGURED ||
 				!hdev->actconfig ||
 				hub != usb_get_intfdata(
@@ -1879,7 +1899,7 @@
 		}
 
 loop:
-		up(&hdev->serialize);
+		usb_unlock_device(hdev);
 		usb_put_dev(hdev);
 
         } /* end while (1) */
@@ -2030,8 +2050,10 @@
  *
  * The caller must own the device lock.  For example, it's safe to use
  * this from a driver probe() routine after downloading new firmware.
+ * For calls that might not occur during probe(), drivers should lock
+ * the device using usb_lock_device_for_reset().
  */
-int __usb_reset_device(struct usb_device *udev)
+int usb_reset_device(struct usb_device *udev)
 {
 	struct usb_device *parent = udev->parent;
 	struct usb_device_descriptor descriptor = udev->descriptor;
@@ -2067,6 +2089,11 @@
 		return -ENOENT;
 	}
 
+	/* ep0 maxpacket size may change; let the HCD know about it.
+	 * Other endpoints will be handled by re-enumeration. */
+	usb_disable_endpoint(udev, 0);
+	usb_disable_endpoint(udev, 0 + USB_DIR_IN);
+
 	ret = hub_port_init(parent, udev, port);
 	if (ret < 0)
 		goto re_enumerate;
@@ -2098,7 +2125,7 @@
 		struct usb_interface *intf = udev->actconfig->interface[i];
 		struct usb_interface_descriptor *desc;
 
-		/* set_interface resets host side toggle and halt status even
+		/* set_interface resets host side toggle even
 		 * for altsetting zero.  the interface may have no driver.
 		 */
 		desc = &intf->cur_altsetting->desc;
@@ -2130,16 +2157,4 @@
 	spin_unlock_irq(&hub_event_lock);
 
 	return -ENODEV;
-}
-EXPORT_SYMBOL(__usb_reset_device);
-
-int usb_reset_device(struct usb_device *udev)
-{
-	int r;
-	
-	down(&udev->serialize);
-	r = __usb_reset_device(udev);
-	up(&udev->serialize);
-
-	return r;
 }
diff -Nru a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
--- a/drivers/usb/core/hub.h	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/core/hub.h	2004-07-13 13:12:22 -07:00
@@ -187,9 +187,8 @@
 
 struct usb_hub {
 	struct usb_interface	*intf;		/* the "real" device */
+	struct usb_device	*hdev;
 	struct urb		*urb;		/* for interrupt polling pipe */
-	struct completion	*urb_complete;	/* wait for urb to end */
-	unsigned int		urb_active:1;
 
 	/* buffer for urb ... 1 bit each for hub and children, rounded up */
 	char			(*buffer)[(USB_MAXCHILDREN + 1 + 7) / 8];
diff -Nru a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
--- a/drivers/usb/core/inode.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/core/inode.c	2004-07-13 13:12:22 -07:00
@@ -48,6 +48,7 @@
 static struct vfsmount *usbfs_mount;
 static int usbdevfs_mount_count;	/* = 0 */
 static int usbfs_mount_count;	/* = 0 */
+static int ignore_mount = 0;
 
 static struct dentry *devices_usbdevfs_dentry;
 static struct dentry *devices_usbfs_dentry;
@@ -88,6 +89,17 @@
 	char *p;
 	int option;
 
+	/* (re)set to defaults. */
+	devuid = 0;
+	busuid = 0;
+	listuid = 0;
+	devgid = 0;
+	busgid = 0;
+	listgid = 0;
+	devmode = S_IWUSR | S_IRUGO;
+	busmode = S_IXUGO | S_IRUGO;
+	listmode = S_IRUGO;
+
 	while ((p = strsep(&data, ",")) != NULL) {
 		substring_t args[MAX_OPT_ARGS];
 		int token;
@@ -151,6 +163,89 @@
 	return 0;
 }
 
+static void update_special(struct dentry *special)
+{
+	special->d_inode->i_uid = listuid;
+	special->d_inode->i_gid = listgid;
+	special->d_inode->i_mode = S_IFREG | listmode;
+}
+
+static void update_dev(struct dentry *dev)
+{
+	dev->d_inode->i_uid = devuid;
+	dev->d_inode->i_gid = devgid;
+	dev->d_inode->i_mode = S_IFREG | devmode;
+}
+
+static void update_bus(struct dentry *bus)
+{
+	struct dentry *dev = NULL;
+
+	bus->d_inode->i_uid = busuid;
+	bus->d_inode->i_gid = busgid;
+	bus->d_inode->i_mode = S_IFDIR | busmode;
+
+	down(&bus->d_inode->i_sem);
+
+	list_for_each_entry(dev, &bus->d_subdirs, d_child)
+		if (dev->d_inode)
+			update_dev(dev);
+
+	up(&bus->d_inode->i_sem);
+}
+
+static void update_sb(struct super_block *sb)
+{
+	struct dentry *root = sb->s_root;
+	struct dentry *bus = NULL;
+
+	if (!root)
+		return;
+
+	down(&root->d_inode->i_sem);
+
+	list_for_each_entry(bus, &root->d_subdirs, d_child) {
+		if (bus->d_inode) {
+			switch (S_IFMT & bus->d_inode->i_mode) {
+			case S_IFDIR:
+				update_bus(bus);
+				break;
+			case S_IFREG:
+				update_special(bus);
+				break;
+			default:
+				warn("Unknown node %s mode %x found on remount!\n",bus->d_name.name,bus->d_inode->i_mode);
+				break;
+			}
+		}
+	}
+
+	up(&root->d_inode->i_sem);
+}
+
+static int remount(struct super_block *sb, int *flags, char *data)
+{
+	/* If this is not a real mount,
+	 * i.e. it's a simple_pin_fs from create_special_files,
+	 * then ignore it.
+	 */
+	if (ignore_mount)
+		return 0;
+
+	if (parse_options(sb, data)) {
+		warn("usbfs: mount parameter error:");
+		return -EINVAL;
+	}
+
+	if (usbfs_mount && usbfs_mount->mnt_sb)
+		update_sb(usbfs_mount->mnt_sb);
+
+	if (usbdevfs_mount && usbdevfs_mount->mnt_sb)
+		update_sb(usbdevfs_mount->mnt_sb);
+
+	return 0;
+}
+
 static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t dev)
 {
 	struct inode *inode = new_inode(sb);
@@ -349,6 +444,7 @@
 static struct super_operations usbfs_ops = {
 	.statfs =	simple_statfs,
 	.drop_inode =	generic_delete_inode,
+	.remount_fs =	remount,
 };
 
 static int usbfs_fill_super(struct super_block *sb, void *data, int silent)
@@ -356,11 +452,6 @@
 	struct inode *inode;
 	struct dentry *root;
 
-	if (parse_options(sb, data)) {
-		warn("usbfs: mount parameter error:");
-		return -EINVAL;
-	}
-
 	sb->s_blocksize = PAGE_CACHE_SIZE;
 	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
 	sb->s_magic = USBDEVICE_SUPER_MAGIC;
@@ -523,6 +614,11 @@
 	struct dentry *parent;
 	int retval;
 
+	/* the simple_pin_fs calls will call remount with no options
+	 * without this flag that would overwrite the real mount options (if any)
+	 */
+	ignore_mount = 1;
+
 	/* create the devices special file */
 	retval = simple_pin_fs("usbdevfs", &usbdevfs_mount, &usbdevfs_mount_count);
 	if (retval) {
@@ -535,6 +631,8 @@
 		err ("Unable to get usbfs mount");
 		goto error_clean_usbdevfs_mount;
 	}
+
+	ignore_mount = 0;
 
 	parent = usbfs_mount->mnt_sb->s_root;
 	devices_usbfs_dentry = fs_create_file ("devices",
diff -Nru a/drivers/usb/core/message.c b/drivers/usb/core/message.c
--- a/drivers/usb/core/message.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/core/message.c	2004-07-13 13:12:22 -07:00
@@ -605,12 +605,128 @@
  * Returns the number of bytes received on success, or else the status code
  * returned by the underlying usb_control_msg() call.
  */
-int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
+int usb_get_string(struct usb_device *dev, unsigned short langid,
+		unsigned char index, void *buf, int size)
 {
-	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-		USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
-		(USB_DT_STRING << 8) + index, langid, buf, size,
-		HZ * USB_CTRL_GET_TIMEOUT);
+	int i;
+	int result;
+
+	for (i = 0; i < 3; ++i) {
+		/* retry on length 0 or stall; some devices are flakey */
+		result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+			(USB_DT_STRING << 8) + index, langid, buf, size,
+			HZ * USB_CTRL_GET_TIMEOUT);
+		if (!(result == 0 || result == -EPIPE))
+			break;
+	}
+	return result;
+}
+
+static int usb_string_sub(struct usb_device *dev, unsigned int langid,
+		unsigned int index, unsigned char *buf)
+{
+	int rc;
+
+	/* Try to read the string descriptor by asking for the maximum
+	 * possible number of bytes */
+	rc = usb_get_string(dev, langid, index, buf, 255);
+
+	/* If that failed try to read the descriptor length, then
+	 * ask for just that many bytes */
+	if (rc < 0) {
+		rc = usb_get_string(dev, langid, index, buf, 2);
+		if (rc == 2)
+			rc = usb_get_string(dev, langid, index, buf, buf[0]);
+	}
+
+	if (rc >= 0) {
+		/* There might be extra junk at the end of the descriptor */
+		if (buf[0] < rc)
+			rc = buf[0];
+		if (rc < 2)
+			rc = -EINVAL;
+	}
+	return rc;
+}
+
+/**
+ * usb_string - returns ISO 8859-1 version of a string descriptor
+ * @dev: the device whose string descriptor is being retrieved
+ * @index: the number of the descriptor
+ * @buf: where to put the string
+ * @size: how big is "buf"?
+ * Context: !in_interrupt ()
+ * 
+ * This converts the UTF-16LE encoded strings returned by devices, from
+ * usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones
+ * that are more usable in most kernel contexts.  Note that all characters
+ * in the chosen descriptor that can't be encoded using ISO-8859-1
+ * are converted to the question mark ("?") character, and this function
+ * chooses strings in the first language supported by the device.
+ *
+ * The ASCII (or, redundantly, "US-ASCII") character set is the seven-bit
+ * subset of ISO 8859-1. ISO-8859-1 is the eight-bit subset of Unicode,
+ * and is appropriate for use many uses of English and several other
+ * Western European languages.  (But it doesn't include the "Euro" symbol.)
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns length of the string (>= 0) or usb_control_msg status (< 0).
+ */
+int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
+{
+	unsigned char *tbuf;
+	int err;
+	unsigned int u, idx;
+
+	if (size <= 0 || !buf || !index)
+		return -EINVAL;
+	buf[0] = 0;
+	tbuf = kmalloc(256, GFP_KERNEL);
+	if (!tbuf)
+		return -ENOMEM;
+
+	/* get langid for strings if it's not yet known */
+	if (!dev->have_langid) {
+		err = usb_string_sub(dev, 0, 0, tbuf);
+		if (err < 0) {
+			dev_err (&dev->dev,
+				"string descriptor 0 read error: %d\n",
+				err);
+			goto errout;
+		} else if (err < 4) {
+			dev_err (&dev->dev, "string descriptor 0 too short\n");
+			err = -EINVAL;
+			goto errout;
+		} else {
+			dev->have_langid = -1;
+			dev->string_langid = tbuf[2] | (tbuf[3]<< 8);
+				/* always use the first langid listed */
+			dev_dbg (&dev->dev, "default language 0x%04x\n",
+				dev->string_langid);
+		}
+	}
+	
+	err = usb_string_sub(dev, dev->string_langid, index, tbuf);
+	if (err < 0)
+		goto errout;
+
+	size--;		/* leave room for trailing NULL char in output buffer */
+	for (idx = 0, u = 2; u < err; u += 2) {
+		if (idx >= size)
+			break;
+		if (tbuf[u+1])			/* high byte */
+			buf[idx++] = '?';  /* non ISO-8859-1 character */
+		else
+			buf[idx++] = tbuf[u];
+	}
+	buf[idx] = 0;
+	err = idx;
+
+ errout:
+	kfree(tbuf);
+	return err;
 }
 
 /**
@@ -738,9 +854,8 @@
 	 * the copy in usb-storage, for as long as we need two copies.
 	 */
 
-	/* toggle was reset by the clear, then ep was reactivated */
+	/* toggle was reset by the clear */
 	usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
-	usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
 
 	return 0;
 }
@@ -754,9 +869,8 @@
  * Deallocates hcd/hardware state for this endpoint ... and nukes all
  * pending urbs.
  *
- * If the HCD hasn't registered a disable() function, this marks the
- * endpoint as halted and sets its maxpacket size to 0 to prevent
- * further submissions.
+ * If the HCD hasn't registered a disable() function, this sets the
+ * endpoint's maxpacket size to 0 to prevent further submissions.
  */
 void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
 {
@@ -765,13 +879,10 @@
 	else {
 		unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
 
-		if (usb_endpoint_out(epaddr)) {
-			usb_endpoint_halt(dev, epnum, 1);
+		if (usb_endpoint_out(epaddr))
 			dev->epmaxpacketout[epnum] = 0;
-		} else {
-			usb_endpoint_halt(dev, epnum, 0);
+		else
 			dev->epmaxpacketin[epnum] = 0;
-		}
 	}
 }
 
@@ -814,7 +925,6 @@
 		usb_disable_endpoint(dev, i + USB_DIR_IN);
 	}
 	dev->toggle[0] = dev->toggle[1] = 0;
-	dev->halted[0] = dev->halted[1] = 0;
 
 	/* getting rid of interfaces will disconnect
 	 * any drivers bound to them (a key side effect)
@@ -850,9 +960,8 @@
  * @dev: the device whose interface is being enabled
  * @epd: pointer to the endpoint descriptor
  *
- * Marks the endpoint as running, resets its toggle, and stores
- * its maxpacket value.  For control endpoints, both the input
- * and output sides are handled.
+ * Resets the endpoint toggle and stores its maxpacket value.
+ * For control endpoints, both the input and output sides are handled.
  */
 void usb_enable_endpoint(struct usb_device *dev,
 		struct usb_endpoint_descriptor *epd)
@@ -864,12 +973,10 @@
 				USB_ENDPOINT_XFER_CONTROL);
 
 	if (usb_endpoint_out(epaddr) || is_control) {
-		usb_endpoint_running(dev, epnum, 1);
 		usb_settoggle(dev, epnum, 1, 0);
 		dev->epmaxpacketout[epnum] = maxsize;
 	}
 	if (!usb_endpoint_out(epaddr) || is_control) {
-		usb_endpoint_running(dev, epnum, 0);
 		usb_settoggle(dev, epnum, 0, 0);
 		dev->epmaxpacketin[epnum] = maxsize;
 	}
@@ -932,6 +1039,9 @@
 	int ret;
 	int manual = 0;
 
+	if (dev->state == USB_STATE_SUSPENDED)
+		return -EHOSTUNREACH;
+
 	iface = usb_ifnum_to_if(dev, interface);
 	if (!iface) {
 		dev_dbg(&dev->dev, "selecting invalid interface %d\n",
@@ -1022,6 +1132,8 @@
  * use usb_set_interface() on the interfaces it claims.  Resetting the whole
  * configuration would affect other drivers' interfaces.
  *
+ * The caller must have locked the device.
+ *
  * Returns zero on success, else a negative error code.
  */
 int usb_reset_configuration(struct usb_device *dev)
@@ -1029,8 +1141,11 @@
 	int			i, retval;
 	struct usb_host_config	*config;
 
-	/* caller must own dev->serialize (config won't change)
-	 * and the usb bus readlock (so driver bindings are stable);
+	if (dev->state == USB_STATE_SUSPENDED)
+		return -EHOSTUNREACH;
+
+	/* caller must have locked the device and must own
+	 * the usb bus readlock (so driver bindings are stable);
 	 * so calls during probe() are fine
 	 */
 
@@ -1050,7 +1165,6 @@
 	}
 
 	dev->toggle[0] = dev->toggle[1] = 0;
-	dev->halted[0] = dev->halted[1] = 0;
 
 	/* re-init hc/hcd interface/endpoint state */
 	for (i = 0; i < config->desc.bNumInterfaces; i++) {
@@ -1087,7 +1201,7 @@
  * usb_set_configuration - Makes a particular device setting be current
  * @dev: the device whose configuration is being updated
  * @configuration: the configuration being chosen.
- * Context: !in_interrupt(), caller holds dev->serialize
+ * Context: !in_interrupt(), caller has locked the device
  *
  * This is used to enable non-default device modes.  Not all devices
  * use this kind of configurability; many devices only have one
@@ -1108,8 +1222,8 @@
  * usb_set_interface().
  *
  * This call is synchronous. The calling context must be able to sleep,
- * and must not hold the driver model lock for USB; usb device driver
- * probe() methods may not use this routine.
+ * must have locked the device, and must not hold the driver model lock
+ * for USB; usb device driver probe() methods cannot use this routine.
  *
  * Returns zero on success, or else the status code returned by the
  * underlying call that failed.  On succesful completion, each interface
@@ -1124,8 +1238,6 @@
 	struct usb_interface **new_interfaces = NULL;
 	int n, nintf;
 
-	/* dev->serialize guards all config changes */
-
 	for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
 		if (dev->config[i].desc.bConfigurationValue == configuration) {
 			cp = &dev->config[i];
@@ -1142,6 +1254,9 @@
 	if (cp && configuration == 0)
 		dev_warn(&dev->dev, "config 0 descriptor??\n");
 
+	if (dev->state == USB_STATE_SUSPENDED)
+		return -EHOSTUNREACH;
+
 	/* Allocate memory for new interfaces before doing anything else,
 	 * so that if we run out then nothing will have changed. */
 	n = nintf = 0;
@@ -1255,102 +1370,6 @@
 	}
 
 	return ret;
-}
-
-/**
- * usb_string - returns ISO 8859-1 version of a string descriptor
- * @dev: the device whose string descriptor is being retrieved
- * @index: the number of the descriptor
- * @buf: where to put the string
- * @size: how big is "buf"?
- * Context: !in_interrupt ()
- * 
- * This converts the UTF-16LE encoded strings returned by devices, from
- * usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones
- * that are more usable in most kernel contexts.  Note that all characters
- * in the chosen descriptor that can't be encoded using ISO-8859-1
- * are converted to the question mark ("?") character, and this function
- * chooses strings in the first language supported by the device.
- *
- * The ASCII (or, redundantly, "US-ASCII") character set is the seven-bit
- * subset of ISO 8859-1. ISO-8859-1 is the eight-bit subset of Unicode,
- * and is appropriate for use many uses of English and several other
- * Western European languages.  (But it doesn't include the "Euro" symbol.)
- *
- * This call is synchronous, and may not be used in an interrupt context.
- *
- * Returns length of the string (>= 0) or usb_control_msg status (< 0).
- */
-int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
-{
-	unsigned char *tbuf;
-	int err, len;
-	unsigned int u, idx;
-
-	if (size <= 0 || !buf || !index)
-		return -EINVAL;
-	buf[0] = 0;
-	tbuf = kmalloc(256, GFP_KERNEL);
-	if (!tbuf)
-		return -ENOMEM;
-
-	/* get langid for strings if it's not yet known */
-	if (!dev->have_langid) {
-		err = usb_get_descriptor(dev, USB_DT_STRING, 0, tbuf, 4);
-		if (err < 0) {
-			dev_err (&dev->dev,
-				"string descriptor 0 read error: %d\n",
-				err);
-			goto errout;
-		} else if (err < 4 || tbuf[0] < 4) {
-			dev_err (&dev->dev, "string descriptor 0 too short\n");
-			err = -EINVAL;
-			goto errout;
-		} else {
-			dev->have_langid = -1;
-			dev->string_langid = tbuf[2] | (tbuf[3]<< 8);
-				/* always use the first langid listed */
-			dev_dbg (&dev->dev, "default language 0x%04x\n",
-				dev->string_langid);
-		}
-	}
-
-	/*
-	 * ask for the length of the string 
-	 */
-
-	err = usb_get_string(dev, dev->string_langid, index, tbuf, 2);
-	if (err == -EPIPE || err == 0) {
-		dev_dbg(&dev->dev, "RETRY string %d read/%d\n", index, 2);
-		err = usb_get_string(dev, dev->string_langid, index, tbuf, 2);
-	}
-	if(err<2)
-		goto errout;
-	len=tbuf[0];	
-	
-	err = usb_get_string(dev, dev->string_langid, index, tbuf, len);
-	if (err == -EPIPE || err == 0) {
-		dev_dbg(&dev->dev, "RETRY string %d read/%d\n", index, len);
-		err = usb_get_string(dev, dev->string_langid, index, tbuf, len);
-	}
-	if (err < 0)
-		goto errout;
-
-	size--;		/* leave room for trailing NULL char in output buffer */
-	for (idx = 0, u = 2; u < err; u += 2) {
-		if (idx >= size)
-			break;
-		if (tbuf[u+1])			/* high byte */
-			buf[idx++] = '?';  /* non ISO-8859-1 character */
-		else
-			buf[idx++] = tbuf[u];
-	}
-	buf[idx] = 0;
-	err = idx;
-
- errout:
-	kfree(tbuf);
-	return err;
 }
 
 // synchronous request completion model
diff -Nru a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
--- a/drivers/usb/core/sysfs.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/core/sysfs.c	2004-07-13 13:12:22 -07:00
@@ -55,9 +55,9 @@
 
 	if (sscanf (buf, "%u", &config) != 1 || config > 255)
 		return -EINVAL;
-	down(&udev->serialize);
+	usb_lock_device(udev);
 	value = usb_set_configuration (udev, config);
-	up(&udev->serialize);
+	usb_unlock_device(udev);
 	return (value < 0) ? value : count;
 }
 
diff -Nru a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
--- a/drivers/usb/core/urb.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/core/urb.c	2004-07-13 13:12:22 -07:00
@@ -77,7 +77,7 @@
 
 /**
  * usb_free_urb - frees the memory used by a urb when all users of it are finished
- * @urb: pointer to the urb to free
+ * @urb: pointer to the urb to free, may be NULL
  *
  * Must be called when a user of a urb is finished with it.  When the last user
  * of the urb calls this function, the memory of the urb is freed.
@@ -93,7 +93,7 @@
 
 /**
  * usb_get_urb - increments the reference count of the urb
- * @urb: pointer to the urb to modify
+ * @urb: pointer to the urb to modify, may be NULL
  *
  * This must be  called whenever a urb is transferred from a device driver to a
  * host controller driver.  This allows proper reference counting to happen
@@ -256,13 +256,6 @@
 	if (!usb_pipecontrol (pipe) && dev->state < USB_STATE_CONFIGURED)
 		return -ENODEV;
 
-	/* (actually HCDs may need to duplicate this, endpoint might yet
-	 * stall due to queued bulk/intr transactions that complete after
-	 * we check)
-	 */
-	if (usb_endpoint_halted (dev, usb_pipeendpoint (pipe), is_out))
-		return -EPIPE;
-
 	/* FIXME there should be a sharable lock protecting us against
 	 * config/altsetting changes and disconnects, kicking in here.
 	 * (here == before maxpacket, and eventually endpoint type,
@@ -398,7 +391,8 @@
 
 /**
  * usb_unlink_urb - abort/cancel a transfer request for an endpoint
- * @urb: pointer to urb describing a previously submitted request
+ * @urb: pointer to urb describing a previously submitted request,
+ *	may be NULL
  *
  * This routine cancels an in-progress request.  URBs complete only
  * once per submission, and may be canceled only once per submission.
@@ -407,26 +401,25 @@
  * canceled (rather than any other code) and will quickly be removed
  * from host controller data structures.
  *
- * When the URB_ASYNC_UNLINK transfer flag for the URB is clear, this
- * request is synchronous.  Success is indicated by returning zero,
- * at which time the urb will have been unlinked and its completion
- * handler will have been called with urb->status == -ENOENT.  Failure is
- * indicated by any other return value.
- *
- * The synchronous cancelation mode may not be used
- * when unlinking an urb from an interrupt context, such as a bottom
- * half or a completion handler; or when holding a spinlock; or in
- * other cases when the caller can't schedule().
+ * In the past, clearing the URB_ASYNC_UNLINK transfer flag for the
+ * URB indicated that the request was synchronous.  This usage is now
+ * deprecated; if the flag is clear the call will be forwarded to
+ * usb_kill_urb() and the return value will be 0.  In the future, drivers
+ * should call usb_kill_urb() directly for synchronous unlinking.
  *
  * When the URB_ASYNC_UNLINK transfer flag for the URB is set, this
  * request is asynchronous.  Success is indicated by returning -EINPROGRESS,
- * at which time the urb will normally not have been unlinked.
- * The completion function will see urb->status == -ECONNRESET.  Failure
- * is indicated by any other return value.
+ * at which time the URB will normally have been unlinked but not yet
+ * given back to the device driver.  When it is called, the completion
+ * function will see urb->status == -ECONNRESET.  Failure is indicated
+ * by any other return value.  Unlinking will fail when the URB is not
+ * currently "linked" (i.e., it was never submitted, or it was unlinked
+ * before, or the hardware is already finished with it), even if the
+ * completion handler has not yet run.
  *
  * Unlinking and Endpoint Queues:
  *
- * Host Controller Driver (HCDs) place all the URBs for a particular
+ * Host Controller Drivers (HCDs) place all the URBs for a particular
  * endpoint in a queue.  Normally the queue advances as the controller
  * hardware processes each request.  But when an URB terminates with any
  * fault (such as an error, or being unlinked) its queue stops, at least
@@ -449,16 +442,57 @@
  * An unlinked URB may leave a gap in the stream of packets.  It is undefined
  * whether such gaps can be filled in.
  *
- * When control URBs terminates with an error, it is likely that the
+ * When a control URB terminates with an error, it is likely that the
  * status stage of the transfer will not take place, even if it is merely
  * a soft error resulting from a short-packet with URB_SHORT_NOT_OK set.
  */
 int usb_unlink_urb(struct urb *urb)
 {
-	if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)
-		return urb->dev->bus->op->unlink_urb(urb);
-	else
+	if (!urb)
+		return -EINVAL;
+	if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
+		usb_kill_urb(urb);
+		return 0;
+	}
+	if (!(urb->dev && urb->dev->bus && urb->dev->bus->op))
 		return -ENODEV;
+	return urb->dev->bus->op->unlink_urb(urb, -ECONNRESET);
+}
+
+/**
+ * usb_kill_urb - cancel a transfer request and wait for it to finish
+ * @urb: pointer to URB describing a previously submitted request,
+ *	may be NULL
+ *
+ * This routine cancels an in-progress request.  It is guaranteed that
+ * upon return all completion handlers will have finished and the URB
+ * will be totally idle and available for reuse.  These features make
+ * this an ideal way to stop I/O in a disconnect() callback or close()
+ * function.  If the request has not already finished or been unlinked
+ * the completion handler will see urb->status == -ENOENT.
+ *
+ * While the routine is running, attempts to resubmit the URB will fail
+ * with error -EPERM.  Thus even if the URB's completion handler always
+ * tries to resubmit, it will not succeed and the URB will become idle.
+ *
+ * This routine may not be used in an interrupt context (such as a bottom
+ * half or a completion handler), or when holding a spinlock, or in other
+ * situations where the caller can't schedule().
+ */
+void usb_kill_urb(struct urb *urb)
+{
+	if (!(urb && urb->dev && urb->dev->bus && urb->dev->bus->op))
+		return;
+	spin_lock_irq(&urb->lock);
+	++urb->reject;
+	spin_unlock_irq(&urb->lock);
+
+	urb->dev->bus->op->unlink_urb(urb, -ENOENT);
+	wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
+
+	spin_lock_irq(&urb->lock);
+	--urb->reject;
+	spin_unlock_irq(&urb->lock);
 }
 
 EXPORT_SYMBOL(usb_init_urb);
@@ -467,4 +501,5 @@
 EXPORT_SYMBOL(usb_get_urb);
 EXPORT_SYMBOL(usb_submit_urb);
 EXPORT_SYMBOL(usb_unlink_urb);
+EXPORT_SYMBOL(usb_kill_urb);
 
diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/core/usb.c	2004-07-13 13:12:22 -07:00
@@ -39,6 +39,7 @@
 #include <linux/spinlock.h>
 #include <linux/errno.h>
 #include <linux/smp_lock.h>
+#include <linux/rwsem.h>
 #include <linux/usb.h>
 
 #include <asm/io.h>
@@ -62,6 +63,8 @@
 int nousb;		/* Disable USB when built into kernel image */
 			/* Not honored on modular build */
 
+static DECLARE_RWSEM(usb_all_devices_rwsem);
+
 
 static int generic_probe (struct device *dev)
 {
@@ -93,11 +96,16 @@
 
 	if (!driver->probe)
 		return error;
+	if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED)
+		return -EHOSTUNREACH;
 
 	id = usb_match_id (intf, driver->id_table);
 	if (id) {
 		dev_dbg (dev, "%s - got id\n", __FUNCTION__);
+		intf->condition = USB_INTERFACE_BINDING;
 		error = driver->probe (intf, id);
+		intf->condition = error ? USB_INTERFACE_UNBOUND :
+				USB_INTERFACE_BOUND;
 	}
 
 	return error;
@@ -109,6 +117,8 @@
 	struct usb_interface *intf = to_usb_interface(dev);
 	struct usb_driver *driver = to_usb_driver(intf->dev.driver);
 
+	intf->condition = USB_INTERFACE_UNBINDING;
+
 	/* release all urbs for this interface */
 	usb_disable_interface(interface_to_usbdev(intf), intf);
 
@@ -120,6 +130,7 @@
 			intf->altsetting[0].desc.bInterfaceNumber,
 			0);
 	usb_set_intfdata(intf, NULL);
+	intf->condition = USB_INTERFACE_UNBOUND;
 
 	return 0;
 }
@@ -149,7 +160,9 @@
 	new_driver->driver.probe = usb_probe_interface;
 	new_driver->driver.remove = usb_unbind_interface;
 
+	usb_lock_all_devices();
 	retval = driver_register(&new_driver->driver);
+	usb_unlock_all_devices();
 
 	if (!retval) {
 		pr_info("%s: registered new driver %s\n",
@@ -178,7 +191,9 @@
 {
 	pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name);
 
+	usb_lock_all_devices();
 	driver_unregister (&driver->driver);
+	usb_unlock_all_devices();
 
 	usbfs_update_special();
 }
@@ -200,7 +215,7 @@
  * alternate settings available for this interfaces.
  *
  * Don't call this function unless you are bound to one of the interfaces
- * on this device or you own the dev->serialize semaphore!
+ * on this device or you have locked the device!
  */
 struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
 {
@@ -233,7 +248,7 @@
  * drivers avoid such mistakes.
  *
  * Don't call this function unless you are bound to the intf interface
- * or you own the device's ->serialize semaphore!
+ * or you have locked the device!
  */
 struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
 		unsigned int altnum)
@@ -301,9 +316,9 @@
  * way to bind to an interface is to return the private data from
  * the driver's probe() method.
  *
- * Callers must own the driver model's usb bus writelock.  So driver
- * probe() entries don't need extra locking, but other call contexts
- * may need to explicitly claim that lock.
+ * Callers must lock the device and own the driver model's usb bus writelock.
+ * So driver probe() entries don't need extra locking, but other call contexts
+ * may need to explicitly claim those locks.
  */
 int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv)
 {
@@ -314,6 +329,7 @@
 
 	dev->driver = &driver->driver;
 	usb_set_intfdata(iface, priv);
+	iface->condition = USB_INTERFACE_BOUND;
 
 	/* if interface was already added, bind now; else let
 	 * the future device_add() bind it, bypassing probe()
@@ -334,8 +350,8 @@
  * also causes the driver disconnect() method to be called.
  *
  * This call is synchronous, and may not be used in an interrupt context.
- * Callers must own the usb_device serialize semaphore and the driver model's
- * usb bus writelock.  So driver disconnect() entries don't need extra locking,
+ * Callers must lock the device and own the driver model's usb bus writelock.
+ * So driver disconnect() entries don't need extra locking,
  * but other call contexts may need to explicitly claim those locks.
  */
 void usb_driver_release_interface(struct usb_driver *driver,
@@ -353,6 +369,7 @@
 
 	dev->driver = NULL;
 	usb_set_intfdata(iface, NULL);
+	iface->condition = USB_INTERFACE_UNBOUND;
 }
 
 /**
@@ -828,6 +845,116 @@
 		put_device(&intf->dev);
 }
 
+/**
+ * usb_lock_device - acquire the lock for a usb device structure
+ * @udev: device that's being locked
+ *
+ * Use this routine rather than manipulating udev->serialize directly.
+ * This is necessary for proper interaction with usb_lock_all_devices().
+ */
+void usb_lock_device(struct usb_device *udev)
+{
+	down_read(&usb_all_devices_rwsem);
+	down(&udev->serialize);
+}
+
+/**
+ * usb_trylock_device - attempt to acquire the lock for a usb device structure
+ * @udev: device that's being locked
+ *
+ * Use this routine rather than manipulating udev->serialize directly.
+ * This is necessary for proper interaction with usb_lock_all_devices().
+ *
+ * Returns 1 if successful, 0 if contention.
+ */
+int usb_trylock_device(struct usb_device *udev)
+{
+	if (!down_read_trylock(&usb_all_devices_rwsem))
+		return 0;
+	if (down_trylock(&udev->serialize)) {
+		up_read(&usb_all_devices_rwsem);
+		return 0;
+	}
+	return 1;
+}
+
+/**
+ * usb_lock_device_for_reset - cautiously acquire the lock for a
+ *	usb device structure
+ * @udev: device that's being locked
+ * @iface: interface bound to the driver making the request (optional)
+ *
+ * Attempts to acquire the device lock, but fails if the device is
+ * NOTATTACHED or SUSPENDED, or if iface is specified and the interface
+ * is neither BINDING nor BOUND.  Rather than sleeping to wait for the
+ * lock, the routine polls repeatedly.  This is to prevent deadlock with
+ * disconnect; in some drivers (such as usb-storage) the disconnect()
+ * callback will block waiting for a device reset to complete.
+ *
+ * Returns a negative error code for failure, otherwise 1 or 0 to indicate
+ * that the device will or will not have to be unlocked.  (0 can be
+ * returned when an interface is given and is BINDING, because in that
+ * case the driver already owns the device lock.)
+ */
+int usb_lock_device_for_reset(struct usb_device *udev,
+		struct usb_interface *iface)
+{
+	if (udev->state == USB_STATE_NOTATTACHED)
+		return -ENODEV;
+	if (iface) {
+		switch (iface->condition) {
+		  case USB_INTERFACE_BINDING:
+			return 0;
+		  case USB_INTERFACE_BOUND:
+			break;
+		  default:
+			return -EINTR;
+		}
+	}
+
+	while (!usb_trylock_device(udev)) {
+		msleep(15);
+		if (udev->state == USB_STATE_NOTATTACHED)
+			return -ENODEV;
+		if (iface && iface->condition != USB_INTERFACE_BOUND)
+			return -EINTR;
+	}
+	return 1;
+}
+
+/**
+ * usb_unlock_device - release the lock for a usb device structure
+ * @udev: device that's being unlocked
+ *
+ * Use this routine rather than manipulating udev->serialize directly.
+ * This is necessary for proper interaction with usb_lock_all_devices().
+ */
+void usb_unlock_device(struct usb_device *udev)
+{
+	up(&udev->serialize);
+	up_read(&usb_all_devices_rwsem);
+}
+
+/**
+ * usb_lock_all_devices - acquire the lock for all usb device structures
+ *
+ * This is necessary when registering a new driver or probing a bus,
+ * since the driver-model core may try to use any usb_device.
+ */
+void usb_lock_all_devices(void)
+{
+	down_write(&usb_all_devices_rwsem);
+}
+
+/**
+ * usb_unlock_all_devices - release the lock for all usb device structures
+ */
+void usb_unlock_all_devices(void)
+{
+	up_write(&usb_all_devices_rwsem);
+}
+
+
 static struct usb_device *match_device(struct usb_device *dev,
 				       u16 vendor_id, u16 product_id)
 {
@@ -849,8 +976,10 @@
 	/* look through all of the children of this device */
 	for (child = 0; child < dev->maxchild; ++child) {
 		if (dev->children[child]) {
+			usb_lock_device(dev->children[child]);
 			ret_dev = match_device(dev->children[child],
 					       vendor_id, product_id);
+			usb_unlock_device(dev->children[child]);
 			if (ret_dev)
 				goto exit;
 		}
@@ -885,7 +1014,9 @@
 		bus = container_of(buslist, struct usb_bus, bus_list);
 		if (!bus->root_hub)
 			continue;
+		usb_lock_device(bus->root_hub);
 		dev = match_device(bus->root_hub, vendor_id, product_id);
+		usb_unlock_device(bus->root_hub);
 		if (dev)
 			goto exit;
 	}
@@ -1362,6 +1493,11 @@
 EXPORT_SYMBOL(usb_put_dev);
 EXPORT_SYMBOL(usb_get_dev);
 EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
+
+EXPORT_SYMBOL(usb_lock_device);
+EXPORT_SYMBOL(usb_trylock_device);
+EXPORT_SYMBOL(usb_lock_device_for_reset);
+EXPORT_SYMBOL(usb_unlock_device);
 
 EXPORT_SYMBOL(usb_driver_claim_interface);
 EXPORT_SYMBOL(usb_driver_release_interface);
diff -Nru a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
--- a/drivers/usb/core/usb.h	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/core/usb.h	2004-07-13 13:12:22 -07:00
@@ -24,5 +24,8 @@
 extern void usb_set_device_state(struct usb_device *udev,
 		enum usb_device_state new_state);
 
+extern void usb_lock_all_devices(void);
+extern void usb_unlock_all_devices(void);
+
 /* for labeling diagnostics */
 extern const char *usbcore_name;
diff -Nru a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
--- a/drivers/usb/gadget/Kconfig	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/gadget/Kconfig	2004-07-13 13:12:22 -07:00
@@ -208,6 +208,16 @@
 	  Say "y" to link the driver statically, or "m" to build a
 	  dynamically linked module called "g_zero".
 
+config USB_ZERO_HNPTEST
+	boolean "HNP Test Device"
+	depends on USB_ZERO && USB_OTG
+	help
+	  You can configure this device to enumerate using the device
+	  identifiers of the USB-OTG test device.  That means that when
+	  this gadget connects to another OTG device, with this one using
+	  the "B-Peripheral" role, that device will use HNP to let this
+	  one serve as the USB host instead (in the "B-Host" role).
+
 config USB_ETH
 	tristate "Ethernet Gadget"
 	depends on NET
diff -Nru a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
--- a/drivers/usb/gadget/ether.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/gadget/ether.c	2004-07-13 13:12:22 -07:00
@@ -338,6 +338,9 @@
  *
  * NOTE:  Controllers like superh_udc should probably be able to use
  * an RNDIS-only configuration.
+ *
+ * FIXME define some higher-powered configurations to make it easier
+ * to recharge batteries ...
  */
 
 #define DEV_CONFIG_VALUE	1	/* cdc or subset */
@@ -361,6 +364,14 @@
 	.bNumConfigurations =	1,
 };
 
+static struct usb_otg_descriptor
+otg_descriptor = {
+	.bLength =		sizeof otg_descriptor,
+	.bDescriptorType =	USB_DT_OTG,
+
+	.bmAttributes =		USB_OTG_SRP,
+};
+
 static struct usb_config_descriptor
 eth_config = {
 	.bLength =		sizeof eth_config,
@@ -375,7 +386,7 @@
 };
 
 #ifdef	CONFIG_USB_ETH_RNDIS
-static const struct usb_config_descriptor 
+static struct usb_config_descriptor 
 rndis_config = {
 	.bLength =              sizeof rndis_config,
 	.bDescriptorType =      USB_DT_CONFIG,
@@ -671,7 +682,8 @@
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static const struct usb_descriptor_header *fs_eth_function [10] = {
+static const struct usb_descriptor_header *fs_eth_function [11] = {
+	(struct usb_descriptor_header *) &otg_descriptor,
 #ifdef DEV_CONFIG_CDC
 	/* "cdc" mode descriptors */
 	(struct usb_descriptor_header *) &control_intf,
@@ -698,11 +710,13 @@
 	fs_eth_function[3] = NULL;
 #else
 	fs_eth_function[0] = NULL;
+	fs_eth_function[1] = 0;
 #endif
 }
 
 #ifdef	CONFIG_USB_ETH_RNDIS
 static const struct usb_descriptor_header *fs_rndis_function [] = {
+	(struct usb_descriptor_header *) &otg_descriptor,
 	/* control interface matches ACM, not Ethernet */
 	(struct usb_descriptor_header *) &rndis_control_intf,
 	(struct usb_descriptor_header *) &header_desc,
@@ -766,7 +780,8 @@
 	.bNumConfigurations =	1,
 };
 
-static const struct usb_descriptor_header *hs_eth_function [10] = {
+static const struct usb_descriptor_header *hs_eth_function [11] = {
+	(struct usb_descriptor_header *) &otg_descriptor,
 #ifdef DEV_CONFIG_CDC
 	/* "cdc" mode descriptors */
 	(struct usb_descriptor_header *) &control_intf,
@@ -793,11 +808,13 @@
 	hs_eth_function[3] = NULL;
 #else
 	hs_eth_function[0] = NULL;
+	hs_eth_function[1] = 0;
 #endif
 }
 
 #ifdef	CONFIG_USB_ETH_RNDIS
 static const struct usb_descriptor_header *hs_rndis_function [] = {
+	(struct usb_descriptor_header *) &otg_descriptor,
 	/* control interface matches ACM, not Ethernet */
 	(struct usb_descriptor_header *) &rndis_control_intf,
 	(struct usb_descriptor_header *) &header_desc,
@@ -870,18 +887,20 @@
  * complications: class descriptors, and an altsetting.
  */
 static int
-config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index)
-{
-	int				len;
+config_buf (enum usb_device_speed speed,
+	u8 *buf, u8 type,
+	unsigned index, int is_otg)
+{
+	int					len;
+	const struct usb_config_descriptor	*config;
+	const struct usb_descriptor_header	**function;
 #ifdef CONFIG_USB_GADGET_DUALSPEED
 	int				hs = (speed == USB_SPEED_HIGH);
 
 	if (type == USB_DT_OTHER_SPEED_CONFIG)
 		hs = !hs;
-#define which_config(t)	(hs ? & t ## _config   : & t ## _config)
 #define which_fn(t)	(hs ? & hs_ ## t ## _function : & fs_ ## t ## _function)
 #else
-#define	which_config(t)	(& t ## _config)
 #define	which_fn(t)	(& fs_ ## t ## _function)
 #endif
 
@@ -892,15 +911,23 @@
 	/* list the RNDIS config first, to make Microsoft's drivers
 	 * happy. DOCSIS 1.0 needs this too.
 	 */
-	if (device_desc.bNumConfigurations == 2 && index == 0)
-		len = usb_gadget_config_buf (which_config (rndis), buf,
-			USB_BUFSIZ, (const struct usb_descriptor_header **)
-				which_fn (rndis));
-	else
+	if (device_desc.bNumConfigurations == 2 && index == 0) {
+		config = &rndis_config;
+		function = (const struct usb_descriptor_header **)
+				which_fn (rndis);
+	} else
 #endif
-		len = usb_gadget_config_buf (which_config (eth), buf,
-			USB_BUFSIZ, (const struct usb_descriptor_header **)
-				which_fn (eth));
+	{
+		config = &eth_config;
+		function = (const struct usb_descriptor_header **)
+				which_fn (eth);
+	}
+
+	/* for now, don't advertise srp-only devices */
+	if (!is_otg)
+		function++;
+
+	len = usb_gadget_config_buf (config, buf, USB_BUFSIZ, function);
 	if (len < 0)
 		return len;
 	((struct usb_config_descriptor *) buf)->bDescriptorType = type;
@@ -1387,6 +1414,7 @@
 	/* descriptors just go into the pre-allocated ep0 buffer,
 	 * while config change events may enable network traffic.
 	 */
+	req->complete = eth_setup_complete;
 	switch (ctrl->bRequest) {
 
 	case USB_REQ_GET_DESCRIPTOR:
@@ -1414,7 +1442,8 @@
 		case USB_DT_CONFIG:
 			value = config_buf (gadget->speed, req->buf,
 					ctrl->wValue >> 8,
-					ctrl->wValue & 0xff);
+					ctrl->wValue & 0xff,
+					gadget->is_otg);
 			if (value >= 0)
 				value = min (ctrl->wLength, (u16) value);
 			break;
@@ -1431,6 +1460,10 @@
 	case USB_REQ_SET_CONFIGURATION:
 		if (ctrl->bRequestType != 0)
 			break;
+		if (gadget->a_hnp_support)
+			DEBUG (dev, "HNP available\n");
+		else if (gadget->a_alt_hnp_support)
+			DEBUG (dev, "HNP needs a different root port\n");
 		spin_lock (&dev->lock);
 		value = eth_set_config (dev, ctrl->wValue, GFP_ATOMIC);
 		spin_unlock (&dev->lock);
@@ -1724,9 +1757,10 @@
 
 	/* Padding up to RX_EXTRA handles minor disagreements with host.
 	 * Normally we use the USB "terminate on short read" convention;
-	 * so allow up to (N*maxpacket)-1, since that memory is normally
-	 * already allocated.  Major loss of synch means -EOVERFLOW; any
-	 * obviously corrupted packets will automatically be discarded. 
+	 * so allow up to (N*maxpacket), since that memory is normally
+	 * already allocated.  Some hardware doesn't deal well with short
+	 * reads (e.g. DMA must be N*maxpacket), so for now don't trim a
+	 * byte off the end (to force hardware errors on overflow).
 	 *
 	 * RNDIS uses internal framing, and explicitly allows senders to
 	 * pad to end-of-packet.  That's potentially nice for speed,
@@ -1739,10 +1773,6 @@
 		size += sizeof (struct rndis_packet_msg_type);
 #endif	
 	size -= size % dev->out_ep->maxpacket;
-#ifdef CONFIG_USB_ETH_RNDIS
-	if (!dev->rndis)
-#endif	
-		size--;
 
 	if ((skb = alloc_skb (size, gfp_flags)) == 0) {
 		DEBUG (dev, "no rx skb\n");
@@ -2429,6 +2459,14 @@
 
 	device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
 	usb_gadget_set_selfpowered (gadget);
+
+	if (gadget->is_otg) {
+		otg_descriptor.bmAttributes |= USB_OTG_HNP,
+		eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+#ifdef	CONFIG_USB_ETH_RNDIS
+		rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+#endif
+	}
 
  	net = alloc_etherdev (sizeof *dev);
  	if (!net)
diff -Nru a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
--- a/drivers/usb/gadget/inode.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/gadget/inode.c	2004-07-13 13:12:22 -07:00
@@ -253,6 +253,10 @@
 #define CHIP			"goku_udc"
 #endif
 
+#ifdef	CONFIG_USB_GADGET_OMAP
+#define CHIP			"omap_udc"
+#endif
+
 #ifdef	CONFIG_USB_GADGET_SA1100
 #define CHIP			"sa1100"
 #endif
@@ -737,7 +741,7 @@
  * speed descriptor, then optional high speed descriptor.
  */
 static ssize_t
-ep_config (struct file *fd, const char *buf, size_t len, loff_t *ptr)
+ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
 {
 	struct ep_data		*data = fd->private_data;
 	struct usb_ep		*ep;
@@ -944,7 +948,7 @@
 }
 
 static ssize_t
-ep0_read (struct file *fd, char *buf, size_t len, loff_t *ptr)
+ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
 {
 	struct dev_data			*dev = fd->private_data;
 	ssize_t				retval;
@@ -1125,7 +1129,7 @@
 }
 
 static ssize_t
-ep0_write (struct file *fd, const char *buf, size_t len, loff_t *ptr)
+ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
 {
 	struct dev_data		*dev = fd->private_data;
 	ssize_t			retval = -ESRCH;
@@ -1763,7 +1767,7 @@
 }
 
 static ssize_t
-dev_config (struct file *fd, const char *buf, size_t len, loff_t *ptr)
+dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
 {
 	struct dev_data		*dev = fd->private_data;
 	ssize_t			value = len, length = len;
diff -Nru a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
--- a/drivers/usb/gadget/serial.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/gadget/serial.c	2004-07-13 13:12:22 -07:00
@@ -249,6 +249,20 @@
 #define hw_optimize(g)			do {} while (0)
 #endif
 
+#ifdef	CONFIG_USB_GADGET_OMAP
+#define CHIP			"omap"
+#define EP0_MAXPACKET			64
+static const char EP_OUT_NAME [] = "ep2out-bulk";
+#define EP_OUT_NUM	2
+static const char EP_IN_NAME [] = "ep1in-bulk";
+#define EP_IN_NUM	1
+#define SELFPOWER 			USB_CONFIG_ATT_SELFPOWER
+/* supports remote wakeup, but this driver doesn't */
+
+/* no hw optimizations to apply */
+#define hw_optimize(g) do {} while (0)
+#endif
+
 
 /*
  * SA-1100 UDC:  widely used in first gen Linux-capable PDAs.
diff -Nru a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
--- a/drivers/usb/gadget/zero.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/gadget/zero.c	2004-07-13 13:12:22 -07:00
@@ -194,8 +194,13 @@
  * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
  * Instead:  allocate your own, using normal USB-IF procedures.
  */
+#ifndef	CONFIG_USB_ZERO_HNPTEST
 #define DRIVER_VENDOR_NUM	0x0525		/* NetChip */
 #define DRIVER_PRODUCT_NUM	0xa4a0		/* Linux-USB "Gadget Zero" */
+#else
+#define DRIVER_VENDOR_NUM	0x1a0a		/* OTG test device IDs */
+#define DRIVER_PRODUCT_NUM	0xbadd
+#endif
 
 /*-------------------------------------------------------------------------*/
 
@@ -259,6 +264,14 @@
 	.bMaxPower =		1,	/* self-powered */
 };
 
+static struct usb_otg_descriptor
+otg_descriptor = {
+	.bLength =		sizeof otg_descriptor,
+	.bDescriptorType =	USB_DT_OTG,
+
+	.bmAttributes =		USB_OTG_SRP,
+};
+
 /* one interface in each configuration */
 
 static const struct usb_interface_descriptor
@@ -302,6 +315,7 @@
 };
 
 static const struct usb_descriptor_header *fs_source_sink_function [] = {
+	(struct usb_descriptor_header *) &otg_descriptor,
 	(struct usb_descriptor_header *) &source_sink_intf,
 	(struct usb_descriptor_header *) &fs_sink_desc,
 	(struct usb_descriptor_header *) &fs_source_desc,
@@ -309,6 +323,7 @@
 };
 
 static const struct usb_descriptor_header *fs_loopback_function [] = {
+	(struct usb_descriptor_header *) &otg_descriptor,
 	(struct usb_descriptor_header *) &loopback_intf,
 	(struct usb_descriptor_header *) &fs_sink_desc,
 	(struct usb_descriptor_header *) &fs_source_desc,
@@ -356,6 +371,7 @@
 };
 
 static const struct usb_descriptor_header *hs_source_sink_function [] = {
+	(struct usb_descriptor_header *) &otg_descriptor,
 	(struct usb_descriptor_header *) &source_sink_intf,
 	(struct usb_descriptor_header *) &hs_source_desc,
 	(struct usb_descriptor_header *) &hs_sink_desc,
@@ -363,6 +379,7 @@
 };
 
 static const struct usb_descriptor_header *hs_loopback_function [] = {
+	(struct usb_descriptor_header *) &otg_descriptor,
 	(struct usb_descriptor_header *) &loopback_intf,
 	(struct usb_descriptor_header *) &hs_source_desc,
 	(struct usb_descriptor_header *) &hs_sink_desc,
@@ -444,6 +461,10 @@
 			? fs_source_sink_function
 			: fs_loopback_function;
 
+	/* for now, don't advertise srp-only devices */
+	if (!gadget->is_otg)
+		function++;
+
 	len = usb_gadget_config_buf (is_source_sink
 					? &source_sink_config
 					: &loopback_config,
@@ -485,7 +506,7 @@
 
 /* optionally require specific source/sink data patterns  */
 
-static inline int
+static int
 check_read_data (
 	struct zero_dev		*dev,
 	struct usb_ep		*ep,
@@ -519,7 +540,7 @@
 	return 0;
 }
 
-static inline void
+static void
 reinit_write_data (
 	struct zero_dev		*dev,
 	struct usb_ep		*ep,
@@ -811,6 +832,7 @@
 		dev->out_ep = NULL;
 	}
 	dev->config = 0;
+	del_timer (&dev->resume);
 }
 
 /* change our operational config.  this code must agree with the code
@@ -902,6 +924,7 @@
 	/* usually this stores reply data in the pre-allocated ep0 buffer,
 	 * but config change events will reconfigure hardware.
 	 */
+	req->zero = 0;
 	switch (ctrl->bRequest) {
 
 	case USB_REQ_GET_DESCRIPTOR:
@@ -951,6 +974,12 @@
 	case USB_REQ_SET_CONFIGURATION:
 		if (ctrl->bRequestType != 0)
 			goto unknown;
+		if (gadget->a_hnp_support)
+			DBG (dev, "HNP available\n");
+		else if (gadget->a_alt_hnp_support)
+			DBG (dev, "HNP needs a different root port\n");
+		else
+			VDBG (dev, "HNP inactive\n");
 		spin_lock (&dev->lock);
 		value = zero_set_config (dev, ctrl->wValue, GFP_ATOMIC);
 		spin_unlock (&dev->lock);
@@ -1080,8 +1109,10 @@
 	/* normally the host would be woken up for something
 	 * more significant than just a timer firing...
 	 */
-	status = usb_gadget_wakeup (dev->gadget);
-	DBG (dev, "wakeup --> %d\n", status);
+	if (dev->gadget->speed != USB_SPEED_UNKNOWN) {
+		status = usb_gadget_wakeup (dev->gadget);
+		DBG (dev, "wakeup --> %d\n", status);
+	}
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1198,6 +1229,12 @@
 	hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
 	hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
 #endif
+
+	if (gadget->is_otg) {
+		otg_descriptor.bmAttributes |= USB_OTG_HNP,
+		source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+		loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
 
 	usb_gadget_set_selfpowered (gadget);
 
diff -Nru a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
--- a/drivers/usb/host/ehci-hub.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/host/ehci-hub.c	2004-07-13 13:12:22 -07:00
@@ -81,7 +81,7 @@
 }
 
 
-/* caller owns root->serialize, and should reset/reinit on error */
+/* caller has locked the root hub, and should reset/reinit on error */
 static int ehci_hub_resume (struct usb_hcd *hcd)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
--- a/drivers/usb/host/ehci-q.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/host/ehci-q.c	2004-07-13 13:12:22 -07:00
@@ -153,17 +153,9 @@
 			usb_pipein (urb->pipe) ? "in" : "out",
 			token, urb->status);
 
-		/* stall indicates some recovery action is needed */
-		if (urb->status == -EPIPE) {
-			int	pipe = urb->pipe;
-
-			if (!usb_pipecontrol (pipe))
-				usb_endpoint_halt (urb->dev,
-					usb_pipeendpoint (pipe),
-					usb_pipeout (pipe));
-
 		/* if async CSPLIT failed, try cleaning out the TT buffer */
-		} else if (urb->dev->tt && !usb_pipeint (urb->pipe)
+		if (urb->status != -EPIPE
+				&& urb->dev->tt && !usb_pipeint (urb->pipe)
 				&& ((token & QTD_STS_MMF) != 0
 					|| QTD_CERR(token) == 0)
 				&& (!ehci_is_ARC(ehci)
diff -Nru a/drivers/usb/host/hc_simple.c b/drivers/usb/host/hc_simple.c
--- a/drivers/usb/host/hc_simple.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/host/hc_simple.c	2004-07-13 13:12:22 -07:00
@@ -146,11 +146,6 @@
 	if (!urb->dev || !urb->dev->bus || urb->hcpriv)
 		return -EINVAL;
 
-	if (usb_endpoint_halted
-	    (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) {
-		printk ("hci_submit_urb: endpoint_halted\n");
-		return -EPIPE;
-	}
 	hci = (hci_t *) urb->dev->bus->hcpriv;
 
 	/* a request to the virtual root hub */
@@ -189,7 +184,7 @@
  *
  * Return: 0 if success or error code 
  **************************************************************************/
-static int hci_unlink_urb (struct urb * urb)
+static int hci_unlink_urb (struct urb * urb, int status)
 {
 	unsigned long flags;
 	hci_t *hci;
@@ -219,45 +214,21 @@
 	if (!list_empty (&urb->urb_list) && urb->status == -EINPROGRESS) {
 		/* URB active? */
 
-		if (urb->transfer_flags & URB_ASYNC_UNLINK) {
-			/* asynchronous with callback */
-			/* relink the urb to the del list */
-			list_move (&urb->urb_list, &hci->del_list);
-			spin_unlock_irqrestore (&usb_urb_lock, flags);
-		} else {
-			/* synchronous without callback */
-
-			add_wait_queue (&hci->waitq, &wait);
-
-			set_current_state (TASK_UNINTERRUPTIBLE);
-			comp = urb->complete;
-			urb->complete = NULL;
-
-			/* relink the urb to the del list */
-			list_move(&urb->urb_list, &hci->del_list);
-
-			spin_unlock_irqrestore (&usb_urb_lock, flags);
-
-			schedule_timeout (HZ / 50);
-
-			if (!list_empty (&urb->urb_list))
-				list_del (&urb->urb_list);
-
-			urb->complete = comp;
-			urb->hcpriv = NULL;
-			remove_wait_queue (&hci->waitq, &wait);
-		}
+		/* asynchronous with callback */
+		/* relink the urb to the del list */
+		list_move (&urb->urb_list, &hci->del_list);
+		urb->status = status;
+		spin_unlock_irqrestore (&usb_urb_lock, flags);
 	} else {
 		/* hcd does not own URB but we keep the driver happy anyway */
 		spin_unlock_irqrestore (&usb_urb_lock, flags);
 
-		if (urb->complete && (urb->transfer_flags & URB_ASYNC_UNLINK)) {
-			urb->status = -ENOENT;
+		if (urb->complete) {
+			urb->status = status;
 			urb->actual_length = 0;
 			urb->complete (urb, NULL);
-			urb->status = 0;
-		} else {
-			urb->status = -ENOENT;
+			if (urb->reject)
+				wake_up (&usb_kill_urb_queue);
 		}
 	}
 
diff -Nru a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
--- a/drivers/usb/host/ohci-hcd.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/host/ohci-hcd.c	2004-07-13 13:12:22 -07:00
@@ -340,6 +340,7 @@
 		goto done;
 
 	if (!HCD_IS_RUNNING (ohci->hcd.state)) {
+sanitize:
 		ed->state = ED_IDLE;
 		finish_unlinks (ohci, 0, NULL);
 	}
@@ -347,7 +348,10 @@
 	switch (ed->state) {
 	case ED_UNLINK:		/* wait for hw to finish? */
 		/* major IRQ delivery trouble loses INTR_SF too... */
-		WARN_ON (limit-- == 0);
+		if (limit-- == 0) {
+			ohci_warn (ohci, "IRQ INTR_SF lossage\n");
+			goto sanitize;
+		}
 		spin_unlock_irqrestore (&ohci->lock, flags);
 		set_current_state (TASK_UNINTERRUPTIBLE);
 		schedule_timeout (1);
@@ -671,6 +675,8 @@
 	flush_scheduled_work();
 	if (HCD_IS_RUNNING(ohci->hcd.state))
 		hc_reset (ohci);
+	else
+		writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
 	
 	remove_debug_files (ohci);
 	ohci_mem_cleanup (ohci);
diff -Nru a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
--- a/drivers/usb/host/ohci-hub.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/host/ohci-hub.c	2004-07-13 13:12:22 -07:00
@@ -165,7 +165,7 @@
 
 static int hc_restart (struct ohci_hcd *ohci);
 
-/* caller owns root->serialize */
+/* caller has locked the root hub */
 static int ohci_hub_resume (struct usb_hcd *hcd)
 {
 	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
@@ -190,7 +190,7 @@
 		break;
 	case OHCI_USB_RESUME:
 		/* HCFS changes sometime after INTR_RD */
-		ohci_info (ohci, "remote wakeup\n");
+		ohci_info (ohci, "wakeup\n");
 		break;
 	case OHCI_USB_OPER:
 		ohci_dbg (ohci, "odd resume\n");
@@ -301,9 +301,9 @@
 {
 	struct usb_hcd	*hcd = _hcd;
 
-	down (&hcd->self.root_hub->serialize);
+	usb_lock_device (hcd->self.root_hub);
 	(void) ohci_hub_resume (hcd);
-	up (&hcd->self.root_hub->serialize);
+	usb_unlock_device (hcd->self.root_hub);
 }
 
 #else
@@ -381,12 +381,11 @@
 			&& ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
 					& ohci->hc_control)
 				== OHCI_USB_OPER
-			&& down_trylock (&hcd->self.root_hub->serialize) == 0
-			) {
+			&& usb_trylock_device (hcd->self.root_hub)) {
 		ohci_vdbg (ohci, "autosuspend\n");
 		(void) ohci_hub_suspend (&ohci->hcd);
 		ohci->hcd.state = USB_STATE_RUNNING;
-		up (&hcd->self.root_hub->serialize);
+		usb_unlock_device (hcd->self.root_hub);
 	}
 #endif
 
@@ -515,7 +514,7 @@
 #ifndef	OHCI_VERBOSE_DEBUG
 	if (*(u16*)(buf+2))	/* only if wPortChange is interesting */
 #endif
-		dbg_port (ohci, "GetStatus", wIndex + 1, temp);
+		dbg_port (ohci, "GetStatus", wIndex, temp);
 		break;
 	case SetHubFeature:
 		switch (wValue) {
diff -Nru a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c
--- a/drivers/usb/host/ohci-mem.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/host/ohci-mem.c	2004-07-13 13:12:22 -07:00
@@ -31,6 +31,7 @@
 	if (ohci != 0) {
 		memset (ohci, 0, sizeof (struct ohci_hcd));
 		ohci->hcd.product_desc = "OHCI Host Controller";
+		ohci->next_statechange = jiffies;
 		spin_lock_init (&ohci->lock);
 		INIT_LIST_HEAD (&ohci->pending);
 		INIT_WORK (&ohci->rh_resume, ohci_rh_resume, &ohci->hcd);
diff -Nru a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
--- a/drivers/usb/host/ohci-pci.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/host/ohci-pci.c	2004-07-13 13:12:22 -07:00
@@ -127,9 +127,9 @@
 #ifdef	CONFIG_USB_SUSPEND
 	(void) usb_suspend_device (hcd->self.root_hub);
 #else
-	down (&hcd->self.root_hub->serialize);
+	usb_lock_device (hcd->self.root_hub);
 	(void) ohci_hub_suspend (hcd);
-	up (&hcd->self.root_hub->serialize);
+	usb_unlock_device (hcd->self.root_hub);
 #endif
 
 	/* let things settle down a bit */
@@ -175,9 +175,9 @@
 	/* get extra cleanup even if remote wakeup isn't in use */
 	retval = usb_resume_device (hcd->self.root_hub);
 #else
-	down (&hcd->self.root_hub->serialize);
+	usb_lock_device (hcd->self.root_hub);
 	retval = ohci_hub_resume (hcd);
-	up (&hcd->self.root_hub->serialize);
+	usb_unlock_device (hcd->self.root_hub);
 #endif
 
 	if (retval == 0) {
diff -Nru a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
--- a/drivers/usb/host/ohci-q.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/host/ohci-q.c	2004-07-13 13:12:22 -07:00
@@ -751,12 +751,6 @@
 
   		cc = TD_CC_GET (tdINFO);
 
-		/* control endpoints only have soft stalls */
-  		if (type != PIPE_CONTROL && cc == TD_CC_STALL)
-			usb_endpoint_halt (urb->dev,
-				usb_pipeendpoint (urb->pipe),
-				usb_pipeout (urb->pipe));
-
 		/* update packet status if needed (short is normally ok) */
 		if (cc == TD_DATAUNDERRUN
 				&& !(urb->transfer_flags & URB_SHORT_NOT_OK))
@@ -924,7 +918,7 @@
 		/* only take off EDs that the HC isn't using, accounting for
 		 * frame counter wraps and EDs with partially retired TDs
 		 */
-		if (likely (HCD_IS_RUNNING(ohci->hcd.state))) {
+		if (likely (regs && HCD_IS_RUNNING(ohci->hcd.state))) {
 			if (tick_before (tick, ed->tick)) {
 skip_ed:
 				last = &ed->ed_next;
diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
--- a/drivers/usb/host/uhci-hcd.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/host/uhci-hcd.c	2004-07-13 13:12:22 -07:00
@@ -862,7 +862,7 @@
 	urbp->short_control_packet = 1;
 
 	td = list_entry(urbp->td_list.prev, struct uhci_td, list);
-	urbp->qh->element = td->dma_handle;
+	urbp->qh->element = cpu_to_le32(td->dma_handle);
 
 	return -EINPROGRESS;
 }
@@ -1120,10 +1120,6 @@
 
 td_error:
 	ret = uhci_map_status(status, uhci_packetout(td_token(td)));
-	if (ret == -EPIPE)
-		/* endpoint has stalled - mark it halted */
-		usb_endpoint_halt(urb->dev, uhci_endpoint(td_token(td)),
-	    			uhci_packetout(td_token(td)));
 
 err:
 	/* 
diff -Nru a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c
--- a/drivers/usb/image/mdc800.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/image/mdc800.c	2004-07-13 13:12:22 -07:00
@@ -1020,17 +1020,17 @@
 	{
 		err ("can't alloc memory!");
 
-		try_free_mem (mdc800->download_urb_buffer);
-		try_free_mem (mdc800->write_urb_buffer);
-		try_free_mem (mdc800->irq_urb_buffer);
-
-		try_free_urb (mdc800->write_urb);
-		try_free_urb (mdc800->download_urb);
-		try_free_urb (mdc800->irq_urb);
+		kfree(mdc800->download_urb_buffer);
+		kfree(mdc800->write_urb_buffer);
+		kfree(mdc800->irq_urb_buffer);
+
+		usb_free_urb(mdc800->write_urb);
+		usb_free_urb(mdc800->download_urb);
+		usb_free_urb(mdc800->irq_urb);
 
 		kfree (mdc800);
 	}
-	mdc800=0;
+	mdc800 = NULL;
 	return retval;
 }
 
@@ -1048,7 +1048,7 @@
 	kfree (mdc800->download_urb_buffer);
 
 	kfree (mdc800);
-	mdc800=0;
+	mdc800 = NULL;
 }
 
 module_init (usb_mdc800_init);
diff -Nru a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c
--- a/drivers/usb/image/microtek.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/image/microtek.c	2004-07-13 13:12:22 -07:00
@@ -214,8 +214,8 @@
 #ifdef MTS_DO_DEBUG
 
 static inline void mts_debug_dump(struct mts_desc* desc) {
-	MTS_DEBUG("desc at 0x%x: halted = %02x%02x, toggle = %02x%02x\n",
-		  (int)desc,(int)desc->usb_dev->halted[1],(int)desc->usb_dev->halted[0],
+	MTS_DEBUG("desc at 0x%x: toggle = %02x%02x\n",
+		  (int)desc,
 		  (int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0]
 		);
 	MTS_DEBUG("ep_out=%x ep_response=%x ep_image=%x\n",
@@ -341,12 +341,18 @@
 static int mts_scsi_host_reset (Scsi_Cmnd *srb)
 {
 	struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
+	int result, rc;
 
 	MTS_DEBUG_GOT_HERE();
 	mts_debug_dump(desc);
 
-	usb_reset_device(desc->usb_dev); /*FIXME: untested on new reset code */
-	return 0;  /* RANT why here 0 and not SUCCESS */
+	rc = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf);
+	if (rc < 0)
+		return FAILED;
+	result = usb_reset_device(desc->usb_dev);;
+	if (rc)
+		usb_unlock_device(desc->usb_dev);
+	return result ? FAILED : SUCCESS;
 }
 
 static
@@ -777,6 +783,7 @@
 		goto out_kfree;
 
 	new_desc->usb_dev = dev;
+	new_desc->usb_intf = intf;
 	init_MUTEX(&new_desc->lock);
 
 	/* endpoints */
diff -Nru a/drivers/usb/image/microtek.h b/drivers/usb/image/microtek.h
--- a/drivers/usb/image/microtek.h	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/image/microtek.h	2004-07-13 13:12:22 -07:00
@@ -31,6 +31,7 @@
 	struct mts_desc *prev;
 
 	struct usb_device *usb_dev;
+	struct usb_interface *usb_intf;
 
 	/* Endpoint addresses */
 	u8 ep_out;
diff -Nru a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
--- a/drivers/usb/input/hid-core.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/input/hid-core.c	2004-07-13 13:12:22 -07:00
@@ -807,7 +807,11 @@
 	unsigned size = field->report_size;
 	__s32 min = field->logical_minimum;
 	__s32 max = field->logical_maximum;
-	__s32 value[count]; /* WARNING: gcc specific */
+	__s32 *value;
+
+	value = kmalloc(sizeof(__s32)*count, GFP_ATOMIC);
+	if (!value)
+		return;
 
 	for (n = 0; n < count; n++) {
 
@@ -817,7 +821,7 @@
 			if (!(field->flags & HID_MAIN_ITEM_VARIABLE) /* Ignore report if ErrorRollOver */
 			    && value[n] >= min && value[n] <= max
 			    && field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1)
-				return;
+				goto exit;
 	}
 
 	for (n = 0; n < count; n++) {
@@ -847,6 +851,8 @@
 	}
 
 	memcpy(field->value, value, count * sizeof(__s32));
+exit:
+	kfree(value);
 }
 
 static int hid_input_report(int type, struct urb *urb, struct pt_regs *regs)
@@ -1425,6 +1431,9 @@
 #define USB_VENDOR_ID_GLAB		0x06c2
 #define USB_DEVICE_ID_4_PHIDGETSERVO_30	0x0038
 #define USB_DEVICE_ID_1_PHIDGETSERVO_30	0x0039
+#define USB_DEVICE_ID_8_8_8_IF_KIT	0x0045
+#define USB_DEVICE_ID_0_0_4_IF_KIT	0x0040
+#define USB_DEVICE_ID_0_8_8_IF_KIT	0x0053
 
 #define USB_VENDOR_ID_WISEGROUP		0x0925
 #define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
@@ -1483,8 +1492,13 @@
 	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 7, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_VOLITO, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PTU, HID_QUIRK_IGNORE },
+
 	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
+
 	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
 
diff -Nru a/drivers/usb/media/Kconfig b/drivers/usb/media/Kconfig
--- a/drivers/usb/media/Kconfig	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/media/Kconfig	2004-07-13 13:12:22 -07:00
@@ -162,6 +162,21 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called se401.
 
+config USB_SN9C102
+	tristate "USB SN9C10[12] PC Camera Controller support (EXPERIMENTAL)"
+	depends on USB && VIDEO_DEV && EXPERIMENTAL
+	---help---
+	  Say Y here if you want support for cameras based on SN9C101 and
+	  SN9C102 PC Camera Controllers.
+
+	  See <file:Documentation/usb/sn9c102.txt> for more informations.
+
+	  This driver uses the Video For Linux API. You must say Y or M to
+	  "Video For Linux" to use this driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sn9c102.
+
 config USB_STV680
 	tristate "USB STV680 (Pencam) Camera support"
 	depends on USB && VIDEO_DEV
@@ -181,7 +196,7 @@
 
 config USB_W9968CF
 	tristate "USB W996[87]CF JPEG Dual Mode Camera support"
-	depends on USB && VIDEO_DEV && I2C
+	depends on USB && VIDEO_DEV && I2C && VIDEO_OVCAMCHIP
 	---help---
 	  Say Y here if you want support for cameras based on OV681 or
 	  Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips.
@@ -190,16 +205,13 @@
 	  separate module only (released under GPL). It allows to use higher 
 	  resolutions and framerates, but cannot be included in the official 
 	  Linux kernel for performance purposes.
-	  At the moment the driver needs a third-party module for the CMOS 
-	  sensors, which is available on internet: it is recommended to read
-	  <file:Documentation/usb/w9968cf.txt> for more informations and for
-	  a list of supported cameras.
-	
-	  This driver uses the Video For Linux and the I2C APIs. You must say
-	  Y or M to both "Video For Linux" and "I2C Support" to use this 
-	  driver.
-	
-	  This code is also available as a module ( = code which can be
-	  inserted in and removed from the running kernel whenever you want).
-	  The module will be called w9968cf.o. If you want to compile it as a
-	  module, say M here and read <file:Documentation/kbuild/modules.txt>.
+
+	  See <file:Documentation/usb/w9968cf.txt> for more informations.
+
+	  This driver uses the Video For Linux and the I2C APIs. It needs the
+	  OmniVision Camera Chip support as well. You must say Y or M to
+	  "Video For Linux", "I2C Support" and "OmniVision Camera Chip 
+	  support" to use this driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called w9968cf.
diff -Nru a/drivers/usb/media/Makefile b/drivers/usb/media/Makefile
--- a/drivers/usb/media/Makefile	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/media/Makefile	2004-07-13 13:12:22 -07:00
@@ -3,6 +3,7 @@
 #
 
 pwc-objs	:= pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o
+sn9c102-objs	:= sn9c102_core.o sn9c102_pas106b.o sn9c102_tas5110c1b.o sn9c102_tas5130d1b.o
 
 obj-$(CONFIG_USB_DABUSB)	+= dabusb.o
 obj-$(CONFIG_USB_DSBR)		+= dsbr100.o
@@ -11,6 +12,7 @@
 obj-$(CONFIG_USB_OV511)		+= ov511.o
 obj-$(CONFIG_USB_PWC)		+= pwc.o
 obj-$(CONFIG_USB_SE401)		+= se401.o
+obj-$(CONFIG_USB_SN9C102)	+= sn9c102.o
 obj-$(CONFIG_USB_STV680)	+= stv680.o
 obj-$(CONFIG_USB_VICAM)		+= vicam.o usbvideo.o
 obj-$(CONFIG_USB_W9968CF)	+= w9968cf.o
diff -Nru a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c
--- a/drivers/usb/media/se401.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/media/se401.c	2004-07-13 13:12:22 -07:00
@@ -1295,7 +1295,7 @@
 		    &se401->button, sizeof(se401->button),
 		    se401_button_irq,
 		    se401,
-		    HZ/10
+		    8
 		);
 		if (usb_submit_urb(se401->inturb, GFP_KERNEL)) {
 			info("int urb burned down");
diff -Nru a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/usb/media/sn9c102.h	2004-07-13 13:12:22 -07:00
@@ -0,0 +1,181 @@
+/***************************************************************************
+ * V4L2 driver for SN9C10[12] PC Camera Controllers                        *
+ *                                                                         *
+ * Copyright (C) 2004 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    *
+ * the Free Software Foundation; either version 2 of the License, or       *
+ * (at your option) any later version.                                     *
+ *                                                                         *
+ * This program is distributed in the hope that it will be useful,         *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ * GNU General Public License for more details.                            *
+ *                                                                         *
+ * You should have received a copy of the GNU General Public License       *
+ * along with this program; if not, write to the Free Software             *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
+ ***************************************************************************/
+
+#ifndef _SN9C102_H_
+#define _SN9C102_H_
+
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/videodev.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/rwsem.h>
+
+#include "sn9c102_sensor.h"
+
+/*****************************************************************************/
+
+#define SN9C102_DEBUG
+#define SN9C102_DEBUG_LEVEL       2
+#define SN9C102_MAX_DEVICES       64
+#define SN9C102_MAX_FRAMES        32
+#define SN9C102_URBS              2
+#define SN9C102_ISO_PACKETS       7
+#define SN9C102_ALTERNATE_SETTING 8
+#define SN9C102_CTRL_TIMEOUT      10*HZ
+
+/*****************************************************************************/
+
+#define SN9C102_MODULE_NAME  "V4L2 driver for SN9C10[12] PC Camera Controllers"
+#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.01-beta"
+#define SN9C102_MODULE_VERSION_CODE  KERNEL_VERSION(1, 0, 1)
+
+SN9C102_ID_TABLE;
+SN9C102_SENSOR_TABLE;
+
+enum sn9c102_frame_state {
+	F_UNUSED,
+	F_QUEUED,
+	F_GRABBING,
+	F_DONE,
+	F_ERROR,
+};
+
+struct sn9c102_frame_t {
+	void* bufmem;
+	struct v4l2_buffer buf;
+	enum sn9c102_frame_state state;
+	struct list_head frame;
+	unsigned long vma_use_count;
+};
+
+enum sn9c102_dev_state {
+	DEV_INITIALIZED = 0x01,
+	DEV_DISCONNECTED = 0x02,
+	DEV_MISCONFIGURED = 0x04,
+};
+
+enum sn9c102_io_method {
+	IO_NONE,
+	IO_READ,
+	IO_MMAP,
+};
+
+enum sn9c102_stream_state {
+	STREAM_OFF,
+	STREAM_INTERRUPT,
+	STREAM_ON,
+};
+
+struct sn9c102_sysfs_attr {
+	u8 reg, val, i2c_reg, i2c_val;
+};
+
+static DECLARE_MUTEX(sn9c102_sysfs_lock);
+static DECLARE_RWSEM(sn9c102_disconnect);
+
+struct sn9c102_device {
+	struct device dev;
+
+	struct video_device* v4ldev;
+
+	struct sn9c102_sensor* sensor;
+
+	struct usb_device* usbdev;
+	struct urb* urb[SN9C102_URBS];
+	void* transfer_buffer[SN9C102_URBS];
+	u8* control_buffer;
+
+	struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES];
+	struct list_head inqueue, outqueue;
+	u32 frame_count, nbuffers;
+
+	enum sn9c102_io_method io;
+	enum sn9c102_stream_state stream;
+
+	struct sn9c102_sysfs_attr sysfs;
+	u16 reg[32];
+
+	enum sn9c102_dev_state state;
+	u8 users;
+
+	struct semaphore dev_sem, fileop_sem;
+	spinlock_t queue_lock;
+	wait_queue_head_t open, wait_frame, wait_stream;
+};
+
+/*****************************************************************************/
+
+void
+sn9c102_attach_sensor(struct sn9c102_device* cam,
+                      struct sn9c102_sensor* sensor)
+{
+	cam->sensor = sensor;
+	cam->sensor->dev = &cam->dev;
+	cam->sensor->usbdev = cam->usbdev;
+}
+
+/*****************************************************************************/
+
+#undef DBG
+#undef KDBG
+#ifdef SN9C102_DEBUG
+#	define DBG(level, fmt, args...)                                       \
+{                                                                             \
+	if (debug >= (level)) {                                               \
+		if ((level) == 1)                                             \
+			dev_err(&cam->dev, fmt "\n", ## args);                \
+		else if ((level) == 2)                                        \
+			dev_info(&cam->dev, fmt "\n", ## args);               \
+		else if ((level) >= 3)                                        \
+			dev_info(&cam->dev, "[%s:%d] " fmt "\n",              \
+			         __FUNCTION__, __LINE__ , ## args);           \
+	}                                                                     \
+}
+#	define KDBG(level, fmt, args...)                                      \
+{                                                                             \
+	if (debug >= (level)) {                                               \
+		if ((level) == 1 || (level) == 2)                             \
+			pr_info("sn9c102: " fmt "\n", ## args);               \
+		else if ((level) == 3)                                        \
+			pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__,  \
+			         __LINE__ , ## args);                         \
+	}                                                                     \
+}
+#else
+#	define KDBG(level, fmt, args...) do {;} while(0);
+#	define DBG(level, fmt, args...) do {;} while(0);
+#endif
+
+#undef PDBG
+#define PDBG(fmt, args...)                                                    \
+dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args);
+
+#undef PDBGG
+#define PDBGG(fmt, args...) do {;} while(0); /* placeholder */
+
+#endif /* _SN9C102_H_ */
diff -Nru a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/usb/media/sn9c102_core.c	2004-07-13 13:12:22 -07:00
@@ -0,0 +1,2439 @@
+/***************************************************************************
+ * V4L2 driver for SN9C10[12] PC Camera Controllers                        *
+ *                                                                         *
+ * Copyright (C) 2004 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    *
+ * the Free Software Foundation; either version 2 of the License, or       *
+ * (at your option) any later version.                                     *
+ *                                                                         *
+ * This program is distributed in the hope that it will be useful,         *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ * GNU General Public License for more details.                            *
+ *                                                                         *
+ * You should have received a copy of the GNU General Public License       *
+ * along with this program; if not, write to the Free Software             *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
+ ***************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/stddef.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/page-flags.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include "sn9c102.h"
+
+/*****************************************************************************/
+
+MODULE_DEVICE_TABLE(usb, sn9c102_id_table);
+
+MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL);
+MODULE_DESCRIPTION(SN9C102_MODULE_NAME);
+MODULE_VERSION(SN9C102_MODULE_VERSION);
+MODULE_LICENSE(SN9C102_MODULE_LICENSE);
+
+static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1};
+static unsigned int nv;
+module_param_array(video_nr, short, nv, 0444);
+MODULE_PARM_DESC(video_nr,
+                 "\n<-1|n[,...]> Specify V4L2 minor mode number."
+                 "\n -1 = use next available (default)"
+                 "\n  n = use minor number n (integer >= 0)"
+                 "\nYou can specify up to "__MODULE_STRING(SN9C102_MAX_DEVICES)
+                 " cameras this way."
+                 "\nFor example:"
+                 "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
+                 "\nthe second camera and use auto for the first"
+                 "\none and for every other camera."
+                 "\n");
+
+#ifdef SN9C102_DEBUG
+static unsigned short debug = SN9C102_DEBUG_LEVEL;
+module_param(debug, ushort, 0644);
+MODULE_PARM_DESC(debug,
+                 "\n<n> Debugging information level, from 0 to 3:"
+                 "\n0 = none (use carefully)"
+                 "\n1 = critical errors"
+                 "\n2 = significant informations"
+                 "\n3 = more verbose messages"
+                 "\nLevel 3 is useful for testing only, when only "
+                 "one device is used."
+                 "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"."
+                 "\n");
+#endif
+
+/*****************************************************************************/
+
+typedef char sn9c102_sof_header_t[7];
+typedef char sn9c102_eof_header_t[4];
+
+static sn9c102_sof_header_t sn9c102_sof_header[] = {
+	{0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x00},
+	{0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01},
+};
+
+/* Number of random bytes that complete the SOF above headers */
+#define SN9C102_SOFLEN 5
+
+static sn9c102_eof_header_t sn9c102_eof_header[] = {
+	{0x00, 0x00, 0x00, 0x00},
+	{0x40, 0x00, 0x00, 0x00},
+	{0x80, 0x00, 0x00, 0x00},
+	{0xc0, 0x00, 0x00, 0x00},
+};
+
+/*****************************************************************************/
+
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+	unsigned long kva, ret;
+
+	kva = (unsigned long)page_address(vmalloc_to_page((void *)adr));
+	kva |= adr & (PAGE_SIZE-1);
+	ret = __pa(kva);
+	return ret;
+}
+
+
+static void* rvmalloc(size_t size)
+{
+	void* mem;
+	unsigned long adr;
+
+	size = PAGE_ALIGN(size);
+
+	mem = vmalloc_32((unsigned long)size);
+	if (!mem)
+		return NULL;
+
+	memset(mem, 0, size);
+
+	adr = (unsigned long)mem;
+	while (size > 0) {
+		SetPageReserved(vmalloc_to_page((void *)adr));
+		adr += PAGE_SIZE;
+		size -= PAGE_SIZE;
+	}
+
+	return mem;
+}
+
+
+static void rvfree(void* mem, size_t size)
+{
+	unsigned long adr;
+
+	if (!mem)
+		return;
+
+	size = PAGE_ALIGN(size);
+
+	adr = (unsigned long)mem;
+	while (size > 0) {
+		ClearPageReserved(vmalloc_to_page((void *)adr));
+		adr += PAGE_SIZE;
+		size -= PAGE_SIZE;
+	}
+
+	vfree(mem);
+}
+
+
+static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count)
+{
+	struct v4l2_pix_format* p = &(cam->sensor->pix_format);
+	const size_t imagesize = (p->width * p->height * p->priv)/8;
+	void* buff = NULL;
+	u32 i;
+
+	if (count > SN9C102_MAX_FRAMES)
+		count = SN9C102_MAX_FRAMES;
+
+	cam->nbuffers = count;
+	while (cam->nbuffers > 0) {
+		if ((buff = rvmalloc(cam->nbuffers * imagesize)))
+			break;
+		cam->nbuffers--;
+	}
+
+	for (i = 0; i < cam->nbuffers; i++) {
+		cam->frame[i].bufmem = buff + i*imagesize;
+		cam->frame[i].buf.index = i;
+		cam->frame[i].buf.m.offset = i*imagesize;
+		cam->frame[i].buf.length = imagesize;
+		cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		cam->frame[i].buf.sequence = 0;
+		cam->frame[i].buf.field = V4L2_FIELD_NONE;
+		cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
+		cam->frame[i].buf.flags = 0;
+	}
+
+	return cam->nbuffers;
+}
+
+
+static void sn9c102_release_buffers(struct sn9c102_device* cam)
+{
+	if (cam->nbuffers) {
+		rvfree(cam->frame[0].bufmem,
+		       cam->nbuffers * cam->frame[0].buf.length);
+		cam->nbuffers = 0;
+	}
+}
+
+
+static void sn9c102_empty_framequeues(struct sn9c102_device* cam)
+{
+	u32 i;
+
+	INIT_LIST_HEAD(&cam->inqueue);
+	INIT_LIST_HEAD(&cam->outqueue);
+
+	for (i = 0; i < SN9C102_MAX_FRAMES; i++) {
+		cam->frame[i].state = F_UNUSED;
+		cam->frame[i].buf.bytesused = 0;
+	}
+}
+
+
+static void sn9c102_queue_unusedframes(struct sn9c102_device* cam)
+{
+	unsigned long lock_flags;
+	u32 i;
+
+	for (i = 0; i < cam->nbuffers; i++)
+		if (cam->frame[i].state == F_UNUSED) {
+			cam->frame[i].state = F_QUEUED;
+			spin_lock_irqsave(&cam->queue_lock, lock_flags);
+			list_add_tail(&cam->frame[i].frame, &cam->inqueue);
+			spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+		}
+}
+
+/*****************************************************************************/
+
+int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index)
+{
+	struct usb_device* udev = cam->usbdev;
+	u8* buff = cam->control_buffer;
+	int res;
+
+	if (index == 0x18)
+		value = (value & 0xcf) | (cam->reg[0x18] & 0x30);
+
+	*buff = value;
+
+	res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+	                      index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
+	if (res < 0) {
+		DBG(3, "Failed to write a register (value 0x%02X, index "
+		       "0x%02X, error %d)", value, index, res)
+		return -1;
+	}
+
+	cam->reg[index] = value;
+
+	return 0;
+}
+
+
+/* NOTE: reading some registers always returns 0 */
+static int sn9c102_read_reg(struct sn9c102_device* cam, u16 index)
+{
+	struct usb_device* udev = cam->usbdev;
+	u8* buff = cam->control_buffer;
+	int res;
+
+	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+	                      index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
+	if (res < 0)
+		DBG(3, "Failed to read a register (index 0x%02X, error %d)",
+		    index, res)
+
+	return (res >= 0) ? (int)(*buff) : -1;
+}
+
+
+int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index)
+{
+	if (index > 0x1f)
+		return -EINVAL;
+
+	return cam->reg[index];
+}
+
+
+static int
+sn9c102_i2c_wait(struct sn9c102_device* cam, struct sn9c102_sensor* sensor)
+{
+	int i, r;
+
+	for (i = 1; i <= 5; i++) {
+		r = sn9c102_read_reg(cam, 0x08);
+		if (r < 0)
+			return -EIO;
+		if (r & 0x04)
+			return 0;
+		if (sensor->frequency & SN9C102_I2C_400KHZ)
+			udelay(5*8);
+		else
+			udelay(16*8);
+	}
+	return -EBUSY;
+}
+
+
+static int
+sn9c102_i2c_detect_read_error(struct sn9c102_device* cam, 
+                              struct sn9c102_sensor* sensor)
+{
+	int r;
+	r = sn9c102_read_reg(cam, 0x08);
+	return (r < 0 || (r >= 0 && !(r & 0x08))) ? -EIO : 0;
+}
+
+
+static int
+sn9c102_i2c_detect_write_error(struct sn9c102_device* cam, 
+                               struct sn9c102_sensor* sensor)
+{
+	int r;
+	r = sn9c102_read_reg(cam, 0x08);
+	return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0;
+}
+
+
+int 
+sn9c102_i2c_try_read(struct sn9c102_device* cam,
+                     struct sn9c102_sensor* sensor, u8 address)
+{
+	struct usb_device* udev = cam->usbdev;
+	u8* data = cam->control_buffer;
+	int err = 0, res;
+
+	/* Write cycle - address */
+	data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
+	          ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10;
+	data[1] = sensor->slave_write_id;
+	data[2] = address;
+	data[7] = 0x10;
+	res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+	                      0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
+	if (res < 0)
+		err += res;
+
+	err += sn9c102_i2c_wait(cam, sensor);
+
+	/* Read cycle - 1 byte */
+	data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
+	          ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) |
+	          0x10 | 0x02;
+	data[1] = sensor->slave_read_id;
+	data[7] = 0x10;
+	res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+	                      0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
+	if (res < 0)
+		err += res;
+
+	err += sn9c102_i2c_wait(cam, sensor);
+
+	/* The read byte will be placed in data[4] */
+	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+	                      0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT);
+	if (res < 0)
+		err += res;
+
+	err += sn9c102_i2c_detect_read_error(cam, sensor);
+
+	if (err)
+		DBG(3, "I2C read failed for %s image sensor", sensor->name)
+
+	PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[4])
+
+	return err ? -1 : (int)data[4];
+}
+
+
+int 
+sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
+                          struct sn9c102_sensor* sensor, u8 n, u8 data0,
+                          u8 data1, u8 data2, u8 data3, u8 data4, u8 data5)
+{
+	struct usb_device* udev = cam->usbdev;
+	u8* data = cam->control_buffer;
+	int err = 0, res;
+
+	/* Write cycle. It usually is address + value */
+	data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
+	          ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0)
+	          | ((n - 1) << 4);
+	data[1] = data0;
+	data[2] = data1;
+	data[3] = data2;
+	data[4] = data3;
+	data[5] = data4;
+	data[6] = data5;
+	data[7] = 0x10;
+	res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+	                      0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
+	if (res < 0)
+		err += res;
+
+	err += sn9c102_i2c_wait(cam, sensor);
+	err += sn9c102_i2c_detect_write_error(cam, sensor);
+
+	if (err)
+		DBG(3, "I2C write failed for %s image sensor", sensor->name)
+
+	PDBGG("I2C write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
+	      "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X",
+	      n, data0, data1, data2, data3, data4, data5)
+
+	return err ? -1 : 0;
+}
+
+
+int 
+sn9c102_i2c_try_write(struct sn9c102_device* cam,
+                      struct sn9c102_sensor* sensor, u8 address, u8 value)
+{
+	return sn9c102_i2c_try_raw_write(cam, sensor, 3, 
+	                                 sensor->slave_write_id, address,
+	                                 value, 0, 0, 0);
+}
+
+
+int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address)
+{
+	if (!cam->sensor)
+		return -1;
+
+	return sn9c102_i2c_try_read(cam, cam->sensor, address);
+}
+
+
+int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value)
+{
+	if (!cam->sensor)
+		return -1;
+
+	return sn9c102_i2c_try_write(cam, cam->sensor, address, value);
+}
+
+/*****************************************************************************/
+
+static void* sn9c102_find_sof_header(void* mem, size_t len)
+{
+	size_t soflen=sizeof(sn9c102_sof_header_t), SOFLEN=SN9C102_SOFLEN, i;
+	u8 j, n = sizeof(sn9c102_sof_header) / soflen;
+
+	for (i = 0; (len >= soflen+SOFLEN) && (i <= len-soflen-SOFLEN); i++)
+		for (j = 0; j < n; j++)
+			if (!memcmp(mem + i, sn9c102_sof_header[j], soflen))
+				/* Skips the header */
+				return mem + i + soflen + SOFLEN;
+
+	return NULL;
+}
+
+
+static void* sn9c102_find_eof_header(void* mem, size_t len)
+{
+	size_t eoflen = sizeof(sn9c102_eof_header_t), i;
+	unsigned j, n = sizeof(sn9c102_eof_header) / eoflen;
+
+	for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++)
+		for (j = 0; j < n; j++)
+			if (!memcmp(mem + i, sn9c102_eof_header[j], eoflen))
+				return mem + i;
+
+	return NULL;
+}
+
+
+static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
+{
+	struct sn9c102_device* cam = urb->context;
+	struct sn9c102_frame_t** f;
+	unsigned long lock_flags;
+	u8 i;
+	int err = 0;
+
+	if (urb->status == -ENOENT)
+		return;
+
+	f = &cam->frame_current;
+
+	if (cam->stream == STREAM_INTERRUPT) {
+		cam->stream = STREAM_OFF;
+		if ((*f))
+			(*f)->state = F_QUEUED;
+		DBG(3, "Stream interrupted")
+		wake_up_interruptible(&cam->wait_stream);
+	}
+
+	if ((cam->state & DEV_DISCONNECTED)||(cam->state & DEV_MISCONFIGURED))
+		return;
+
+	if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue))
+		goto resubmit_urb;
+
+	if (!(*f))
+		(*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t,
+		                  frame);
+
+	for (i = 0; i < urb->number_of_packets; i++) {
+		unsigned int img, len, status;
+		void *pos, *sof, *eof;
+
+		len = urb->iso_frame_desc[i].actual_length;
+		status = urb->iso_frame_desc[i].status;
+		pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+		if (status) {
+			DBG(3, "Error in isochronous frame")
+			(*f)->state = F_ERROR;
+			continue;
+		}
+
+		PDBGG("Isochrnous frame: length %u, #%u i", len, i)
+
+		/* NOTE: It is probably correct to assume that SOF and EOF
+		         headers do not occur between two consecutive packets,
+		         but who knows..Whatever is the truth, this assumption
+		         doesn't introduce bugs. */
+
+redo:
+		sof = sn9c102_find_sof_header(pos, len);
+		if (!sof) {
+			eof = sn9c102_find_eof_header(pos, len);
+			if ((*f)->state == F_GRABBING) {
+end_of_frame:
+				img = len;
+
+				if (eof)
+					img = (eof > pos) ? eof - pos - 1 : 0;
+
+				if ((*f)->buf.bytesused+img>(*f)->buf.length) {
+					u32 b = (*f)->buf.bytesused + img -
+					        (*f)->buf.length;
+					img = (*f)->buf.length - 
+					      (*f)->buf.bytesused;
+					DBG(3, "Expected EOF not found: "
+					       "video frame cut")
+					if (eof)
+						DBG(3, "Exceeded limit: +%u "
+						       "bytes", (unsigned)(b))
+				}
+
+				memcpy((*f)->bufmem + (*f)->buf.bytesused, pos,
+				       img);
+
+				if ((*f)->buf.bytesused == 0)
+					do_gettimeofday(&(*f)->buf.timestamp);
+
+				(*f)->buf.bytesused += img;
+
+				if ((*f)->buf.bytesused == (*f)->buf.length) {
+					u32 b = (*f)->buf.bytesused;
+					(*f)->state = F_DONE;
+					(*f)->buf.sequence= ++cam->frame_count;
+					spin_lock_irqsave(&cam->queue_lock,
+					                  lock_flags);
+					list_move_tail(&(*f)->frame,
+					               &cam->outqueue);
+					if (!list_empty(&cam->inqueue))
+						(*f) = list_entry(
+						        cam->inqueue.next,
+						        struct sn9c102_frame_t,
+						        frame );
+					else
+						(*f) = NULL;
+					spin_unlock_irqrestore(&cam->queue_lock
+					                       , lock_flags);
+					DBG(3, "Video frame captured: "
+					       "%lu bytes", (unsigned long)(b))
+
+					if (!(*f))
+						goto resubmit_urb;
+
+				} else if (eof) {
+					(*f)->state = F_ERROR;
+					DBG(3, "Not expected EOF after %lu "
+					       "bytes of image data", 
+					  (unsigned long)((*f)->buf.bytesused))
+				}
+
+				if (sof) /* (1) */
+					goto start_of_frame;
+
+			} else if (eof) {
+				DBG(3, "EOF without SOF")
+				continue;
+
+			} else {
+				PDBGG("Ignoring pointless isochronous frame")
+				continue;
+			}
+
+		} else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) {
+start_of_frame:
+			(*f)->state = F_GRABBING;
+			(*f)->buf.bytesused = 0;
+			len -= (sof - pos);
+			pos = sof;
+			DBG(3, "SOF detected: new video frame")
+			if (len)
+				goto redo;
+
+		} else if ((*f)->state == F_GRABBING) {
+			eof = sn9c102_find_eof_header(pos, len);
+			if (eof && eof < sof)
+				goto end_of_frame; /* (1) */
+			else {
+				DBG(3, "SOF before expected EOF after %lu "
+				       "bytes of image data", 
+				    (unsigned long)((*f)->buf.bytesused))
+				goto start_of_frame;
+			}
+		}
+	}
+
+resubmit_urb:
+	urb->dev = cam->usbdev;
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err < 0 && err != -EPERM) {
+		cam->state |= DEV_MISCONFIGURED;
+		DBG(1, "usb_submit_urb() failed")
+	}
+
+	wake_up_interruptible(&cam->wait_frame);
+}
+
+
+static int sn9c102_start_transfer(struct sn9c102_device* cam)
+{
+	struct usb_device *udev = cam->usbdev;
+	struct urb* urb;
+	const unsigned int wMaxPacketSize[] = {0, 128, 256, 384, 512,
+                                               680, 800, 900, 1023};
+	const unsigned int psz = wMaxPacketSize[SN9C102_ALTERNATE_SETTING];
+	s8 i, j;
+	int err = 0;
+
+	for (i = 0; i < SN9C102_URBS; i++) {
+		cam->transfer_buffer[i] = kmalloc(SN9C102_ISO_PACKETS * psz,
+		                                  GFP_KERNEL);
+		if (!cam->transfer_buffer[i]) {
+			err = -ENOMEM;
+			DBG(1, "Not enough memory")
+			goto free_buffers;
+		}
+	}
+
+	for (i = 0; i < SN9C102_URBS; i++) {
+		urb = usb_alloc_urb(SN9C102_ISO_PACKETS, GFP_KERNEL);
+		cam->urb[i] = urb;
+		if (!urb) {
+			err = -ENOMEM;
+			DBG(1, "usb_alloc_urb() failed")
+			goto free_urbs;
+		}
+		urb->dev = udev;
+		urb->context = cam;
+		urb->pipe = usb_rcvisocpipe(udev, 1);
+		urb->transfer_flags = URB_ISO_ASAP;
+		urb->number_of_packets = SN9C102_ISO_PACKETS;
+		urb->complete = sn9c102_urb_complete;
+		urb->transfer_buffer = cam->transfer_buffer[i];
+		urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS;
+		urb->interval = 1;
+		for (j = 0; j < SN9C102_ISO_PACKETS; j++) {
+			urb->iso_frame_desc[j].offset = psz * j;
+			urb->iso_frame_desc[j].length = psz;
+		}
+	}
+
+	/* Enable video */
+	if (!(cam->reg[0x01] & 0x04)) {
+		err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01);
+		if (err) {
+			err = -EIO;
+			DBG(1, "I/O hardware error")
+			goto free_urbs;
+		}
+	}
+
+	err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING);
+	if (err) {
+		DBG(1, "usb_set_interface() failed")
+		goto free_urbs;
+	}
+
+	cam->frame_current = NULL;
+
+	for (i = 0; i < SN9C102_URBS; i++) {
+		err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+		if (err) {
+			for (j = i-1; j >= 0; j--)
+				usb_kill_urb(cam->urb[j]);
+			DBG(1, "usb_submit_urb() failed, error %d", err)
+			goto free_urbs;
+		}
+	}
+
+	return 0;
+
+free_urbs:
+	for (i = 0; (i < SN9C102_URBS) &&  cam->urb[i]; i++)
+		usb_free_urb(cam->urb[i]);
+
+free_buffers:
+	for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++)
+		kfree(cam->transfer_buffer[i]);
+
+	return err;
+}
+
+
+static int sn9c102_stop_transfer(struct sn9c102_device* cam)
+{
+	struct usb_device *udev = cam->usbdev;
+	s8 i;
+	int err = 0;
+
+	if (cam->state & DEV_DISCONNECTED)
+		return 0;
+
+	for (i = SN9C102_URBS-1; i >= 0; i--) {
+		usb_kill_urb(cam->urb[i]);
+		usb_free_urb(cam->urb[i]);
+		kfree(cam->transfer_buffer[i]);
+	}
+
+	err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
+	if (err)
+		DBG(3, "usb_set_interface() failed")
+
+	return err;
+}
+
+/*****************************************************************************/
+
+static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count)
+{
+	char str[5];
+	char* endp;
+	unsigned long val;
+
+	if (len < 4) {
+		strncpy(str, buff, len);
+		str[len+1] = '\0';
+	} else {
+		strncpy(str, buff, 4);
+		str[4] = '\0';
+	}
+
+	val = simple_strtoul(str, &endp, 0);
+
+	*count = 0;
+	if (val <= 0xff)
+		*count = (ssize_t)(endp - str);
+	if ((*count) && (len == *count+1) && (buff[*count] == '\n'))
+		*count += 1;
+
+	return (u8)val;
+}
+
+/* NOTE 1: being inside one of the following methods implies that the v4l
+           device exists for sure (see kobjects and reference counters)
+   NOTE 2: buffers are PAGE_SIZE long */
+
+static ssize_t sn9c102_show_reg(struct class_device* cd, char* buf)
+{
+	struct sn9c102_device* cam;
+	ssize_t count;
+
+	if (down_interruptible(&sn9c102_sysfs_lock))
+		return -ERESTARTSYS;
+
+	cam = video_get_drvdata(to_video_device(cd));
+	if (!cam) {
+		up(&sn9c102_sysfs_lock);
+		return -ENODEV;
+	}
+
+	count = sprintf(buf, "%u\n", cam->sysfs.reg);
+
+	up(&sn9c102_sysfs_lock);
+
+	return count;
+} 
+
+
+static ssize_t 
+sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len)
+{
+	struct sn9c102_device* cam;
+	u8 index;
+	ssize_t count;
+
+	if (down_interruptible(&sn9c102_sysfs_lock))
+		return -ERESTARTSYS;
+
+	cam = video_get_drvdata(to_video_device(cd));
+	if (!cam) {
+		up(&sn9c102_sysfs_lock);
+		return -ENODEV;
+	}
+
+	index = sn9c102_strtou8(buf, len, &count);
+	if (index > 0x1f || !count) {
+		up(&sn9c102_sysfs_lock);
+		return -EINVAL;
+	}
+
+	cam->sysfs.reg = index;
+
+	DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg)
+	DBG(3, "Written bytes: %zd", count)
+
+	up(&sn9c102_sysfs_lock);
+
+	return count;
+}
+
+
+static ssize_t sn9c102_show_val(struct class_device* cd, char* buf)
+{
+	struct sn9c102_device* cam;
+	ssize_t count;
+	int val;
+
+	if (down_interruptible(&sn9c102_sysfs_lock))
+		return -ERESTARTSYS;
+
+	cam = video_get_drvdata(to_video_device(cd));
+	if (!cam) {
+		up(&sn9c102_sysfs_lock);
+		return -ENODEV;
+	}
+
+	if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) {
+		up(&sn9c102_sysfs_lock);
+		return -EIO;
+	}
+
+	count = sprintf(buf, "%d\n", val);
+
+	DBG(3, "Read bytes: %zd", count)
+
+	up(&sn9c102_sysfs_lock);
+
+	return count;
+} 
+
+
+static ssize_t
+sn9c102_store_val(struct class_device* cd, const char* buf, size_t len)
+{
+	struct sn9c102_device* cam;
+	u8 value;
+	ssize_t count;
+	int err;
+
+	if (down_interruptible(&sn9c102_sysfs_lock))
+		return -ERESTARTSYS;
+
+	cam = video_get_drvdata(to_video_device(cd));
+	if (!cam) {
+		up(&sn9c102_sysfs_lock);
+		return -ENODEV;
+	}
+
+	value = sn9c102_strtou8(buf, len, &count);
+	if (!count) {
+		up(&sn9c102_sysfs_lock);
+		return -EINVAL;
+	}
+
+	err = sn9c102_write_reg(cam, value, cam->sysfs.reg);
+	if (err) {
+		up(&sn9c102_sysfs_lock);
+		return -EIO;
+	}
+
+	DBG(2, "Written SN9C10X reg. 0x%02X, val. 0x%02X",
+	    cam->sysfs.reg, value)
+	DBG(3, "Written bytes: %zd", count)
+
+	up(&sn9c102_sysfs_lock);
+
+	return count;
+}
+
+
+static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf)
+{
+	struct sn9c102_device* cam;
+	ssize_t count;
+
+	if (down_interruptible(&sn9c102_sysfs_lock))
+		return -ERESTARTSYS;
+
+	cam = video_get_drvdata(to_video_device(cd));
+	if (!cam) {
+		up(&sn9c102_sysfs_lock);
+		return -ENODEV;
+	}
+
+	count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg);
+
+	DBG(3, "Read bytes: %zd", count)
+
+	up(&sn9c102_sysfs_lock);
+
+	return count;
+} 
+
+
+static ssize_t 
+sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
+{
+	struct sn9c102_device* cam;
+	u8 index;
+	ssize_t count;
+
+	if (down_interruptible(&sn9c102_sysfs_lock))
+		return -ERESTARTSYS;
+
+	cam = video_get_drvdata(to_video_device(cd));
+	if (!cam) {
+		up(&sn9c102_sysfs_lock);
+		return -ENODEV;
+	}
+
+	index = sn9c102_strtou8(buf, len, &count);
+	if (!count) {
+		up(&sn9c102_sysfs_lock);
+		return -EINVAL;
+	}
+
+	cam->sysfs.i2c_reg = index;
+
+	DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg)
+	DBG(3, "Written bytes: %zd", count)
+
+	up(&sn9c102_sysfs_lock);
+
+	return count;
+}
+
+
+static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf)
+{
+	struct sn9c102_device* cam;
+	ssize_t count;
+	int val;
+
+	if (down_interruptible(&sn9c102_sysfs_lock))
+		return -ERESTARTSYS;
+
+	cam = video_get_drvdata(to_video_device(cd));
+	if (!cam) {
+		up(&sn9c102_sysfs_lock);
+		return -ENODEV;
+	}
+
+	if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
+		up(&sn9c102_sysfs_lock);
+		return -EIO;
+	}
+
+	count = sprintf(buf, "%d\n", val);
+
+	DBG(3, "Read bytes: %zd", count)
+
+	up(&sn9c102_sysfs_lock);
+
+	return count;
+} 
+
+
+static ssize_t
+sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
+{
+	struct sn9c102_device* cam;
+	u8 value;
+	ssize_t count;
+	int err;
+
+	if (down_interruptible(&sn9c102_sysfs_lock))
+		return -ERESTARTSYS;
+
+	cam = video_get_drvdata(to_video_device(cd));
+	if (!cam) {
+		up(&sn9c102_sysfs_lock);
+		return -ENODEV;
+	}
+
+	value = sn9c102_strtou8(buf, len, &count);
+	if (!count) {
+		up(&sn9c102_sysfs_lock);
+		return -EINVAL;
+	}
+
+	err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value);
+	if (err) {
+		up(&sn9c102_sysfs_lock);
+		return -EIO;
+	}
+
+	DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X",
+	    cam->sysfs.i2c_reg, value)
+	DBG(3, "Written bytes: %zd", count)
+
+	up(&sn9c102_sysfs_lock);
+
+	return count;
+}
+
+
+static ssize_t
+sn9c102_store_redblue(struct class_device* cd, const char* buf, size_t len)
+{
+	ssize_t res = 0;
+	u8 value;
+	ssize_t count;
+
+	value = sn9c102_strtou8(buf, len, &count);
+	if (!count)
+		return -EINVAL;
+
+	if ((res = sn9c102_store_reg(cd, "0x10", 4)) >= 0)
+		res = sn9c102_store_val(cd, buf, len);
+
+	return res;
+}
+
+
+static ssize_t
+sn9c102_store_green(struct class_device* cd, const char* buf, size_t len)
+{
+	ssize_t res = 0;
+	u8 value;
+	ssize_t count;
+
+	value = sn9c102_strtou8(buf, len, &count);
+	if (!count || value > 0x0f)
+		return -EINVAL;
+
+	if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0)
+		res = sn9c102_store_val(cd, buf, len);
+
+	return res;
+}
+
+
+static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR,
+                         sn9c102_show_reg, sn9c102_store_reg);
+static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR,
+                         sn9c102_show_val, sn9c102_store_val);
+static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
+                         sn9c102_show_i2c_reg, sn9c102_store_i2c_reg);
+static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
+                         sn9c102_show_i2c_val, sn9c102_store_i2c_val);
+static CLASS_DEVICE_ATTR(redblue, S_IWUGO, NULL, sn9c102_store_redblue);
+static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green);
+
+
+static void sn9c102_create_sysfs(struct sn9c102_device* cam)
+{
+	struct video_device *v4ldev = cam->v4ldev;
+
+	video_device_create_file(v4ldev, &class_device_attr_reg);
+	video_device_create_file(v4ldev, &class_device_attr_val);
+	video_device_create_file(v4ldev, &class_device_attr_redblue);
+	video_device_create_file(v4ldev, &class_device_attr_green);
+	if (cam->sensor->slave_write_id && cam->sensor->slave_read_id) {
+		video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
+		video_device_create_file(v4ldev, &class_device_attr_i2c_val);
+	}
+}
+
+/*****************************************************************************/
+
+static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale)
+{
+	u8 r = 0;
+	int err = 0;
+
+	if (scale == 1)
+		r = cam->reg[0x18] & 0xcf;
+	else if (scale == 2) {
+		r = cam->reg[0x18] & 0xcf;
+		r |= 0x10;
+	} else if (scale == 4)
+		r = cam->reg[0x18] | 0x20;
+
+	err += sn9c102_write_reg(cam, r, 0x18);
+	if (err)
+		return -EIO;
+
+	PDBGG("Scaling factor: %u", scale)
+
+	return 0;
+}
+
+
+static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
+{
+	struct sn9c102_sensor* s = cam->sensor;
+	u8 h_start = (u8)(rect->left - s->cropcap.bounds.left),
+	   v_start = (u8)(rect->top - s->cropcap.bounds.top),
+	   h_size = (u8)(rect->width / 16),
+	   v_size = (u8)(rect->height / 16),
+	   ae_strx = 0x00,
+	   ae_stry = 0x00,
+	   ae_endx = h_size / 2,
+	   ae_endy = v_size / 2;
+	int err = 0;
+
+	/* These are a sort of stroboscopic signal for some sensors */
+	err += sn9c102_write_reg(cam, h_size, 0x1a);
+	err += sn9c102_write_reg(cam, v_size, 0x1b);
+
+	err += sn9c102_write_reg(cam, h_start, 0x12);
+	err += sn9c102_write_reg(cam, v_start, 0x13);
+	err += sn9c102_write_reg(cam, h_size, 0x15);
+	err += sn9c102_write_reg(cam, v_size, 0x16);
+	err += sn9c102_write_reg(cam, ae_strx, 0x1c);
+	err += sn9c102_write_reg(cam, ae_stry, 0x1d);
+	err += sn9c102_write_reg(cam, ae_endx, 0x1e);
+	err += sn9c102_write_reg(cam, ae_endy, 0x1f);
+	if (err)
+		return -EIO;
+
+	PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size "
+	      "%u %u %u %u %u %u", h_start, v_start, h_size, v_size, ho_size,
+	      vo_size)
+
+	return 0;
+}
+
+
+static int sn9c102_init(struct sn9c102_device* cam)
+{
+	struct sn9c102_sensor* s = cam->sensor;
+	struct v4l2_control ctrl;
+	struct v4l2_queryctrl *qctrl;
+	struct v4l2_rect* rect;
+	u8 i = 0, n = 0;
+	int err = 0;
+
+	if (!(cam->state & DEV_INITIALIZED)) {
+		init_waitqueue_head(&cam->open);
+		qctrl = s->qctrl;
+		rect = &(s->cropcap.defrect);
+	} else { /* use current values */
+		qctrl = s->_qctrl;
+		rect = &(s->_rect);
+	}
+
+	err += sn9c102_set_scale(cam, rect->width / s->pix_format.width);
+	err += sn9c102_set_crop(cam, rect);
+	if (err)
+		return err;
+
+	if (s->init) {
+		err = s->init(cam);
+		if (err) {
+			DBG(3, "Sensor initialization failed")
+			return err;
+		}
+	}
+
+	if (s->set_crop)
+		if ((err = s->set_crop(cam, rect))) {
+			DBG(3, "set_crop() failed")
+			return err;
+		}
+
+	if (s->set_ctrl) {
+		n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
+		for (i = 0; i < n; i++)
+			if (s->qctrl[i].id != 0 && 
+			    !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) {
+				ctrl.id = s->qctrl[i].id;
+				ctrl.value = qctrl[i].default_value;
+				err = s->set_ctrl(cam, &ctrl);
+				if (err) {
+					DBG(3, "Set control failed")
+					return err;
+				}
+			}
+	}
+
+	if (!(cam->state & DEV_INITIALIZED)) {
+		init_MUTEX(&cam->fileop_sem);
+		spin_lock_init(&cam->queue_lock);
+		init_waitqueue_head(&cam->wait_frame);
+		init_waitqueue_head(&cam->wait_stream);
+		memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
+		memcpy(&(s->_rect), &(s->cropcap.defrect), 
+		       sizeof(struct v4l2_rect));
+		cam->state |= DEV_INITIALIZED;
+	}
+
+	DBG(2, "Initialization succeeded")
+	return 0;
+}
+
+
+static void sn9c102_release_resources(struct sn9c102_device* cam)
+{
+	down(&sn9c102_sysfs_lock);
+
+	DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor)
+	video_set_drvdata(cam->v4ldev, NULL);
+	video_unregister_device(cam->v4ldev);
+
+	up(&sn9c102_sysfs_lock);
+
+	kfree(cam->control_buffer);
+}
+
+/*****************************************************************************/
+
+static int sn9c102_open(struct inode* inode, struct file* filp)
+{
+	struct sn9c102_device* cam;
+	int err = 0;
+
+	/* This the only safe way to prevent race conditions with disconnect */
+	if (!down_read_trylock(&sn9c102_disconnect))
+		return -ERESTARTSYS;
+
+	cam = video_get_drvdata(video_devdata(filp));
+
+	if (down_interruptible(&cam->dev_sem)) {
+		up_read(&sn9c102_disconnect);
+		return -ERESTARTSYS;
+	}
+
+	if (cam->users) {
+		DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor)
+		if ((filp->f_flags & O_NONBLOCK) ||
+		    (filp->f_flags & O_NDELAY)) {
+			err = -EWOULDBLOCK;
+			goto out;
+		}
+		up(&cam->dev_sem);
+		err = wait_event_interruptible_exclusive(cam->open,
+		                                  cam->state & DEV_DISCONNECTED
+		                                         || !cam->users);
+		if (err) {
+			up_read(&sn9c102_disconnect);
+			return err;
+		}
+		if (cam->state & DEV_DISCONNECTED) {
+			up_read(&sn9c102_disconnect);
+			return -ENODEV;
+		}
+		down(&cam->dev_sem);
+	}
+
+
+	if (cam->state & DEV_MISCONFIGURED) {
+		err = sn9c102_init(cam);
+		if (err) {
+			DBG(1, "Initialization failed again. "
+			       "I will retry on next open().")
+			goto out;
+		}
+		cam->state &= ~DEV_MISCONFIGURED;
+	}
+
+	if ((err = sn9c102_start_transfer(cam)))
+		goto out;
+
+	filp->private_data = cam;
+	cam->users++;
+	cam->io = IO_NONE;
+	cam->stream = STREAM_OFF;
+	cam->nbuffers = 0;
+	cam->frame_count = 0;
+	sn9c102_empty_framequeues(cam);
+
+	DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor)
+
+out:
+	up(&cam->dev_sem);
+	up_read(&sn9c102_disconnect);
+	return err;
+}
+
+
+static int sn9c102_release(struct inode* inode, struct file* filp)
+{
+	struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+
+	down(&cam->dev_sem); /* prevent disconnect() to be called */
+
+	sn9c102_stop_transfer(cam);
+
+	sn9c102_release_buffers(cam);
+
+	if (cam->state & DEV_DISCONNECTED) {
+		sn9c102_release_resources(cam);
+		up(&cam->dev_sem);
+		kfree(cam);
+		return 0;
+	}
+
+	cam->users--;
+	wake_up_interruptible_nr(&cam->open, 1);
+
+	DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor)
+
+	up(&cam->dev_sem);
+
+	return 0;
+}
+
+
+static ssize_t
+sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
+{
+	struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+	struct sn9c102_frame_t* f, * i;
+	unsigned long lock_flags;
+	int err = 0;
+
+	if (down_interruptible(&cam->fileop_sem))
+		return -ERESTARTSYS;
+
+	if (cam->state & DEV_DISCONNECTED) {
+		DBG(1, "Device not present")
+		up(&cam->fileop_sem);
+		return -ENODEV;
+	}
+
+	if (cam->state & DEV_MISCONFIGURED) {
+		DBG(1, "The camera is misconfigured. Close and open it again.")
+		up(&cam->fileop_sem);
+		return -EIO;
+	}
+
+	if (cam->io == IO_MMAP) {
+		DBG(3, "Close and open the device again to choose "
+		       "the read method")
+		up(&cam->fileop_sem);
+		return -EINVAL;
+	}
+
+	if (cam->io == IO_NONE) {
+		if (!sn9c102_request_buffers(cam, 2)) {
+			DBG(1, "read() failed, not enough memory")
+			up(&cam->fileop_sem);
+			return -ENOMEM;
+		}
+		cam->io = IO_READ;
+		cam->stream = STREAM_ON;
+		sn9c102_queue_unusedframes(cam);
+	}
+
+	if (!count) {
+		up(&cam->fileop_sem);
+		return 0;
+	}
+
+	if (list_empty(&cam->outqueue)) {
+		if (filp->f_flags & O_NONBLOCK) {
+			up(&cam->fileop_sem);
+			return -EAGAIN;
+		}
+		err = wait_event_interruptible
+		      ( cam->wait_frame, 
+		        (!list_empty(&cam->outqueue)) ||
+		        (cam->state & DEV_DISCONNECTED) );
+		if (err) {
+			up(&cam->fileop_sem);
+			return err;
+		}
+		if (cam->state & DEV_DISCONNECTED) {
+			up(&cam->fileop_sem);
+			return -ENODEV;
+		}
+	}
+
+	f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame);
+
+	spin_lock_irqsave(&cam->queue_lock, lock_flags);
+	list_for_each_entry(i, &cam->outqueue, frame)
+		i->state = F_UNUSED;
+	INIT_LIST_HEAD(&cam->outqueue);
+	spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+	sn9c102_queue_unusedframes(cam);
+
+	if (count > f->buf.length)
+		count = f->buf.length;
+
+	if (copy_to_user(buf, f->bufmem, count)) {
+		up(&cam->fileop_sem);
+		return -EFAULT;
+	}
+	*f_pos += count;
+
+	PDBGG("Frame #%lu, bytes read: %zu", (unsigned long)f->buf.index,count)
+
+	up(&cam->fileop_sem);
+
+	return count;
+}
+
+
+static unsigned int sn9c102_poll(struct file *filp, poll_table *wait)
+{
+	struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+	unsigned int mask = 0;
+
+	if (down_interruptible(&cam->fileop_sem))
+		return POLLERR;
+
+	if (cam->state & DEV_DISCONNECTED) {
+		DBG(1, "Device not present")
+		goto error;
+	}
+
+	if (cam->state & DEV_MISCONFIGURED) {
+		DBG(1, "The camera is misconfigured. Close and open it again.")
+		goto error;
+	}
+
+	if (cam->io == IO_NONE) {
+		if (!sn9c102_request_buffers(cam, 2)) {
+			DBG(1, "poll() failed, not enough memory")
+			goto error;
+		}
+		cam->io = IO_READ;
+		cam->stream = STREAM_ON;
+	}
+
+	if (cam->io == IO_READ)
+		sn9c102_queue_unusedframes(cam);
+
+	poll_wait(filp, &cam->wait_frame, wait);
+
+	if (!list_empty(&cam->outqueue))
+		mask |= POLLIN | POLLRDNORM;
+
+	up(&cam->fileop_sem);
+
+	return mask;
+
+error:
+	up(&cam->fileop_sem);
+	return POLLERR;
+}
+
+
+static void sn9c102_vm_open(struct vm_area_struct* vma)
+{
+	struct sn9c102_frame_t* f = vma->vm_private_data;
+	f->vma_use_count++;
+}
+
+
+static void sn9c102_vm_close(struct vm_area_struct* vma)
+{
+	/* NOTE: buffers are not freed here */
+	struct sn9c102_frame_t* f = vma->vm_private_data;
+	f->vma_use_count--;
+}
+
+
+static struct vm_operations_struct sn9c102_vm_ops = {
+	.open = sn9c102_vm_open,
+	.close = sn9c102_vm_close,
+};
+
+
+static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+	struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+	unsigned long size = vma->vm_end - vma->vm_start,
+	              start = vma->vm_start,
+	              pos,
+	              page;
+	u32 i;
+
+	if (down_interruptible(&cam->fileop_sem))
+		return -ERESTARTSYS;
+
+	if (cam->state & DEV_DISCONNECTED) {
+		DBG(1, "Device not present")
+		up(&cam->fileop_sem);
+		return -ENODEV;
+	}
+
+	if (cam->state & DEV_MISCONFIGURED) {
+		DBG(1, "The camera is misconfigured. Close and open it again.")
+		up(&cam->fileop_sem);
+		return -EIO;
+	}
+
+	if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
+	    size != PAGE_ALIGN(cam->frame[0].buf.length)) {
+		up(&cam->fileop_sem);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < cam->nbuffers; i++) {
+		if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff)
+			break;
+	}
+	if (i == cam->nbuffers) {
+		up(&cam->fileop_sem);
+		return -EINVAL;
+	}
+
+	pos = (unsigned long)cam->frame[i].bufmem;
+	while (size > 0) { /* size is page-aligned */
+		page = kvirt_to_pa(pos);
+		if (remap_page_range(vma, start, page, PAGE_SIZE, 
+		                     vma->vm_page_prot)) {
+			up(&cam->fileop_sem);
+			return -EAGAIN;
+		}
+		start += PAGE_SIZE;
+		pos += PAGE_SIZE;
+		size -= PAGE_SIZE;
+	}
+
+	vma->vm_ops = &sn9c102_vm_ops;
+	vma->vm_flags &= ~VM_IO; /* not I/O memory */
+	vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+	vma->vm_private_data = &cam->frame[i];
+
+	sn9c102_vm_open(vma);
+
+	up(&cam->fileop_sem);
+
+	return 0;
+}
+
+
+static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
+                              unsigned int cmd, void __user * arg)
+{
+	struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+
+	switch (cmd) {
+
+	case VIDIOC_QUERYCAP:
+	{
+		struct v4l2_capability cap = {
+			.driver = "sn9c102",
+			.version = SN9C102_MODULE_VERSION_CODE,
+			.capabilities = V4L2_CAP_VIDEO_CAPTURE | 
+			                V4L2_CAP_READWRITE |
+		 	                V4L2_CAP_STREAMING,
+		};
+
+		strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
+		strlcpy(cap.bus_info, cam->dev.bus_id, sizeof(cap.bus_info));
+
+		if (copy_to_user(arg, &cap, sizeof(cap)))
+			return -EFAULT;
+
+		return 0;
+	}
+
+	case VIDIOC_ENUMINPUT:
+	{
+		struct v4l2_input i;
+
+		if (copy_from_user(&i, arg, sizeof(i)))
+			return -EFAULT;
+
+		if (i.index)
+			return -EINVAL;
+
+		memset(&i, 0, sizeof(i));
+		strcpy(i.name, "USB");
+
+		if (copy_to_user(arg, &i, sizeof(i)))
+			return -EFAULT;
+
+		return 0;
+	}
+
+	case VIDIOC_G_INPUT:
+	case VIDIOC_S_INPUT:
+	{
+		int index;
+
+		if (copy_from_user(&index, arg, sizeof(index)))
+			return -EFAULT;
+
+		if (index != 0)
+			return -EINVAL;
+
+		return 0;
+	}
+
+	case VIDIOC_QUERYCTRL:
+	{
+		struct sn9c102_sensor* s = cam->sensor;
+		struct v4l2_queryctrl qc;
+		u8 i, n;
+
+		if (copy_from_user(&qc, arg, sizeof(qc)))
+			return -EFAULT;
+
+		n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
+		for (i = 0; i < n; i++)
+			if (qc.id && qc.id == s->qctrl[i].id) {
+				memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
+				if (copy_to_user(arg, &qc, sizeof(qc)))
+					return -EFAULT;
+				return 0;
+			}
+
+		return -EINVAL;
+	}
+
+	case VIDIOC_G_CTRL:
+	{
+		struct sn9c102_sensor* s = cam->sensor;
+		struct v4l2_control ctrl;
+		int err = 0;
+
+		if (!s->get_ctrl)
+			return -EINVAL;
+
+		if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+			return -EFAULT;
+
+		err = s->get_ctrl(cam, &ctrl);
+
+		if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
+			return -EFAULT;
+
+		return err;
+	}
+
+	case VIDIOC_S_CTRL:
+	{
+		struct sn9c102_sensor* s = cam->sensor;
+		struct v4l2_control ctrl;
+		u8 i, n;
+		int err = 0;
+
+		if (!s->set_ctrl)
+			return -EINVAL;
+
+		if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+			return -EFAULT;
+
+		if ((err = s->set_ctrl(cam, &ctrl)))
+			return err;
+
+		n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
+		for (i = 0; i < n; i++)
+			if (ctrl.id == s->qctrl[i].id) {
+				s->_qctrl[i].default_value = ctrl.value;
+				break;
+			}
+
+		return 0;
+	}
+
+	case VIDIOC_CROPCAP:
+	{
+		struct v4l2_cropcap* cc = &(cam->sensor->cropcap);
+
+		cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		cc->pixelaspect.numerator = 1;
+		cc->pixelaspect.denominator = 1;
+
+		if (copy_to_user(arg, cc, sizeof(*cc)))
+			return -EFAULT;
+
+		return 0;
+	}
+
+	case VIDIOC_G_CROP:
+	{
+		struct sn9c102_sensor* s = cam->sensor;
+		struct v4l2_crop crop = {
+			.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+		};
+
+		memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
+
+		if (copy_to_user(arg, &crop, sizeof(crop)))
+			return -EFAULT;
+
+		return 0;
+	}
+
+	case VIDIOC_S_CROP:
+	{
+		struct sn9c102_sensor* s = cam->sensor;
+		struct v4l2_crop crop;
+		struct v4l2_rect* rect;
+		struct v4l2_rect* bounds = &(s->cropcap.bounds);
+		struct v4l2_pix_format* pix_format = &(s->pix_format);
+		u8 scale;
+		const enum sn9c102_stream_state stream = cam->stream;
+		const u32 nbuffers = cam->nbuffers;
+		u32 i;
+		int err = 0;
+
+		if (copy_from_user(&crop, arg, sizeof(crop)))
+			return -EFAULT;
+
+		rect = &(crop.c);
+
+		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 (rect->width < 16)
+			rect->width = 16;
+		if (rect->height < 16)
+			rect->height = 16;
+		if (rect->width > bounds->width)
+			rect->width = bounds->width;
+		if (rect->height > bounds->height)
+			rect->height = bounds->height;
+		if (rect->left < bounds->left)
+			rect->left = bounds->left;
+		if (rect->top < bounds->top)
+			rect->top = bounds->top;
+		if (rect->left + rect->width > bounds->left + bounds->width)
+			rect->left = bounds->left+bounds->width - rect->width;
+		if (rect->top + rect->height > bounds->top + bounds->height)
+			rect->top = bounds->top+bounds->height - rect->height;
+
+		rect->width &= ~15L;
+		rect->height &= ~15L;
+
+		{ /* calculate the scaling factor */
+			u32 a, b;
+			a = rect->width * rect->height;
+			b = pix_format->width * pix_format->height;
+			scale = b ? (u8)((a / b) <= 1 ? 1 : ((a / b) == 3 ? 2 :
+			            ((a / b) > 4 ? 4 : (a / b)))) : 1;
+		}
+
+		if (cam->stream == STREAM_ON) {
+			cam->stream = STREAM_INTERRUPT;
+			err = wait_event_interruptible
+			      ( cam->wait_stream, 
+			        (cam->stream == STREAM_OFF) ||
+			        (cam->state & DEV_DISCONNECTED) );
+			if (err) {
+				cam->state |= DEV_MISCONFIGURED;
+				DBG(1, "The camera is misconfigured. To use "
+				       "it, close and open /dev/video%d "
+				       "again.", cam->v4ldev->minor)
+				return err;
+			}
+			if (cam->state & DEV_DISCONNECTED)
+				return -ENODEV;
+		}
+
+		if (copy_to_user(arg, &crop, sizeof(crop))) {
+			cam->stream = stream;
+			return -EFAULT;
+		}
+
+		sn9c102_release_buffers(cam);
+
+		err = sn9c102_set_crop(cam, rect);
+		if (s->set_crop)
+			err += s->set_crop(cam, rect);
+		err += sn9c102_set_scale(cam, scale);
+
+		if (err) { /* atomic, no rollback in ioctl() */
+			cam->state |= DEV_MISCONFIGURED;
+			DBG(1, "VIDIOC_S_CROP failed because of hardware "
+			       "problems. To use the camera, close and open "
+			       "/dev/video%d again.", cam->v4ldev->minor)
+			return err;
+		}
+
+		s->pix_format.width = rect->width/scale;
+		s->pix_format.height = rect->height/scale;
+		memcpy(&(s->_rect), rect, sizeof(*rect));
+
+		if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) {
+			cam->state |= DEV_MISCONFIGURED;
+			DBG(1, "VIDIOC_S_CROP failed because of not enough "
+			       "memory. To use the camera, close and open "
+			       "/dev/video%d again.", cam->v4ldev->minor)
+			return -ENOMEM;
+		}
+
+		cam->stream = stream;
+
+		return 0;
+	}
+
+	case VIDIOC_ENUM_FMT:
+	{
+		struct sn9c102_sensor* s = cam->sensor;
+		struct v4l2_fmtdesc fmtd;
+
+		if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
+			return -EFAULT;
+
+		if (fmtd.index != 0)
+			return -EINVAL;
+
+		memset(&fmtd, 0, sizeof(fmtd));
+
+		fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		strcpy(fmtd.description, "bayer rgb");
+		fmtd.pixelformat = s->pix_format.pixelformat;
+
+		if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
+			return -EFAULT;
+
+		return 0;
+	}
+
+	case VIDIOC_G_FMT:
+	{
+		struct v4l2_format format;
+		struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format);
+
+		if (copy_from_user(&format, arg, sizeof(format)))
+			return -EFAULT;
+
+		if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+
+		pfmt->bytesperline = (pfmt->width * pfmt->priv) / 8;
+		pfmt->sizeimage = pfmt->height * pfmt->bytesperline;
+		pfmt->field = V4L2_FIELD_NONE;
+		memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
+
+		if (copy_to_user(arg, &format, sizeof(format)))
+			return -EFAULT;
+
+		return 0;
+	}
+
+	case VIDIOC_TRY_FMT:
+	case VIDIOC_S_FMT:
+	{
+		struct sn9c102_sensor* s = cam->sensor;
+		struct v4l2_format format;
+		struct v4l2_pix_format* pix;
+		struct v4l2_pix_format* pfmt = &(s->pix_format);
+		struct v4l2_rect* bounds = &(s->cropcap.bounds);
+		struct v4l2_rect rect;
+		u8 scale;
+		const enum sn9c102_stream_state stream = cam->stream;
+		const u32 nbuffers = cam->nbuffers;
+		u32 i;
+		int err = 0;
+
+		if (copy_from_user(&format, arg, sizeof(format)))
+			return -EFAULT;
+
+		pix = &(format.fmt.pix);
+
+		if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+
+		memcpy(&rect, &(s->_rect), sizeof(rect));
+
+		{ /* calculate the scaling factor */
+			u32 a, b;
+			a = rect.width * rect.height;
+			b = pix->width * pix->height;
+			scale = b ? (u8)((a / b) <= 1 ? 1 : ((a / b) == 3 ? 2 :
+			            ((a / b) > 4 ? 4 : (a / b)))) : 1;
+		}
+
+		rect.width = scale * pix->width;
+		rect.height = scale * pix->height;
+
+		if (rect.width < 16)
+			rect.width = 16;
+		if (rect.height < 16)
+			rect.height = 16;
+		if (rect.width > bounds->left + bounds->width - rect.left)
+			rect.width = bounds->left+bounds->width - rect.left;
+		if (rect.height > bounds->top + bounds->height - rect.top)
+			rect.height = bounds->top + bounds->height - rect.top;
+
+		rect.width &= ~15L;
+		rect.height &= ~15L;
+
+		pix->width = rect.width / scale;
+		pix->height = rect.height / scale;
+
+		pix->pixelformat = pfmt->pixelformat;
+		pix->priv = pfmt->priv; /* bpp */
+		pix->colorspace = pfmt->colorspace;
+		pix->bytesperline = (pix->width * pix->priv) / 8;
+		pix->sizeimage = pix->height * pix->bytesperline;
+		pix->field = V4L2_FIELD_NONE;
+
+		if (cmd == VIDIOC_TRY_FMT)
+			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->stream == STREAM_ON) {
+			cam->stream = STREAM_INTERRUPT;
+			err = wait_event_interruptible
+			      ( cam->wait_stream, 
+			        (cam->stream == STREAM_OFF) ||
+			        (cam->state & DEV_DISCONNECTED) );
+			if (err) {
+				cam->state |= DEV_MISCONFIGURED;
+				DBG(1, "The camera is misconfigured. To use "
+				       "it, close and open /dev/video%d "
+				       "again.", cam->v4ldev->minor)
+				return err;
+			}
+			if (cam->state & DEV_DISCONNECTED)
+				return -ENODEV;
+		}
+
+		if (copy_to_user(arg, &format, sizeof(format))) {
+			cam->stream = stream;
+			return -EFAULT;
+		}
+
+		sn9c102_release_buffers(cam);
+
+		err = sn9c102_set_crop(cam, &rect);
+		if (s->set_crop)
+			err += s->set_crop(cam, &rect);
+		err += sn9c102_set_scale(cam, scale);
+
+		if (err) { /* atomic, no rollback in ioctl() */
+			cam->state |= DEV_MISCONFIGURED;
+			DBG(1, "VIDIOC_S_FMT failed because of hardware "
+			       "problems. To use the camera, close and open "
+			       "/dev/video%d again.", cam->v4ldev->minor)
+			return err;
+		}
+
+		memcpy(pfmt, pix, sizeof(*pix));
+		memcpy(&(s->_rect), &rect, sizeof(rect));
+
+		if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) {
+			cam->state |= DEV_MISCONFIGURED;
+			DBG(1, "VIDIOC_S_FMT failed because of not enough "
+			       "memory. To use the camera, close and open "
+			       "/dev/video%d again.", cam->v4ldev->minor)
+			return -ENOMEM;
+		}
+
+		cam->stream = stream;
+
+		return 0;
+	}
+
+	case VIDIOC_REQBUFS:
+	{
+		struct v4l2_requestbuffers rb;
+		u32 i;
+		int err;
+
+		if (copy_from_user(&rb, arg, sizeof(rb)))
+			return -EFAULT;
+
+		if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+		    rb.memory != V4L2_MEMORY_MMAP)
+			return -EINVAL;
+
+		if (cam->io == IO_READ) {
+			DBG(3, "Close and open the device again to choose "
+			       "the mmap I/O method")
+			return -EINVAL;
+		}
+
+		for (i = 0; i < cam->nbuffers; i++)
+			if (cam->frame[i].vma_use_count) {
+				DBG(3, "VIDIOC_REQBUFS failed. "
+				       "Previous buffers are still mapped.")
+				return -EINVAL;
+			}
+
+		if (cam->stream == STREAM_ON) {
+			cam->stream = STREAM_INTERRUPT;
+			err = wait_event_interruptible
+			      ( cam->wait_stream, 
+			        (cam->stream == STREAM_OFF) ||
+			        (cam->state & DEV_DISCONNECTED) );
+			if (err) {
+				cam->state |= DEV_MISCONFIGURED;
+				DBG(1, "The camera is misconfigured. To use "
+				       "it, close and open /dev/video%d "
+				       "again.", cam->v4ldev->minor)
+				return err;
+			}
+			if (cam->state & DEV_DISCONNECTED)
+				return -ENODEV;
+		}
+
+		sn9c102_empty_framequeues(cam);
+
+		sn9c102_release_buffers(cam);
+		if (rb.count)
+			rb.count = sn9c102_request_buffers(cam, rb.count);
+
+		if (copy_to_user(arg, &rb, sizeof(rb))) {
+			sn9c102_release_buffers(cam);
+			cam->io = IO_NONE;
+			return -EFAULT;
+		}
+
+		cam->io = rb.count ? IO_MMAP : IO_NONE;
+
+		return 0;
+	}
+
+	case VIDIOC_QUERYBUF:
+	{
+		struct v4l2_buffer b;
+
+		if (copy_from_user(&b, arg, sizeof(b)))
+			return -EFAULT;
+
+		if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+		    b.index >= cam->nbuffers || cam->io != IO_MMAP)
+			return -EINVAL;
+
+		memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
+
+		if (cam->frame[b.index].vma_use_count)
+			b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+		if (cam->frame[b.index].state == F_DONE)
+			b.flags |= V4L2_BUF_FLAG_DONE;
+		else if (cam->frame[b.index].state != F_UNUSED)
+			b.flags |= V4L2_BUF_FLAG_QUEUED;
+
+		if (copy_to_user(arg, &b, sizeof(b)))
+			return -EFAULT;
+
+		return 0;
+	}
+
+	case VIDIOC_QBUF:
+	{
+		struct v4l2_buffer b;
+		unsigned long lock_flags;
+
+		if (copy_from_user(&b, arg, sizeof(b)))
+			return -EFAULT;
+
+		if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+		    b.index >= cam->nbuffers || cam->io != IO_MMAP)
+			return -EINVAL;
+
+		if (cam->frame[b.index].state != F_UNUSED)
+			return -EINVAL;
+
+		cam->frame[b.index].state = F_QUEUED;
+
+		spin_lock_irqsave(&cam->queue_lock, lock_flags);
+		list_add_tail(&cam->frame[b.index].frame, &cam->inqueue);
+		spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+		PDBGG("Frame #%lu queued", (unsigned long)b.index)
+
+		return 0;
+	}
+
+	case VIDIOC_DQBUF:
+	{
+		struct v4l2_buffer b;
+		struct sn9c102_frame_t *f;
+		unsigned long lock_flags;
+		int err = 0;
+
+		if (copy_from_user(&b, arg, sizeof(b)))
+			return -EFAULT;
+
+		if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP)
+			return -EINVAL;
+
+		if (list_empty(&cam->outqueue)) {
+			if (cam->stream == STREAM_OFF)
+				return -EINVAL;
+			if (filp->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+			err = wait_event_interruptible
+			      ( cam->wait_frame, 
+			        (!list_empty(&cam->outqueue)) ||
+			        (cam->state & DEV_DISCONNECTED) );
+			if (err)
+				return err;
+			if (cam->state & DEV_DISCONNECTED)
+				return -ENODEV;
+		}
+
+		spin_lock_irqsave(&cam->queue_lock, lock_flags);
+		f = list_entry(cam->outqueue.next, struct sn9c102_frame_t,
+		               frame);
+		list_del(&cam->outqueue);
+		spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+		f->state = F_UNUSED;
+
+		memcpy(&b, &f->buf, sizeof(b));
+		if (f->vma_use_count)
+			b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+		if (copy_to_user(arg, &b, sizeof(b)))
+			return -EFAULT;
+
+		PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index)
+
+		return 0;
+	}
+
+	case VIDIOC_STREAMON:
+	{
+		int type;
+
+		if (copy_from_user(&type, arg, sizeof(type)))
+			return -EFAULT;
+
+		if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+			return -EINVAL;
+
+		if (list_empty(&cam->inqueue))
+			return -EINVAL;
+
+		cam->stream = STREAM_ON;
+
+		DBG(3, "Stream on")
+
+		return 0;
+	}
+
+	case VIDIOC_STREAMOFF:
+	{
+		int type, err;
+
+		if (copy_from_user(&type, arg, sizeof(type)))
+			return -EFAULT;
+
+		if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+			return -EINVAL;
+
+		if (cam->stream == STREAM_ON) {
+			cam->stream = STREAM_INTERRUPT;
+			err = wait_event_interruptible
+			      ( cam->wait_stream, 
+			        (cam->stream == STREAM_OFF) ||
+			        (cam->state & DEV_DISCONNECTED) );
+			if (err) {
+				cam->state |= DEV_MISCONFIGURED;
+				DBG(1, "The camera is misconfigured. To use "
+				       "it, close and open /dev/video%d "
+				       "again.", cam->v4ldev->minor)
+				return err;
+			}
+			if (cam->state & DEV_DISCONNECTED)
+				return -ENODEV;
+		}
+
+		sn9c102_empty_framequeues(cam);
+
+		DBG(3, "Stream off")
+
+		return 0;
+	}
+
+	case VIDIOC_G_STD:
+	case VIDIOC_S_STD:
+	case VIDIOC_QUERYSTD:
+	case VIDIOC_ENUMSTD:
+	case VIDIOC_QUERYMENU:
+	case VIDIOC_G_PARM:
+	case VIDIOC_S_PARM:
+		return -EINVAL;
+
+	default:
+		return -EINVAL;
+
+	}
+}
+
+
+static int sn9c102_ioctl(struct inode* inode, struct file* filp,
+                         unsigned int cmd, unsigned long arg)
+{
+	struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+	int err = 0;
+
+	if (down_interruptible(&cam->fileop_sem))
+		return -ERESTARTSYS;
+
+	if (cam->state & DEV_DISCONNECTED) {
+		DBG(1, "Device not present")
+		up(&cam->fileop_sem);
+		return -ENODEV;
+	}
+
+	if (cam->state & DEV_MISCONFIGURED) {
+		DBG(1, "The camera is misconfigured. Close and open it again.")
+		up(&cam->fileop_sem);
+		return -EIO;
+	}
+
+	err = sn9c102_v4l2_ioctl(inode, filp, cmd, (void __user *)arg);
+
+	up(&cam->fileop_sem);
+
+	return err;
+}
+
+
+static struct file_operations sn9c102_fops = {
+	.owner =   THIS_MODULE,
+	.open =    sn9c102_open,
+	.release = sn9c102_release,
+	.ioctl =   sn9c102_ioctl,
+	.read =    sn9c102_read,
+	.poll =    sn9c102_poll,
+	.mmap =    sn9c102_mmap,
+	.llseek =  no_llseek,
+};
+
+/*****************************************************************************/
+
+/* It exists a single interface only. We do not need to validate anything. */
+static int
+sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct sn9c102_device* cam;
+	static unsigned int dev_nr = 0;
+	unsigned int i, n;
+	int err = 0, r;
+
+	n = sizeof(sn9c102_id_table)/sizeof(sn9c102_id_table[0]);
+	for (i = 0; i < n-1; i++)
+		if (udev->descriptor.idVendor==sn9c102_id_table[i].idVendor &&
+		    udev->descriptor.idProduct==sn9c102_id_table[i].idProduct)
+			break;
+	if (i == n-1)
+		return -ENODEV;
+
+	if (!(cam = kmalloc(sizeof(struct sn9c102_device), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(cam, 0, sizeof(*cam));
+
+	cam->usbdev = udev;
+
+	memcpy(&cam->dev, &udev->dev, sizeof(struct device));
+
+	if (!(cam->control_buffer = kmalloc(8, GFP_KERNEL))) {
+		DBG(1, "kmalloc() failed")
+		err = -ENOMEM;
+		goto fail;
+	}
+	memset(cam->control_buffer, 0, 8);
+
+	if (!(cam->v4ldev = video_device_alloc())) {
+		DBG(1, "video_device_alloc() failed")
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	init_MUTEX(&cam->dev_sem);
+
+	r = sn9c102_read_reg(cam, 0x00);
+	if (r < 0 || r != 0x10) {
+		DBG(1, "Sorry, this is not a SN9C10[12] based camera "
+		       "(vid/pid 0x%04X/0x%04X)",
+		    sn9c102_id_table[i].idVendor,sn9c102_id_table[i].idProduct)
+		err = -ENODEV;
+		goto fail;
+	}
+
+	DBG(2, "SN9C10[12] PC Camera Controller detected "
+	       "(vid/pid 0x%04X/0x%04X)",
+	    sn9c102_id_table[i].idVendor, sn9c102_id_table[i].idProduct)
+
+	for  (i = 0; sn9c102_sensor_table[i]; i++) {
+		err = sn9c102_sensor_table[i](cam);
+		if (!err)
+			break;
+	}
+
+	if (!err && cam->sensor) {
+		DBG(2, "%s image sensor detected", cam->sensor->name)
+		DBG(3, "Support for %s maintained by %s",
+		    cam->sensor->name, cam->sensor->maintainer)
+	} else {
+		DBG(1, "No supported image sensor detected")
+		err = -ENODEV;
+		goto fail;
+	}
+
+	if (sn9c102_init(cam)) {
+		DBG(1, "Initialization failed. I will retry on open().")
+		cam->state |= DEV_MISCONFIGURED;
+	}
+
+	strcpy(cam->v4ldev->name, "SN9C10[12] PC Camera");
+	cam->v4ldev->owner = THIS_MODULE;
+	cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
+	cam->v4ldev->hardware = VID_HARDWARE_SN9C102;
+	cam->v4ldev->fops = &sn9c102_fops;
+	cam->v4ldev->minor = video_nr[dev_nr];
+	cam->v4ldev->release = video_device_release;
+	video_set_drvdata(cam->v4ldev, cam);
+
+	down(&cam->dev_sem);
+
+	err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
+	                            video_nr[dev_nr]);
+	if (err) {
+		DBG(1, "V4L2 device registration failed")
+		if (err == -ENFILE && video_nr[dev_nr] == -1)
+			DBG(1, "Free /dev/videoX node not found")
+		video_nr[dev_nr] = -1;
+		dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
+		up(&cam->dev_sem);
+		goto fail;
+	}
+
+	DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor)
+
+	sn9c102_create_sysfs(cam);
+
+	usb_set_intfdata(intf, cam);
+
+	up(&cam->dev_sem);
+
+	return 0;
+
+fail:
+	if (cam) {
+		kfree(cam->control_buffer);
+		if (cam->v4ldev)
+			video_device_release(cam->v4ldev);
+		kfree(cam);
+	}
+	return err;
+}
+
+
+static void sn9c102_usb_disconnect(struct usb_interface* intf)
+{
+	struct sn9c102_device* cam = usb_get_intfdata(intf);
+
+	if (!cam)
+		return;
+
+	down_write(&sn9c102_disconnect);
+
+	down(&cam->dev_sem); 
+
+	DBG(2, "Disconnecting %s...", cam->v4ldev->name)
+
+	wake_up_interruptible_all(&cam->open);
+
+	if (cam->users) {
+		DBG(2, "Device /dev/video%d is open! Deregistration and "
+		       "memory deallocation are deferred on close.",
+		    cam->v4ldev->minor)
+		cam->state |= DEV_MISCONFIGURED;
+		sn9c102_stop_transfer(cam);
+		cam->state |= DEV_DISCONNECTED;
+		wake_up_interruptible(&cam->wait_frame);
+		wake_up_interruptible(&cam->wait_stream);
+	} else {
+		cam->state |= DEV_DISCONNECTED;
+		sn9c102_release_resources(cam);
+	}
+
+	up(&cam->dev_sem);
+
+	if (!cam->users)
+		kfree(cam);
+
+	up_write(&sn9c102_disconnect);
+}
+
+
+static struct usb_driver sn9c102_usb_driver = {
+	.owner =      THIS_MODULE,
+	.name =       "sn9c102",
+	.id_table =   sn9c102_id_table,
+	.probe =      sn9c102_usb_probe,
+	.disconnect = sn9c102_usb_disconnect,
+};
+
+/*****************************************************************************/
+
+static int __init sn9c102_module_init(void)
+{
+	int err = 0;
+
+	KDBG(2, SN9C102_MODULE_NAME " v" SN9C102_MODULE_VERSION)
+	KDBG(3, SN9C102_MODULE_AUTHOR)
+
+	if ((err = usb_register(&sn9c102_usb_driver)))
+		KDBG(1, "usb_register() failed")
+
+	return err;
+}
+
+
+static void __exit sn9c102_module_exit(void)
+{
+	usb_deregister(&sn9c102_usb_driver);
+}
+
+
+module_init(sn9c102_module_init);
+module_exit(sn9c102_module_exit);
diff -Nru a/drivers/usb/media/sn9c102_pas106b.c b/drivers/usb/media/sn9c102_pas106b.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/usb/media/sn9c102_pas106b.c	2004-07-13 13:12:22 -07:00
@@ -0,0 +1,209 @@
+/***************************************************************************
+ * Driver for PAS106B image sensor connected to the SN9C10[12] PC Camera   *
+ * Controllers                                                             *
+ *                                                                         *
+ * Copyright (C) 2004 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    *
+ * the Free Software Foundation; either version 2 of the License, or       *
+ * (at your option) any later version.                                     *
+ *                                                                         *
+ * This program is distributed in the hope that it will be useful,         *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ * GNU General Public License for more details.                            *
+ *                                                                         *
+ * You should have received a copy of the GNU General Public License       *
+ * along with this program; if not, write to the Free Software             *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
+ ***************************************************************************/
+
+#include <linux/delay.h>
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor pas106b;
+
+
+static int pas106b_init(struct sn9c102_device* cam)
+{
+	int err = 0;
+
+	err += sn9c102_write_reg(cam, 0x00, 0x10);
+	err += sn9c102_write_reg(cam, 0x00, 0x11);
+	err += sn9c102_write_reg(cam, 0x00, 0x14);
+	err += sn9c102_write_reg(cam, 0x20, 0x17);
+	err += sn9c102_write_reg(cam, 0x20, 0x19);
+	err += sn9c102_write_reg(cam, 0x09, 0x18);
+
+	err += sn9c102_i2c_write(cam, 0x02, 0x0c);
+	err += sn9c102_i2c_write(cam, 0x03, 0x12);
+	err += sn9c102_i2c_write(cam, 0x04, 0x05);
+	err += sn9c102_i2c_write(cam, 0x05, 0x22);
+	err += sn9c102_i2c_write(cam, 0x06, 0xac);
+	err += sn9c102_i2c_write(cam, 0x07, 0x00);
+	err += sn9c102_i2c_write(cam, 0x08, 0x01);
+	err += sn9c102_i2c_write(cam, 0x0a, 0x00);
+	err += sn9c102_i2c_write(cam, 0x0b, 0x00);
+	err += sn9c102_i2c_write(cam, 0x0d, 0x00);
+	err += sn9c102_i2c_write(cam, 0x10, 0x06);
+	err += sn9c102_i2c_write(cam, 0x11, 0x06);
+	err += sn9c102_i2c_write(cam, 0x12, 0x00);
+	err += sn9c102_i2c_write(cam, 0x14, 0x02);
+	err += sn9c102_i2c_write(cam, 0x13, 0x01);
+
+	msleep(400);
+
+	return err;
+}
+
+
+static int pas106b_get_ctrl(struct sn9c102_device* cam, 
+                            struct v4l2_control* ctrl)
+{
+	switch (ctrl->id) {
+	case V4L2_CID_RED_BALANCE:
+		return (ctrl->value = sn9c102_i2c_read(cam, 0x0c))<0 ? -EIO:0;
+	case V4L2_CID_BLUE_BALANCE:
+		return (ctrl->value = sn9c102_i2c_read(cam, 0x09))<0 ? -EIO:0;
+	case V4L2_CID_BRIGHTNESS:
+		return (ctrl->value = sn9c102_i2c_read(cam, 0x0e))<0 ? -EIO:0;
+	default:
+		return -EINVAL;
+	}
+}
+
+
+static int pas106b_set_ctrl(struct sn9c102_device* cam, 
+                            const struct v4l2_control* ctrl)
+{
+	int err = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_RED_BALANCE:
+		err += sn9c102_i2c_write(cam, 0x0c, ctrl->value & 0x1f);
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x1f);
+		break;
+	case V4L2_CID_BRIGHTNESS:
+		err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f);
+		break;
+	default:
+		return -EINVAL;
+	}
+	err += sn9c102_i2c_write(cam, 0x13, 0x01);
+
+	return err;
+}
+
+
+static int pas106b_set_crop(struct sn9c102_device* cam, 
+                            const struct v4l2_rect* rect)
+{
+	struct sn9c102_sensor* s = &pas106b;
+	int err = 0;
+	u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4,
+	   v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3;
+
+	err += sn9c102_write_reg(cam, h_start, 0x12);
+	err += sn9c102_write_reg(cam, v_start, 0x13);
+
+	return err;
+}
+
+
+static struct sn9c102_sensor pas106b = {
+	.name = "PAS106B",
+	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+	.frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
+	.interface = SN9C102_I2C_2WIRES,
+	.slave_read_id = 0x40,
+	.slave_write_id = 0x40,
+	.init = &pas106b_init,
+	.qctrl = {
+		{
+			.id = V4L2_CID_RED_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "red balance",
+			.minimum = 0x00,
+			.maximum = 0x1f,
+			.step = 0x01,
+			.default_value = 0x03,
+			.flags = 0,
+		},
+		{
+			.id = V4L2_CID_BLUE_BALANCE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "blue balance",
+			.minimum = 0x00,
+			.maximum = 0x1f,
+			.step = 0x01,
+			.default_value = 0x02,
+			.flags = 0,
+		},
+		{
+			.id = V4L2_CID_BRIGHTNESS,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "brightness",
+			.minimum = 0x00,
+			.maximum = 0x1f,
+			.step = 0x01,
+			.default_value = 0x06,
+			.flags = 0,
+		},
+	},
+	.get_ctrl = &pas106b_get_ctrl,
+	.set_ctrl = &pas106b_set_ctrl,
+	.cropcap = {
+		.bounds = {
+			.left = 0,
+			.top = 0,
+			.width = 352,
+			.height = 288,
+		},
+		.defrect = {
+			.left = 0,
+			.top = 0,
+			.width = 352,
+			.height = 288,
+		},
+	},
+	.set_crop = &pas106b_set_crop,
+	.pix_format = {
+		.width = 352,
+		.height = 288,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.priv = 8, /* we use this field as 'bits per pixel' */
+	}
+};
+
+
+int sn9c102_probe_pas106b(struct sn9c102_device* cam)
+{
+	int r0 = 0, r1 = 0, err = 0;
+	unsigned int pid = 0;
+
+	/* Minimal initialization to enable the I2C communication
+	   NOTE: do NOT change the values! */
+	err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */
+	err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */
+	err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 48 MHz */
+	if (err)
+		return -EIO;
+
+	r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00);
+	r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01);
+
+	if (r0 < 0 || r1 < 0)
+		return -EIO;
+
+	pid = (r0 << 11) | ((r1 & 0xf0) >> 4);
+	if (pid != 0x007)
+		return -ENODEV;
+
+	sn9c102_attach_sensor(cam, &pas106b);
+
+	return 0;
+}
diff -Nru a/drivers/usb/media/sn9c102_sensor.h b/drivers/usb/media/sn9c102_sensor.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/usb/media/sn9c102_sensor.h	2004-07-13 13:12:22 -07:00
@@ -0,0 +1,270 @@
+/***************************************************************************
+ * API for image sensors connected to the SN9C10[12] PC Camera Controllers *
+ *                                                                         *
+ * Copyright (C) 2004 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    *
+ * the Free Software Foundation; either version 2 of the License, or       *
+ * (at your option) any later version.                                     *
+ *                                                                         *
+ * This program is distributed in the hope that it will be useful,         *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ * GNU General Public License for more details.                            *
+ *                                                                         *
+ * You should have received a copy of the GNU General Public License       *
+ * along with this program; if not, write to the Free Software             *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
+ ***************************************************************************/
+
+#ifndef _SN9C102_SENSOR_H_
+#define _SN9C102_SENSOR_H_
+
+#include <linux/usb.h>
+#include <linux/videodev.h>
+#include <linux/device.h>
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <asm/types.h>
+
+struct sn9c102_device;
+struct sn9c102_sensor;
+
+/*****************************************************************************/
+
+/* OVERVIEW.
+   This is a small interface that allows you to add support for any CCD/CMOS
+   image sensors connected to the SN9C10X bridges. The entire API is documented
+   below. In the most general case, to support a sensor there are three steps
+   you have to follow:
+   1) define the main "sn9c102_sensor" structure by setting the basic fields;
+   2) write a probing function to be called by the core module when the USB
+      camera is recognized, then add both the USB ids and the name of that
+      function to the two corresponding tables SENSOR_TABLE and ID_TABLE (see
+      below);
+   3) implement the methods that you want/need (and fill the rest of the main
+      structure accordingly).
+   "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do
+   NOT need to touch the source code of the core module for the things to work
+   properly, unless you find bugs or flaws in it. Finally, do not forget to
+   read the V4L2 API for completeness. */
+
+/*****************************************************************************/
+ 
+/* Probing functions: on success, you must attach the sensor to the camera
+   by calling sn9c102_attach_sensor() provided below.
+   To enable the I2C communication, you might need to perform a really basic
+   initialization of the SN9C10X chip by using the write function declared 
+   ahead.
+   Functions must return 0 on success, the appropriate error otherwise. */
+extern int sn9c102_probe_pas106b(struct sn9c102_device* cam);
+extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam);
+extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam);
+
+/* Add the above entries to this table. Be sure to add the entry in the right
+   place, since, on failure, the next probing routine is called according to 
+   the order of the list below, from top to bottom */
+#define SN9C102_SENSOR_TABLE                                                  \
+static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = {              \
+	&sn9c102_probe_pas106b, /* strong detection based on SENSOR vid/pid */\
+	&sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */       \
+	&sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */       \
+	NULL,                                                                 \
+};
+
+/* Attach a probed sensor to the camera. */
+extern void 
+sn9c102_attach_sensor(struct sn9c102_device* cam,
+                      struct sn9c102_sensor* sensor);
+
+/* Each SN9C10X camera has proper PID/VID identifiers. Add them here in case.*/
+#define SN9C102_ID_TABLE                                                      \
+static const struct usb_device_id sn9c102_id_table[] = {                      \
+	{ USB_DEVICE(0xc45, 0x6001), },                                       \
+	{ USB_DEVICE(0xc45, 0x6005), }, /* TAS5110C1B */                      \
+	{ USB_DEVICE(0xc45, 0x6009), }, /* PAS106B */                         \
+	{ USB_DEVICE(0xc45, 0x600d), }, /* PAS106B */                         \
+	{ USB_DEVICE(0xc45, 0x6024), },                                       \
+	{ USB_DEVICE(0xc45, 0x6025), }, /* TAS5130D1B Maybe also TAS5110C1B */\
+	{ USB_DEVICE(0xc45, 0x6028), }, /* Maybe PAS202B */                   \
+	{ USB_DEVICE(0xc45, 0x6029), },                                       \
+	{ USB_DEVICE(0xc45, 0x602a), }, /* Maybe HV7131[D|E1] */              \
+	{ USB_DEVICE(0xc45, 0x602c), }, /* Maybe OV7620 */                    \
+	{ USB_DEVICE(0xc45, 0x6030), }, /* Maybe MI03 */                      \
+	{ USB_DEVICE(0xc45, 0x8001), },                                       \
+	{ }                                                                   \
+};
+
+/*****************************************************************************/
+
+/* Read/write routines: they always return -1 on error, 0 or the read value
+   otherwise. NOTE that a real read operation is not supported by the SN9C10X
+   chip for some of its registers. To work around this problem, a pseudo-read
+   call is provided instead: it returns the last successfully written value 
+   on the register (0 if it has never been written), the usual -1 on error. */
+
+/* The "try" I2C I/O versions are used when probing the sensor */
+extern int sn9c102_i2c_try_write(struct sn9c102_device*,struct sn9c102_sensor*,
+                                 u8 address, u8 value);
+extern int sn9c102_i2c_try_read(struct sn9c102_device*,struct sn9c102_sensor*,
+                                u8 address);
+
+/* This must be used if and only if the sensor doesn't implement the standard
+   I2C protocol, like the TASC sensors. There a number of good reasons why you
+   must use the single-byte versions of this function: do not abuse. It writes
+   n bytes, from data0 to datan, (registers 0x09 - 0x09+n of SN9C10X chip) */
+extern int sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
+                                     struct sn9c102_sensor* sensor, u8 n, 
+                                     u8 data0, u8 data1, u8 data2, u8 data3,
+                                     u8 data4, u8 data5);
+
+/* To be used after the sensor struct has been attached to the camera struct */
+extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value);
+extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address);
+
+/* I/O on registers in the bridge. Could be used by the sensor methods too */
+extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index);
+extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index);
+
+/* NOTE: there are no debugging functions here. To uniform the output you must
+   use the dev_info()/dev_warn()/dev_err() macros defined in device.h, already
+   included here, the argument being the struct device 'dev' of the sensor
+   structure. Do NOT use these macros before the sensor is attached or the
+   kernel will crash! However you should not need to notify the user about
+   common errors or other messages, since this is done by the master module. */
+
+/*****************************************************************************/
+
+enum sn9c102_i2c_frequency { /* sensors may support both the frequencies */
+	SN9C102_I2C_100KHZ = 0x01,
+	SN9C102_I2C_400KHZ = 0x02,
+};
+
+enum sn9c102_i2c_interface {
+	SN9C102_I2C_2WIRES,
+	SN9C102_I2C_3WIRES,
+};
+
+struct sn9c102_sensor {
+	char name[32], /* sensor name */
+	     maintainer[64]; /* name of the mantainer <email> */
+
+	/* These sensor capabilities must be provided if the SN9C10X controller
+	   needs to communicate through the sensor serial interface by using
+	   at least one of the i2c functions available */
+	enum sn9c102_i2c_frequency frequency;
+	enum sn9c102_i2c_interface interface;
+
+	/* These identifiers must be provided if the image sensor implements
+	   the standard I2C protocol. TASC sensors don't, although they have a
+	   serial interface: so this is a case where the "raw" I2C version
+	   could be helpful. */
+	u8 slave_read_id, slave_write_id; /* reg. 0x09 */
+
+	/* NOTE: Where not noted,most of the functions below are not mandatory.
+	         Set to null if you do not implement them. If implemented,
+	         they must return 0 on success, the proper error otherwise. */
+
+	int (*init)(struct sn9c102_device* cam);
+	/* This function is called after the sensor has been attached. 
+	   It should be used to initialize the sensor only, but may also
+	   configure part of the SN9C10X chip if necessary. You don't need to
+	   setup picture settings like brightness, contrast, etc.. here, if
+	   the corrisponding controls are implemented (see below), since 
+	   they are adjusted in the core driver by calling the set_ctrl()
+	   method after init(), where the arguments are the default values
+	   specified in the v4l2_queryctrl list of supported controls;
+	   Same suggestions apply for other settings, _if_ the corresponding
+	   methods are present; if not, the initialization must configure the
+	   sensor according to the default configuration structures below. */
+
+	struct v4l2_queryctrl qctrl[V4L2_CID_LASTP1-V4L2_CID_BASE];
+	/* Optional list of default controls, defined as indicated in the 
+	   V4L2 API. Menu type controls are not handled by this interface. */
+
+	int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl);
+	int (*set_ctrl)(struct sn9c102_device* cam,
+	                const struct v4l2_control* ctrl);
+	/* You must implement at least the set_ctrl method if you have defined
+	   the list above. The returned value must follow the V4L2
+	   specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER
+	   are not supported by this driver, so do not implement them. Also,
+	   passed values are NOT checked to see if they are out of bounds. */
+
+	struct v4l2_cropcap cropcap;
+	/* Think the image sensor as a grid of R,G,B monochromatic pixels
+	   disposed according to a particular Bayer pattern, which describes
+	   the complete array of pixels, from (0,0) to (xmax, ymax). We will
+	   use this coordinate system from now on. It is assumed the sensor
+	   chip can be programmed to capture/transmit a subsection of that
+	   array of pixels: we will call this subsection "active window".
+	   It is not always true that the largest achievable active window can
+	   cover the whole array of pixels. The V4L2 API defines another
+	   area called "source rectangle", which, in turn, is a subrectangle of
+	   the active window. The SN9C10X chip is always programmed to read the
+	   source rectangle.
+	   The bounds of both the active window and the source rectangle are
+	   specified in the cropcap substructures 'bounds' and 'defrect'.
+	   By default, the source rectangle should cover the largest possible
+	   area. Again, it is not always true that the largest source rectangle
+	   can cover the entire active window, although it is a rare case for 
+	   the hardware we have. The bounds of the source rectangle _must_ be
+	   multiple of 16 and must use the same coordinate system as indicated
+	   before; their centers shall align initially.
+	   If necessary, the sensor chip must be initialized during init() to
+	   set the bounds of the active sensor window; however, by default, it
+	   usually covers the largest achievable area (maxwidth x maxheight)
+	   of pixels, so no particular initialization is needed, if you have
+	   defined the correct default bounds in the structures.
+	   See the V4L2 API for further details.
+	   NOTE: once you have defined the bounds of the active window
+	         (struct cropcap.bounds) you must not change them.anymore.
+	   Only 'bounds' and 'defrect' fields are mandatory, other fields
+	   will be ignored. */
+
+	int (*set_crop)(struct sn9c102_device* cam,
+	                const struct v4l2_rect* rect);
+	/* To be called on VIDIOC_C_SETCROP. The core module always calls a
+	   default routine which configures the appropriate SN9C10X regs (also
+	   scaling), but you may need to override/adjust specific stuff.
+	   'rect' contains width and height values that are multiple of 16: in
+	   case you override the default function, you always have to program
+	   the chip to match those values; on error return the corresponding
+	   error code without rolling back.
+	   NOTE: in case, you must program the SN9C10X chip to get rid of 
+	         blank pixels or blank lines at the _start_ of each line or
+	         frame after each HSYNC or VSYNC, so that the image starts with
+	         real RGB data (see regs 0x12,0x13) (having set H_SIZE and,
+	         V_SIZE you don't have to care about blank pixels or blank
+	         lines at the end of each line or frame). */
+
+	struct v4l2_pix_format pix_format;
+	/* What you have to define here are: initial 'width' and 'height' of
+	   the target rectangle, the bayer 'pixelformat' and 'priv' which we'll
+	   be used to indicate the number of bits per pixel, 8 or 9. 
+	   Nothing more.
+	   NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4
+	           of cropcap.defrect.width and cropcap.defrect.height. I
+	           suggest 1/1.
+	   NOTE 2: as said above, you have to program the SN9C10X chip to get
+	           rid of any blank pixels, so that the output of the sensor
+	           matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). */
+
+	const struct device* dev;
+	/* This is the argument for dev_err(), dev_info() and dev_warn(). It
+	   is used for debugging purposes. You must not access the struct
+	   before the sensor is attached. */
+
+	const struct usb_device* usbdev;
+	/* Points to the usb_device struct after the sensor is attached.
+	   Do not touch unless you know what you are doing. */
+
+	/* Do NOT write to the data below, it's READ ONLY. It is used by the
+	   core module to store successfully updated values of the above
+	   settings, for rollbacks..etc..in case of errors during atomic I/O */
+	struct v4l2_queryctrl _qctrl[V4L2_CID_LASTP1-V4L2_CID_BASE];
+	struct v4l2_rect _rect;
+};
+
+#endif /* _SN9C102_SENSOR_H_ */
diff -Nru a/drivers/usb/media/sn9c102_tas5110c1b.c b/drivers/usb/media/sn9c102_tas5110c1b.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/usb/media/sn9c102_tas5110c1b.c	2004-07-13 13:12:22 -07:00
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * Driver for TAS5110C1B image sensor connected to the SN9C10[12] PC       *
+ * Camera Controllers                                                      *
+ *                                                                         *
+ * Copyright (C) 2004 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    *
+ * the Free Software Foundation; either version 2 of the License, or       *
+ * (at your option) any later version.                                     *
+ *                                                                         *
+ * This program is distributed in the hope that it will be useful,         *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ * GNU General Public License for more details.                            *
+ *                                                                         *
+ * You should have received a copy of the GNU General Public License       *
+ * along with this program; if not, write to the Free Software             *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor tas5110c1b;
+
+
+static int tas5110c1b_init(struct sn9c102_device* cam)
+{
+	int err = 0;
+
+	err += sn9c102_write_reg(cam, 0x01, 0x01);
+	err += sn9c102_write_reg(cam, 0x44, 0x01);
+	err += sn9c102_write_reg(cam, 0x00, 0x10);
+	err += sn9c102_write_reg(cam, 0x00, 0x11);
+	err += sn9c102_write_reg(cam, 0x00, 0x14);
+	err += sn9c102_write_reg(cam, 0x60, 0x17);
+	err += sn9c102_write_reg(cam, 0x06, 0x18);
+	err += sn9c102_write_reg(cam, 0xcb, 0x19);
+
+	return err;
+}
+
+
+static int tas5110c1b_set_crop(struct sn9c102_device* cam, 
+                               const struct v4l2_rect* rect)
+{
+	struct sn9c102_sensor* s = &tas5110c1b;
+	int err = 0;
+	u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69,
+	   v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9;
+
+	err += sn9c102_write_reg(cam, h_start, 0x12);
+	err += sn9c102_write_reg(cam, v_start, 0x13);
+
+	return err;
+}
+
+
+static struct sn9c102_sensor tas5110c1b = {
+	.name = "TAS5110C1B",
+	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+	.init = &tas5110c1b_init,
+	.cropcap = {
+		.bounds = {
+			.left = 0,
+			.top = 0,
+			.width = 352,
+			.height = 288,
+		},
+		.defrect = {
+			.left = 0,
+			.top = 0,
+			.width = 352,
+			.height = 288,
+		},
+	},
+	.set_crop = &tas5110c1b_set_crop,
+	.pix_format = {
+		.width = 352,
+		.height = 288,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.priv = 8,
+	}
+};
+
+
+int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam)
+{
+	/* This sensor has no identifiers, so let's attach it anyway */
+	sn9c102_attach_sensor(cam, &tas5110c1b);
+
+	/* At the moment, only devices whose PID is 0x6005 have this sensor */
+	if (tas5110c1b.usbdev->descriptor.idProduct != 0x6005)
+		return -ENODEV;
+
+	return 0;
+}
diff -Nru a/drivers/usb/media/sn9c102_tas5130d1b.c b/drivers/usb/media/sn9c102_tas5130d1b.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/usb/media/sn9c102_tas5130d1b.c	2004-07-13 13:12:22 -07:00
@@ -0,0 +1,120 @@
+/***************************************************************************
+ * Driver for TAS5130D1B image sensor connected to the SN9C10[12] PC       *
+ * Camera Controllers                                                      *
+ *                                                                         *
+ * Copyright (C) 2004 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    *
+ * the Free Software Foundation; either version 2 of the License, or       *
+ * (at your option) any later version.                                     *
+ *                                                                         *
+ * This program is distributed in the hope that it will be useful,         *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ * GNU General Public License for more details.                            *
+ *                                                                         *
+ * You should have received a copy of the GNU General Public License       *
+ * along with this program; if not, write to the Free Software             *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor tas5130d1b;
+
+
+static int tas5130d1b_init(struct sn9c102_device* cam)
+{
+	int err = 0;
+
+	err += sn9c102_write_reg(cam, 0x01, 0x01);
+	err += sn9c102_write_reg(cam, 0x20, 0x17);
+	err += sn9c102_write_reg(cam, 0x04, 0x01);
+	err += sn9c102_write_reg(cam, 0x01, 0x10);
+	err += sn9c102_write_reg(cam, 0x00, 0x11);
+	err += sn9c102_write_reg(cam, 0x00, 0x14);
+	err += sn9c102_write_reg(cam, 0x60, 0x17);
+	err += sn9c102_write_reg(cam, 0x07, 0x18);
+	err += sn9c102_write_reg(cam, 0x33, 0x19);
+
+	err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x40,
+	                                 0x47, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x02, 0x20,
+	                                 0xa9, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0xc0,
+	                                 0x49, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x02, 0x20,
+	                                 0x6c, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0xc0,
+	                                 0x08, 0, 0);
+	err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x20,
+	                                 0x00, 0, 0);
+
+	err += sn9c102_write_reg(cam, 0x63, 0x19);
+
+	return err;
+}
+
+
+static int tas5130d1b_set_crop(struct sn9c102_device* cam, 
+                               const struct v4l2_rect* rect)
+{
+	struct sn9c102_sensor* s = &tas5130d1b;
+	int err = 0;
+	u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104,
+	   v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12;
+
+	err += sn9c102_write_reg(cam, h_start, 0x12);
+	err += sn9c102_write_reg(cam, v_start, 0x13);
+
+	return err;
+}
+
+
+static struct sn9c102_sensor tas5130d1b = {
+	.name = "TAS5130D1B",
+	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+	.frequency = SN9C102_I2C_100KHZ,
+	.interface = SN9C102_I2C_3WIRES,
+	.init = &tas5130d1b_init,
+	.cropcap = {
+		.bounds = {
+			.left = 0,
+			.top = 0,
+			.width = 640,
+			.height = 480,
+		},
+		.defrect = {
+			.left = 0,
+			.top = 0,
+			.width = 640,
+			.height = 480,
+		},
+	},
+	.set_crop = &tas5130d1b_set_crop,
+	.pix_format = {
+		.width = 640,
+		.height = 480,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.priv = 8,
+	}
+};
+
+
+int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam)
+{
+	/* This sensor has no identifiers, so let's attach it anyway */
+	sn9c102_attach_sensor(cam, &tas5130d1b);
+
+	/* At the moment, only devices whose PID is 0x6025 have this sensor */
+	if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025)
+		return -ENODEV;
+
+	dev_info(tas5130d1b.dev, "TAS5130D1B detected, but the support for it "
+	                         "is disabled at the moment - needs further "
+	                         "testing -\n");
+
+	return -ENODEV;
+}
diff -Nru a/drivers/usb/media/w9968cf.c b/drivers/usb/media/w9968cf.c
--- a/drivers/usb/media/w9968cf.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/media/w9968cf.c	2004-07-13 13:12:22 -07:00
@@ -5,7 +5,7 @@
  *                                                                         *
  * - Memory management code from bttv driver by Ralph Metzler,             *
  *   Marcus Metzler and Gerd Knorr.                                        *
- * - I2C interface to kernel, high-level CMOS sensor control routines and  *
+ * - I2C interface to kernel, high-level image sensor control routines and *
  *   some symbolic names from OV511 driver by Mark W. McClelland.          *
  * - Low-level I2C fast write function by Piotr Czerczak.                  *
  * - Low-level I2C read function by Frederic Jouault.                      *
@@ -27,21 +27,23 @@
 
 #include <linux/version.h>
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/kernel.h>
+#include <linux/kmod.h>
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/vmalloc.h>
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/string.h>
-#include <linux/ctype.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/ioctl.h>
 #include <linux/delay.h>
+#include <linux/stddef.h>
 #include <asm/page.h>
 #include <asm/uaccess.h>
+#include <linux/page-flags.h>
+#include <linux/moduleparam.h>
 
 #include "w9968cf.h"
 #include "w9968cf_decoder.h"
@@ -49,14 +51,18 @@
 
 
 /****************************************************************************
- * Module macros and paramaters                                             *
+ * Module macros and parameters                                             *
  ****************************************************************************/
 
+MODULE_DEVICE_TABLE(usb, winbond_id_table);
+
 MODULE_AUTHOR(W9968CF_MODULE_AUTHOR" "W9968CF_AUTHOR_EMAIL);
-MODULE_DESCRIPTION(W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION);
+MODULE_DESCRIPTION(W9968CF_MODULE_NAME);
+MODULE_VERSION(W9968CF_MODULE_VERSION);
 MODULE_LICENSE(W9968CF_MODULE_LICENSE);
 MODULE_SUPPORTED_DEVICE("Video");
 
+static int ovmod_load = W9968CF_OVMOD_LOAD;
 static int vppmod_load = W9968CF_VPPMOD_LOAD;
 static unsigned short simcams = W9968CF_SIMCAMS;
 static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/
@@ -100,8 +106,11 @@
 
 static unsigned int param_nv[24]; /* number of values per parameter */
 
+#ifdef CONFIG_KMOD
+module_param(ovmod_load, bool, 0644);
 module_param(vppmod_load, bool, 0444);
-module_param(simcams, ushort, 0444);
+#endif
+module_param(simcams, ushort, 0644);
 module_param_array(video_nr, short, param_nv[0], 0444);
 module_param_array(packet_size, uint, param_nv[1], 0444);
 module_param_array(max_buffers, ushort, param_nv[2], 0444);
@@ -127,21 +136,34 @@
 module_param_array(contrast, uint, param_nv[22], 0444);
 module_param_array(whiteness, uint, param_nv[23], 0444);
 #ifdef W9968CF_DEBUG
-module_param(debug, ushort, 0444);
-module_param(specific_debug, bool, 0444);
+module_param(debug, ushort, 0644);
+module_param(specific_debug, bool, 0644);
 #endif
 
+#ifdef CONFIG_KMOD
+MODULE_PARM_DESC(ovmod_load, 
+                 "\n<0|1> Automatic 'ovcamchip' module loading."
+                 "\n0 disabled, 1 enabled."
+                 "\nIf enabled,'insmod' searches for the required 'ovcamchip'"
+                 "\nmodule in the system, according to its configuration, and"
+                 "\nattempts to load that module automatically. This action is"
+                 "\nperformed once as soon as the 'w9968cf' module is loaded"
+                 "\ninto memory."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_OVMOD_LOAD)"."
+                 "\n");
 MODULE_PARM_DESC(vppmod_load, 
                  "\n<0|1> Automatic 'w9968cf-vpp' module loading."
-                 "\n0 disable, 1 enable."
+                 "\n0 disabled, 1 enabled."
                  "\nIf enabled, every time an application attempts to open a"
                  "\ncamera, 'insmod' searches for the video post-processing"
                  "\nmodule in the system and loads it automatically (if"
-                 "\npresent). The 'w9968cf-vpp' module adds extra image"
-                 "\nmanipulation functions to the 'w9968cf' module, like"
-                 "\nsoftware up-scaling,colour conversions and video decoding."
+                 "\npresent). The optional 'w9968cf-vpp' module adds extra"
+                 "\n image manipulation functions to the 'w9968cf' module,like"
+                 "\nsoftware up-scaling,colour conversions and video decoding"
+                 "\nfor very high frame rates."
                  "\nDefault value is "__MODULE_STRING(W9968CF_VPPMOD_LOAD)"."
                  "\n");
+#endif
 MODULE_PARM_DESC(simcams, 
                  "\n<n> Number of cameras allowed to stream simultaneously."
                  "\nn may vary from 0 to "
@@ -176,8 +198,8 @@
                  "\n<0|1[,...]> "
                  "Hardware double buffering: 0 disabled, 1 enabled."
                  "\nIt should be enabled if you want smooth video output: if"
-                 "\nyou obtain out of sync. video, disable it at all, or try"
-                 "\nto decrease the 'clockdiv' module paramater value."
+                 "\nyou obtain out of sync. video, disable it, or try to"
+                 "\ndecrease the 'clockdiv' module parameter value."
                  "\nDefault value is "__MODULE_STRING(W9968CF_DOUBLE_BUFFER)
                  " for every device."
                  "\n");
@@ -193,7 +215,7 @@
                  "\nDefault value is "__MODULE_STRING(W9968CF_FILTER_TYPE)
                  " for every device."
                  "\nThe filter is used to reduce noise and aliasing artifacts"
-                 "\nproduced by the CCD or CMOS sensor, and the scaling"
+                 "\nproduced by the CCD or CMOS image sensor, and the scaling"
                  " process."
                  "\n");
 MODULE_PARM_DESC(largeview, 
@@ -208,7 +230,7 @@
                  " enough memory."
                  "\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING)
                  " for every device."
-                 "\nIf 'w9968cf-vpp' is not loaded, this paramater is"
+                 "\nIf 'w9968cf-vpp' is not present, this parameter is"
                  " set to 0."
                  "\n");
 MODULE_PARM_DESC(decompression,
@@ -224,8 +246,8 @@
                  "a multiple of 16."
                  "\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION)
                  " for every device."
-                 "\nIf 'w9968cf-vpp' is not loaded, forcing decompression is "
-                 "\nnot allowed; in this case this paramater is set to 2."
+                 "\nIf 'w9968cf-vpp' is not present, forcing decompression is "
+                 "\nnot allowed; in this case this parameter is set to 2."
                  "\n");
 MODULE_PARM_DESC(force_palette,
                  "\n<0"
@@ -255,11 +277,11 @@
                  "\n- RGB565  16 bpp - Software conversion from UYVY"
                  "\n- RGB24   24 bpp - Software conversion from UYVY"
                  "\n- RGB32   32 bpp - Software conversion from UYVY"
-                 "\nWhen not 0, this paramater will override 'decompression'."
+                 "\nWhen not 0, this parameter will override 'decompression'."
                  "\nDefault value is 0 for every device."
                  "\nInitial palette is "
                  __MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"."
-                 "\nIf 'w9968cf-vpp' is not loaded, this paramater is"
+                 "\nIf 'w9968cf-vpp' is not present, this parameter is"
                  " set to 9 (UYVY)."
                  "\n");
 MODULE_PARM_DESC(force_rgb, 
@@ -271,13 +293,13 @@
                  " for every device."
                  "\n");
 MODULE_PARM_DESC(autobright,
-                 "\n<0|1[,...]> CMOS sensor automatically changes brightness:"
+                 "\n<0|1[,...]> Image sensor automatically changes brightness:"
                  "\n 0 = no, 1 = yes"
                  "\nDefault value is "__MODULE_STRING(W9968CF_AUTOBRIGHT)
                  " for every device."
                  "\n");
 MODULE_PARM_DESC(autoexp,
-                 "\n<0|1[,...]> CMOS sensor automatically changes exposure:"
+                 "\n<0|1[,...]> Image sensor automatically changes exposure:"
                  "\n 0 = no, 1 = yes"
                  "\nDefault value is "__MODULE_STRING(W9968CF_AUTOEXP)
                  " for every device."
@@ -304,7 +326,7 @@
                  "Force pixel clock divisor to a specific value (for experts):"
                  "\n  n may vary from 0 to 127."
                  "\n -1 for automatic value."
-                 "\nSee also the 'double_buffer' module paramater."
+                 "\nSee also the 'double_buffer' module parameter."
                  "\nDefault value is "__MODULE_STRING(W9968CF_CLOCKDIV)
                  " for every device."
                  "\n");
@@ -321,7 +343,7 @@
                  " for every device."
                  "\n");
 MODULE_PARM_DESC(monochrome,
-                 "\n<0|1[,...]> Use OV CMOS sensor as monochrome sensor:"
+                 "\n<0|1[,...]> Use image sensor as monochrome sensor:"
                  "\n 0 = no, 1 = yes"
                  "\nNot all the sensors support monochrome color."
                  "\nDefault value is "__MODULE_STRING(W9968CF_MONOCHROME)
@@ -363,7 +385,7 @@
                  "\n4 = warnings"
                  "\n5 = called functions"
                  "\n6 = function internals"
-                 "\nLevel 5 and 6 are useful for testing only, when just "
+                 "\nLevel 5 and 6 are useful for testing only, when only "
                  "one device is used."
                  "\nDefault value is "__MODULE_STRING(W9968CF_DEBUG_LEVEL)"."
                  "\n");
@@ -388,14 +410,14 @@
 static struct file_operations w9968cf_fops;
 static int w9968cf_open(struct inode*, struct file*);
 static int w9968cf_release(struct inode*, struct file*);
-static ssize_t w9968cf_read(struct file*, char __user *, size_t, loff_t*);
 static int w9968cf_mmap(struct file*, struct vm_area_struct*);
 static int w9968cf_ioctl(struct inode*, struct file*, unsigned, unsigned long);
-static int w9968cf_v4l_ioctl(struct inode*, struct file*, unsigned int, void*);
+static ssize_t w9968cf_read(struct file*, char __user *, size_t, loff_t*);
+static int w9968cf_v4l_ioctl(struct inode*, struct file*, unsigned int,
+                             void __user *);
 
 /* USB-specific */
 static int w9968cf_start_transfer(struct w9968cf_device*);
-static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs);
 static int w9968cf_stop_transfer(struct w9968cf_device*);
 static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index);
 static int w9968cf_read_reg(struct w9968cf_device*, u16 index);
@@ -403,6 +425,7 @@
 static int w9968cf_write_sb(struct w9968cf_device*, u16 value);
 static int w9968cf_read_sb(struct w9968cf_device*);
 static int w9968cf_upload_quantizationtables(struct w9968cf_device*);
+static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs);
 
 /* Low-level I2C (SMBus) I/O */
 static int w9968cf_smbus_start(struct w9968cf_device*);
@@ -439,12 +462,11 @@
 static void rvfree(void *mem, unsigned long size);
 static void w9968cf_deallocate_memory(struct w9968cf_device*);
 static int  w9968cf_allocate_memory(struct w9968cf_device*);
-static inline unsigned long w9968cf_get_max_bufsize(struct w9968cf_device*);
 
-/* High-level CMOS sensor control functions */
+/* High-level image sensor control functions */
 static int w9968cf_sensor_set_control(struct w9968cf_device*,int cid,int val);
 static int w9968cf_sensor_get_control(struct w9968cf_device*,int cid,int *val);
-static int w9968cf_sensor_cmd(struct w9968cf_device*, 
+static int w9968cf_sensor_cmd(struct w9968cf_device*,
                               unsigned int cmd, void *arg);
 static int w9968cf_sensor_init(struct w9968cf_device*);
 static int w9968cf_sensor_update_settings(struct w9968cf_device*);
@@ -456,12 +478,13 @@
 static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*,
                                      enum w9968cf_model_id, 
                                      const unsigned short dev_nr);
+static void w9968cf_adjust_configuration(struct w9968cf_device*);
 static int w9968cf_turn_on_led(struct w9968cf_device*);
 static int w9968cf_init_chip(struct w9968cf_device*);
 static int w9968cf_set_picture(struct w9968cf_device*, struct video_picture);
 static int w9968cf_set_window(struct w9968cf_device*, struct video_window);
 static inline u16 w9968cf_valid_palette(u16 palette);
-static u16 w9968cf_valid_depth(u16 palette);
+static inline u16 w9968cf_valid_depth(u16 palette);
 static inline u8 w9968cf_need_decompression(u16 palette);
 static int w9968cf_postprocess_frame(struct w9968cf_device*, 
                                      struct w9968cf_frame_t*);
@@ -472,18 +495,8 @@
 static void w9968cf_release_resources(struct w9968cf_device*);
 
 /* Intermodule communication */
-static int w9968cf_vppmod_detect(void);
-static void w9968cf_vppmod_release(void);
-
-/* Pointers to registered video post-processing functions */
-static void (*w9968cf_vpp_init_decoder)(void);
-static int (*w9968cf_vpp_check_headers)(const unsigned char*,
-                                        const unsigned long);
-static int (*w9968cf_vpp_decode)(const char*, const unsigned, 
-                                 const unsigned, const unsigned, char*);
-static void (*w9968cf_vpp_swap_yuvbytes)(void*, unsigned long);
-static void (*w9968cf_vpp_uyvy_to_rgbx)(u8*, unsigned long, u8*, u16, u8);
-static void (*w9968cf_vpp_scale_up)(u8*, u8*, u16, u16, u16, u16, u16);
+static int w9968cf_vppmod_detect(struct w9968cf_device*);
+static void w9968cf_vppmod_release(struct w9968cf_device*);
 
 
 
@@ -518,12 +531,15 @@
 	{ W9968CF_MOD_CLVBWGP, "Creative Labs Video Blaster WebCam Go Plus" },
 
 	/* Other cameras (having the same descriptors as Generic W996[87]CF) */
-	{ W9968CF_MOD_ADPA5R, "Aroma Digi Pen ADG-5000 Refurbished" },
-	{ W9986CF_MOD_AU, "AVerTV USB" },
+	{ W9968CF_MOD_ADPVDMA, "Aroma Digi Pen VGA Dual Mode ADG-5000" },
+	{ W9986CF_MOD_AAU, "AVerMedia AVerTV USB" },
 	{ W9968CF_MOD_CLVBWG, "Creative Labs Video Blaster WebCam Go" },
-	{ W9968CF_MOD_DLLDK, "Die Lebon LDC-D35A Digital Kamera" },
+	{ W9968CF_MOD_LL, "Lebon LDC-035A" },
 	{ W9968CF_MOD_EEEMC, "Ezonics EZ-802 EZMega Cam" },
+	{ W9968CF_MOD_OOE, "OmniVision OV8610-EDE" },
 	{ W9968CF_MOD_ODPVDMPC, "OPCOM Digi Pen VGA Dual Mode Pen Camera" },
+	{ W9968CF_MOD_PDPII, "Pretec Digi Pen-II" },
+	{ W9968CF_MOD_PDP480, "Pretec DigiPen-480" },
 
 	{  -1, NULL }
 };
@@ -649,17 +665,6 @@
 
 
 /*--------------------------------------------------------------------------
-  Return the maximum size (in bytes) of a frame buffer.
-  --------------------------------------------------------------------------*/
-static inline unsigned long w9968cf_get_max_bufsize(struct w9968cf_device* cam)
-{
-	u8 bpp = (w9968cf_vppmod_present) ? 4 : 2;
-	return (cam->upscaling) ? W9968CF_MAX_WIDTH*W9968CF_MAX_HEIGHT*bpp :
-	                          cam->maxwidth*cam->maxheight*bpp;
-}
-
-
-/*--------------------------------------------------------------------------
   Deallocate previously allocated memory.
   --------------------------------------------------------------------------*/
 static void w9968cf_deallocate_memory(struct w9968cf_device* cam)
@@ -674,26 +679,25 @@
 
 	/* Free temporary frame buffer */
 	if (cam->frame_tmp.buffer) {
-		rvfree(cam->frame_tmp.buffer, W9968CF_HW_BUF_SIZE);
+		rvfree(cam->frame_tmp.buffer, cam->frame_tmp.size);
 		cam->frame_tmp.buffer = NULL;
 	}
 
 	/* Free helper buffer */
-	if (cam->vpp_buffer) {
-		rvfree(cam->vpp_buffer, w9968cf_get_max_bufsize(cam));
-		cam->vpp_buffer = NULL;
+	if (cam->frame_vpp.buffer) {
+		rvfree(cam->frame_vpp.buffer, cam->frame_vpp.size);
+		cam->frame_vpp.buffer = NULL;
 	}
-	
+
 	/* Free video frame buffers */
 	if (cam->frame[0].buffer) {
-		rvfree(cam->frame[0].buffer, 
-		       cam->nbuffers * w9968cf_get_max_bufsize(cam));
+		rvfree(cam->frame[0].buffer, cam->nbuffers*cam->frame[0].size);
 		cam->frame[0].buffer = NULL;
 	}
 
 	cam->nbuffers = 0;
 
-	DBG(5, "Memory successfully deallocated.")
+	DBG(5, "Memory successfully deallocated")
 }
 
 
@@ -704,19 +708,30 @@
   --------------------------------------------------------------------------*/
 static int w9968cf_allocate_memory(struct w9968cf_device* cam)
 {
-	const unsigned long bufsize = w9968cf_get_max_bufsize(cam);
 	const u16 p_size = wMaxPacketSize[cam->altsetting-1];
 	void* buff = NULL;
-	u8 i;
+	unsigned long hw_bufsize, vpp_bufsize;
+	u8 i, bpp;
 
 	/* NOTE: Deallocation is done elsewhere in case of error */
 
+	/* Calculate the max amount of raw data per frame from the device */
+	hw_bufsize = cam->maxwidth*cam->maxheight*2;
+
+	/* Calculate the max buf. size needed for post-processing routines */
+	bpp = (w9968cf_vpp) ? 4 : 2;
+	if (cam->upscaling)
+		vpp_bufsize = max(W9968CF_MAX_WIDTH*W9968CF_MAX_HEIGHT*bpp,
+		                  cam->maxwidth*cam->maxheight*bpp);
+	else
+		vpp_bufsize = cam->maxwidth*cam->maxheight*bpp;
+
 	/* Allocate memory for the isochronous transfer buffers */
 	for (i = 0; i < W9968CF_URBS; i++) {
 		if (!(cam->transfer_buffer[i] =
 		      kmalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) {
 			DBG(1, "Couldn't allocate memory for the isochronous "
-			       "transfer buffers (%d bytes).", 
+			       "transfer buffers (%u bytes)", 
 			    p_size * W9968CF_ISO_PACKETS)
 			return -ENOMEM;
 		}
@@ -724,44 +739,49 @@
 	}
 
 	/* Allocate memory for the temporary frame buffer */
-	if (!(cam->frame_tmp.buffer = rvmalloc(W9968CF_HW_BUF_SIZE))) {
+	if (!(cam->frame_tmp.buffer = rvmalloc(hw_bufsize))) {
 		DBG(1, "Couldn't allocate memory for the temporary "
-		       "video frame buffer (%i bytes).", W9968CF_HW_BUF_SIZE)
+		       "video frame buffer (%lu bytes)", hw_bufsize)
 		return -ENOMEM;
 	}
+	cam->frame_tmp.size = hw_bufsize;
+	cam->frame_tmp.number = -1;
 
 	/* Allocate memory for the helper buffer */
-	if (w9968cf_vppmod_present) {
-		if (!(cam->vpp_buffer = rvmalloc(bufsize))) {
+	if (w9968cf_vpp) {
+		if (!(cam->frame_vpp.buffer = rvmalloc(vpp_bufsize))) {
 			DBG(1, "Couldn't allocate memory for the helper buffer"
-			       " (%li bytes).", bufsize)
+			       " (%lu bytes)", vpp_bufsize)
 			return -ENOMEM;
 		}
+		cam->frame_vpp.size = vpp_bufsize;
 	} else
-		cam->vpp_buffer = NULL;
+		cam->frame_vpp.buffer = NULL;
 
-	/* Allocate memory for video frame buffers */	
+	/* Allocate memory for video frame buffers */
 	cam->nbuffers = cam->max_buffers;
 	while (cam->nbuffers >= 2) {
-		if ((buff = rvmalloc(cam->nbuffers * bufsize)))
+		if ((buff = rvmalloc(cam->nbuffers * vpp_bufsize)))
 			break;
 		else
 			cam->nbuffers--;
 	}
 
 	if (!buff) {
-		DBG(1, "Couldn't allocate memory for the video frame buffers.")
+		DBG(1, "Couldn't allocate memory for the video frame buffers")
 		cam->nbuffers = 0;
 		return -ENOMEM;
 	}
 
 	if (cam->nbuffers != cam->max_buffers)
-		DBG(2, "Couldn't allocate memory for %d video frame buffers. "
-		       "Only memory for %d buffers has been allocated.",
+		DBG(2, "Couldn't allocate memory for %u video frame buffers. "
+		       "Only memory for %u buffers has been allocated",
 		    cam->max_buffers, cam->nbuffers)
 
 	for (i = 0; i < cam->nbuffers; i++) {
-		cam->frame[i].buffer = buff + i*bufsize;
+		cam->frame[i].buffer = buff + i*vpp_bufsize;
+		cam->frame[i].size = vpp_bufsize;
+		cam->frame[i].number = i;
 		/* Circular list */
 		if (i != cam->nbuffers-1)
 			cam->frame[i].next = &cam->frame[i+1];
@@ -770,7 +790,7 @@
 		cam->frame[i].status = F_UNUSED;
 	}
 
-	DBG(5, "Memory successfully allocated.")
+	DBG(5, "Memory successfully allocated")
 	return 0;
 }
 
@@ -794,20 +814,16 @@
 {
 	struct w9968cf_device* cam = (struct w9968cf_device*)urb->context;
 	struct w9968cf_frame_t** f;
-	unsigned long maxbufsize;
 	unsigned int len, status;
 	void* pos;
 	u8 i;
 	int err = 0;
 
 	if ((!cam->streaming) || cam->disconnected) {
-		DBG(4, "Got interrupt, but not streaming.")
+		DBG(4, "Got interrupt, but not streaming")
 		return;
 	}
 
-	maxbufsize = min( (unsigned long)W9968CF_HW_BUF_SIZE, 
-	                  w9968cf_get_max_bufsize(cam) );
-
 	/* "(*f)" will be used instead of "cam->frame_current" */
 	f = &cam->frame_current;
 
@@ -820,8 +836,8 @@
 			(*f)->length = cam->frame_tmp.length;
 			memcpy((*f)->buffer, cam->frame_tmp.buffer,
 			       (*f)->length);
-			DBG(6, "Switched from temp. frame to frame #%zd", 
-			    (*f) - &cam->frame[0])
+			DBG(6, "Switched from temp. frame to frame #%d", 
+			    (*f)->number)
 		}
 	}
 
@@ -832,7 +848,7 @@
 
 		if (status && len != 0) {
 			DBG(4, "URB failed, error in data packet "
-			       "(error #%d, %s).",
+			       "(error #%u, %s)",
 			    status, symbolic(urb_errlist, status))
 			(*f)->status = F_ERROR;
 			continue;
@@ -846,8 +862,8 @@
 			}
 
 			/* Buffer overflows shouldn't happen, however...*/
-			if ((*f)->length + len > maxbufsize) {
-				DBG(4, "Buffer overflow: bad data packets.")
+			if ((*f)->length + len > (*f)->size) {
+				DBG(4, "Buffer overflow: bad data packets")
 				(*f)->status = F_ERROR;
 			}
 
@@ -858,11 +874,10 @@
 
 		} else if ((*f)->status == F_GRABBING) { /* end of frame */
 
-			DBG(6, "Frame #%zd successfully grabbed.",
-			    ((*f)==&cam->frame_tmp ? -1 : (*f)-&cam->frame[0]))
+			DBG(6, "Frame #%d successfully grabbed", (*f)->number)
 
 			if (cam->vpp_flag & VPP_DECOMPRESSION) {
-				err=(*w9968cf_vpp_check_headers)((*f)->buffer,
+				err = w9968cf_vpp->check_headers((*f)->buffer,
 				                                 (*f)->length);
 				if (err) {
 					DBG(4, "Skip corrupted frame: %s",
@@ -887,7 +902,7 @@
 		} else if ((*f)->status == F_ERROR)
 			(*f)->status = F_UNUSED; /* grab it again */
 
-		PDBGG("Frame length %li | pack.#%d | pack.len. %d | state %d",
+		PDBGG("Frame length %lu | pack.#%u | pack.len. %u | state %d",
 		      (unsigned long)(*f)->length, i, len, (*f)->status)
 
 	} /* end for */
@@ -900,7 +915,7 @@
 		if ((err = usb_submit_urb(urb, GFP_ATOMIC))) {
 			cam->misconfigured = 1;
 			DBG(1, "Couldn't resubmit the URB: error %d, %s",
-			    err, symbolic(urb_errlist, err));
+			    err, symbolic(urb_errlist, err))
 		}
 	spin_unlock(&cam->urb_lock);
 
@@ -931,7 +946,7 @@
 		if (!urb) {
 			for (j = 0; j < i; j++)
 				usb_free_urb(cam->urb[j]);
-			DBG(1, "Couldn't allocate the URB structures.")
+			DBG(1, "Couldn't allocate the URB structures")
 			return -ENOMEM;
 		}
 
@@ -976,7 +991,7 @@
 	if (err || (vidcapt < 0)) {
 		for (i = 0; i < W9968CF_URBS; i++)
 			usb_free_urb(cam->urb[i]);
-		DBG(1, "Couldn't tell the camera to start the data transfer.")
+		DBG(1, "Couldn't tell the camera to start the data transfer")
 		return err;
 	}
 
@@ -988,26 +1003,29 @@
 	cam->frame_current = &cam->frame_tmp;
 
 	if (!(cam->vpp_flag & VPP_DECOMPRESSION))
-		DBG(5, "Isochronous transfer size: %li bytes/frame.",
+		DBG(5, "Isochronous transfer size: %lu bytes/frame", 
 		    (unsigned long)t_size*2)
 
 	DBG(5, "Starting the isochronous transfer...")
 
+	cam->streaming = 1;
+
 	/* Submit the URBs */
 	for (i = 0; i < W9968CF_URBS; i++) {
 		err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
 		if (err) {
-			for (j = i-1; j >= 0; j--)
-				if (!usb_unlink_urb(cam->urb[j]))
-					usb_free_urb(cam->urb[j]);
+			cam->streaming = 0;
+			for (j = i-1; j >= 0; j--) {
+				usb_kill_urb(cam->urb[j]);
+				usb_free_urb(cam->urb[j]);
+			}
 			DBG(1, "Couldn't send a transfer request to the "
-			       "USB core (error #%d, %s).", err, 
+			       "USB core (error #%d, %s)", err, 
 			    symbolic(urb_errlist, err))
+			return err;
 		}
 	}
 
-	cam->streaming = 1;
-
 	return 0;
 }
 
@@ -1023,6 +1041,9 @@
 	int err = 0;
 	s8 i;
 
+	if (!cam->streaming)
+		return 0;
+
 	/* This avoids race conditions with usb_submit_urb() 
 	   in the URB completition handler */
 	spin_lock_irqsave(&cam->urb_lock, lock_flags);
@@ -1031,10 +1052,9 @@
 
 	for (i = W9968CF_URBS-1; i >= 0; i--)
 		if (cam->urb[i]) {
-			if (!usb_unlink_urb(cam->urb[i])) {
-				usb_free_urb(cam->urb[i]);
-				cam->urb[i] = NULL;
-			}
+			usb_kill_urb(cam->urb[i]);
+			usb_free_urb(cam->urb[i]);
+			cam->urb[i] = NULL;
 		}
 
 	if (cam->disconnected)
@@ -1052,7 +1072,7 @@
 	}
 
 exit:
-	DBG(5, "Isochronous transfer stopped.")
+	DBG(5, "Isochronous transfer stopped")
 	return 0;
 }
 
@@ -1072,7 +1092,7 @@
 
 	if (res < 0)
 		DBG(4, "Failed to write a register "
-		       "(value 0x%04X, index 0x%02X, error #%d, %s).",
+		       "(value 0x%04X, index 0x%02X, error #%d, %s)",
 		    value, index, res, symbolic(urb_errlist, res))
 
 	return (res >= 0) ? 0 : -1;
@@ -1095,7 +1115,7 @@
 
 	if (res < 0)
 		DBG(4, "Failed to read a register "
-		       "(index 0x%02X, error #%d, %s).",
+		       "(index 0x%02X, error #%d, %s)",
 		    index, res, symbolic(urb_errlist, res))
 
 	return (res >= 0) ? (int)(*buff) : -1;
@@ -1120,7 +1140,7 @@
 
 	if (res < 0)
 		DBG(4, "Failed to write the FSB registers "
-		       "(error #%d, %s).", res, symbolic(urb_errlist, res))
+		       "(error #%d, %s)", res, symbolic(urb_errlist, res))
 
 	return (res >= 0) ? 0 : -1;
 }
@@ -1270,7 +1290,7 @@
 	if (sda < 0)
 		err += sda;
 	if (sda == 1) {
-		DBG(6, "Couldn't receive the ACK.")
+		DBG(6, "Couldn't receive the ACK")
 		err += -1;
 	}
 
@@ -1353,11 +1373,11 @@
 
 	if (!err)
 		DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X "
-		       "value 0x%02X.", address, subaddress, value)
+		       "value 0x%02X", address, subaddress, value)
 	else
 		DBG(5, "I2C write byte data failed, addr.0x%04X, "
-		       "subaddr.0x%02X, value 0x%02X.", 
-	            address, subaddress, value)
+		       "subaddr.0x%02X, value 0x%02X", 
+		    address, subaddress, value)
 
 	return err;
 }
@@ -1392,11 +1412,11 @@
 
 	if (!err)
 		DBG(5, "I2C read byte data done, addr.0x%04X, "
-		       "subaddr.0x%02X, value 0x%02X.", 
+		       "subaddr.0x%02X, value 0x%02X", 
 		    address, subaddress, *value)
 	else
 		DBG(5, "I2C read byte data failed, addr.0x%04X, "
-		       "subaddr.0x%02X, wrong value 0x%02X.",
+		       "subaddr.0x%02X, wrong value 0x%02X",
 		    address, subaddress, *value)
 
 	return err;
@@ -1424,11 +1444,11 @@
 	err += w9968cf_write_sb(cam, 0x0000);
 
 	if (!err)
-		DBG(5, "I2C read byte done, addr.0x%04X."
-		       "value 0x%02X.", address, *value)
+		DBG(5, "I2C read byte done, addr.0x%04X, "
+		       "value 0x%02X", address, *value)
 	else
-		DBG(5, "I2C read byte failed, addr.0x%04X."
-		       "wrong value 0x%02X.", address, *value)
+		DBG(5, "I2C read byte failed, addr.0x%04X, "
+		       "wrong value 0x%02X", address, *value)
 
 	return err;
 }
@@ -1439,7 +1459,7 @@
 w9968cf_i2c_adap_write_byte(struct w9968cf_device* cam,
                             u16 address, u8 value)
 {
-	DBG(4, "i2c_write_byte() is an unsupported transfer mode.")
+	DBG(4, "i2c_write_byte() is an unsupported transfer mode")
 	return -EINVAL;
 }
 
@@ -1546,9 +1566,8 @@
 	struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
 	const char* clientname = i2c_clientname(client);
 
-	if (cam->sensor_client == client) {
+	if (cam->sensor_client == client)
 		cam->sensor_client = NULL;
-	}
 
 	DBG(5, "I2C detach client [%s]", clientname)
 
@@ -1593,9 +1612,9 @@
 
 	err = i2c_add_adapter(&cam->i2c_adapter);
 	if (err)
-		DBG(1, "Failed to register the I2C adapter.")
+		DBG(1, "Failed to register the I2C adapter")
 	else
-		DBG(5, "I2C adapter registered.")
+		DBG(5, "I2C adapter registered")
 
 	return err;
 }
@@ -1622,9 +1641,9 @@
 	err += w9968cf_write_reg(cam, 0x0010, 0x01); /* ..high 'beep-beep' */
 
 	if (err)
-		DBG(2, "Couldn't turn on the LED.")
+		DBG(2, "Couldn't turn on the LED")
 
-	DBG(5, "LED turned on.")
+	DBG(5, "LED turned on")
 
 	return err;
 }
@@ -1637,6 +1656,13 @@
   --------------------------------------------------------------------------*/
 static int w9968cf_init_chip(struct w9968cf_device* cam)
 {
+	unsigned long hw_bufsize = cam->maxwidth*cam->maxheight*2,
+	              y0 = 0x0000,
+	              u0 = y0 + hw_bufsize/2,
+	              v0 = u0 + hw_bufsize/4,
+	              y1 = v0 + hw_bufsize/4,
+	              u1 = y1 + hw_bufsize/2,
+	              v1 = u1 + hw_bufsize/4;
 	int err = 0;
 
 	err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */
@@ -1645,23 +1671,25 @@
 	err += w9968cf_write_reg(cam, 0x405d, 0x03); /* DRAM timings */
 	err += w9968cf_write_reg(cam, 0x0030, 0x04); /* SDRAM timings */
 
-	err += w9968cf_write_reg(cam, 0x0000, 0x20); /* Y frame buf.0, low */
-	err += w9968cf_write_reg(cam, 0x0000, 0x21); /* Y frame buf.0, high */
-	err += w9968cf_write_reg(cam, 0xb000, 0x22); /* Y frame buf.1, low */
-	err += w9968cf_write_reg(cam, 0x0004, 0x23); /* Y frame buf.1, high */
-	err += w9968cf_write_reg(cam, 0x5800, 0x24); /* U frame buf.0, low */
-	err += w9968cf_write_reg(cam, 0x0002, 0x25); /* U frame buf.0, high */
-	err += w9968cf_write_reg(cam, 0x0800, 0x26); /* U frame buf.1, low */
-	err += w9968cf_write_reg(cam, 0x0007, 0x27); /* U frame buf.1, high */
-	err += w9968cf_write_reg(cam, 0x8400, 0x28); /* V frame buf.0, low */
-	err += w9968cf_write_reg(cam, 0x0003, 0x29); /* V frame buf.0, high */
-	err += w9968cf_write_reg(cam, 0x3400, 0x2a); /* V frame buf.1, low */
-	err += w9968cf_write_reg(cam, 0x0008, 0x2b); /* V frame buf.1, high */
-
-	err += w9968cf_write_reg(cam, 0x6000, 0x32); /* JPEG bitstream buf 0 */
-	err += w9968cf_write_reg(cam, 0x0009, 0x33); /* JPEG bitstream buf 0 */
-	err += w9968cf_write_reg(cam, 0x2000, 0x34); /* JPEG bitstream buf 1 */
-	err += w9968cf_write_reg(cam, 0x000d, 0x35); /* JPEG bitstream buf 1 */
+	err += w9968cf_write_reg(cam, y0 & 0xffff, 0x20); /* Y buf.0, low */
+	err += w9968cf_write_reg(cam, y0 >> 16, 0x21);    /* Y buf.0, high */
+	err += w9968cf_write_reg(cam, u0 & 0xffff, 0x24); /* U buf.0, low */
+	err += w9968cf_write_reg(cam, u0 >> 16, 0x25);    /* U buf.0, high */
+	err += w9968cf_write_reg(cam, v0 & 0xffff, 0x28); /* V buf.0, low */
+	err += w9968cf_write_reg(cam, v0 >> 16, 0x29);    /* V buf.0, high */
+
+	err += w9968cf_write_reg(cam, y1 & 0xffff, 0x22); /* Y buf.1, low */
+	err += w9968cf_write_reg(cam, y1 >> 16, 0x23);    /* Y buf.1, high */
+	err += w9968cf_write_reg(cam, u1 & 0xffff, 0x26); /* U buf.1, low */
+	err += w9968cf_write_reg(cam, u1 >> 16, 0x27);    /* U buf.1, high */
+	err += w9968cf_write_reg(cam, v1 & 0xffff, 0x2a); /* V buf.1, low */
+	err += w9968cf_write_reg(cam, v1 >> 16, 0x2b);    /* V buf.1, high */
+
+	err += w9968cf_write_reg(cam, y1 & 0xffff, 0x32); /* JPEG buf 0 low */
+	err += w9968cf_write_reg(cam, y1 >> 16, 0x33);    /* JPEG buf 0 high */
+
+	err += w9968cf_write_reg(cam, y1 & 0xffff, 0x34); /* JPEG buf 1 low */
+	err += w9968cf_write_reg(cam, y1 >> 16, 0x35);    /* JPEG bug 1 high */
 
 	err += w9968cf_write_reg(cam, 0x0000, 0x36);/* JPEG restart interval */
 	err += w9968cf_write_reg(cam, 0x0804, 0x37);/*JPEG VLE FIFO threshold*/
@@ -1672,9 +1700,9 @@
 	err += w9968cf_set_window(cam, cam->window);
 
 	if (err)
-		DBG(1, "Chip initialization failed.")
+		DBG(1, "Chip initialization failed")
 	else
-		DBG(5, "Chip successfully initialized.")
+		DBG(5, "Chip successfully initialized")
 
 	return err;
 }
@@ -1736,8 +1764,8 @@
 			break;
 	}
 
-	/* FIXME: 'hardware double buffer' doesn't work when compressed video
-	          is enabled (corrupted frames). */
+	/* NOTE: due to memory issues, it is better to disable the hardware
+	         double buffering during compression */
 	if (cam->double_buffer && !(cam->vpp_flag & VPP_DECOMPRESSION))
 		reg_v |= 0x0080;
 
@@ -1761,16 +1789,15 @@
 	cam->hw_palette = hw_palette;
 
 	/* Settings changed, so we clear the frame buffers */
-	memset(cam->frame[0].buffer, 0, 
-	       cam->nbuffers*w9968cf_get_max_bufsize(cam));
+	memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size);
 
-	DBG(4, "Palette is %s, depth is %d bpp.",
+	DBG(4, "Palette is %s, depth is %u bpp",
 	    symbolic(v4l1_plist, pict.palette), pict.depth)
 
 	return 0;
 
 error:
-	DBG(1, "Failed to change picture settings.")
+	DBG(1, "Failed to change picture settings")
 	return err;
 }
 
@@ -1921,21 +1948,20 @@
 	cam->hw_height = h;
 
 	/* Settings changed, so we clear the frame buffers */
-	memset(cam->frame[0].buffer, 0, 
-	       cam->nbuffers*w9968cf_get_max_bufsize(cam));
+	memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size);
 
-	DBG(4, "The capture area is %dx%d, Offset (x,y)=(%d,%d).", 
+	DBG(4, "The capture area is %dx%d, Offset (x,y)=(%u,%u)", 
 	    win.width, win.height, win.x, win.y)
 
-	PDBGG("x=%d ,y=%d, w=%d, h=%d, ax=%d, ay=%d, s_win.x=%d, s_win.y=%d, "
-	      "cw=%d, ch=%d, win.x=%d ,win.y=%d, win.width=%d, win.height=%d",
+	PDBGG("x=%u ,y=%u, w=%u, h=%u, ax=%u, ay=%u, s_win.x=%u, s_win.y=%u, "
+	      "cw=%u, ch=%u, win.x=%u, win.y=%u, win.width=%u, win.height=%u",
 	      x, y, w, h, ax, ay, s_win.x, s_win.y, cw, ch, win.x, win.y,
 	      win.width, win.height)
 
 	return 0;
 
 error:
-	DBG(1, "Failed to change the capture area size.")
+	DBG(1, "Failed to change the capture area size")
 	return err;
 }
 
@@ -1959,7 +1985,7 @@
   Return the depth corresponding to the given palette.
   Palette _must_ be supported !
   --------------------------------------------------------------------------*/
-static u16 w9968cf_valid_depth(u16 palette)
+static inline u16 w9968cf_valid_depth(u16 palette)
 {
 	u8 i=0;
 	while (w9968cf_formatlist[i].palette != palette)
@@ -1996,10 +2022,12 @@
 	if ((*width < cam->minwidth) || (*height < cam->minheight))
 		return -ERANGE;
 
-	maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION)
-	       && w9968cf_vppmod_present ? W9968CF_MAX_WIDTH : cam->maxwidth;
-	maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION)
-	       && w9968cf_vppmod_present ? W9968CF_MAX_HEIGHT : cam->maxheight;
+	maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) &&
+	       w9968cf_vpp ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth)
+	                   : cam->maxwidth;
+	maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) &&
+	       w9968cf_vpp ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight)
+	                   : cam->maxheight;
 
 	if (*width > maxw)
 		*width = maxw;
@@ -2011,7 +2039,7 @@
 		*height &= ~15L;
 	}
 
-	PDBGG("Window size adjusted w=%d, h=%d ", *width, *height)
+	PDBGG("Window size adjusted w=%u, h=%u ", *width, *height)
 
 	return 0;
 }
@@ -2050,7 +2078,7 @@
 
 	spin_unlock_irqrestore(&cam->flist_lock, lock_flags);
 
-	DBG(6, "Frame #%d pushed into the FIFO list. Position %d.", f_num, f)
+	DBG(6, "Frame #%u pushed into the FIFO list. Position %u", f_num, f)
 }
 
 
@@ -2074,7 +2102,7 @@
 
 	spin_unlock(&cam->flist_lock);
 
-	DBG(6,"Popped frame #%zd from the list.",*framep-&cam->frame[0])
+	DBG(6,"Popped frame #%d from the list", (*framep)->number)
 }
 
 
@@ -2086,7 +2114,7 @@
 w9968cf_postprocess_frame(struct w9968cf_device* cam, 
                           struct w9968cf_frame_t* fr)
 {
-	void *pIn = fr->buffer, *pOut = cam->vpp_buffer, *tmp;
+	void *pIn = fr->buffer, *pOut = cam->frame_vpp.buffer, *tmp;
 	u16 w = cam->window.width,
 	    h = cam->window.height,
 	    d = cam->picture.depth,
@@ -2102,41 +2130,41 @@
 	if (cam->vpp_flag & VPP_DECOMPRESSION) {
 		memcpy(pOut, pIn, fr->length);
 		_PSWAP(pIn, pOut)
-		err = (*w9968cf_vpp_decode)(pIn, fr->length, hw_w, hw_h, pOut);
-		PDBGG("Compressed frame length: %li",(unsigned long)fr->length)
+		err = w9968cf_vpp->decode(pIn, fr->length, hw_w, hw_h, pOut);
+		PDBGG("Compressed frame length: %lu",(unsigned long)fr->length)
 		fr->length = (hw_w*hw_h*hw_d)/8;
 		_PSWAP(pIn, pOut)
 		if (err) {
 			DBG(4, "An error occurred while decoding the frame: "
-			       "%s.", symbolic(decoder_errlist, err))
+			       "%s", symbolic(decoder_errlist, err))
 			return err;
 		} else
 			DBG(6, "Frame decoded")
 	}
 
 	if (cam->vpp_flag & VPP_SWAP_YUV_BYTES) {
-		(*w9968cf_vpp_swap_yuvbytes)(pIn, fr->length);
-		DBG(6, "Original UYVY component ordering changed.")
+		w9968cf_vpp->swap_yuvbytes(pIn, fr->length);
+		DBG(6, "Original UYVY component ordering changed")
 	}
 
 	if (cam->vpp_flag & VPP_UPSCALE) {
-		(*w9968cf_vpp_scale_up)(pIn, pOut, hw_w, hw_h, hw_d, w, h);
+		w9968cf_vpp->scale_up(pIn, pOut, hw_w, hw_h, hw_d, w, h);
 		fr->length = (w*h*hw_d)/8;
 		_PSWAP(pIn, pOut)
-		DBG(6, "Vertical up-scaling done: %d,%d,%dbpp->%d,%d",
+		DBG(6, "Vertical up-scaling done: %u,%u,%ubpp->%u,%u",
 		    hw_w, hw_h, hw_d, w, h)
 	}
 
 	if (cam->vpp_flag & VPP_UYVY_TO_RGBX) {
-		(*w9968cf_vpp_uyvy_to_rgbx)(pIn, fr->length, pOut, fmt, rgb);
+		w9968cf_vpp->uyvy_to_rgbx(pIn, fr->length, pOut, fmt, rgb);
 		fr->length = (w*h*d)/8;
 		_PSWAP(pIn, pOut)
-		DBG(6, "UYVY-16bit to %s conversion done.", 
+		DBG(6, "UYVY-16bit to %s conversion done", 
 		    symbolic(v4l1_plist, fmt))
 	}
 
 	if (pOut == fr->buffer)
-		memcpy(fr->buffer, cam->vpp_buffer, fr->length);
+		memcpy(fr->buffer, cam->frame_vpp.buffer, fr->length);
 
 	return 0;
 }
@@ -2144,7 +2172,7 @@
 
 
 /****************************************************************************
- * CMOS sensor control routines                                             *
+ * Image sensor control routines                                            *
  ****************************************************************************/
 
 static int 
@@ -2184,17 +2212,17 @@
 	struct i2c_client* c = cam->sensor_client;
 	int rc = 0;
 
-	if (c->driver->command) {
-		rc = c->driver->command(cam->sensor_client, cmd, arg);
-		/* The I2C driver returns -EPERM on non-supported controls */
-		return (rc < 0 && rc != -EPERM) ? rc : 0;
-	} else
-		return -ENODEV;
+	if (!c || !c->driver || !c->driver->command)
+		return -EINVAL;
+
+	rc = c->driver->command(c, cmd, arg);
+	/* The I2C driver returns -EPERM on non-supported controls */
+	return (rc < 0 && rc != -EPERM) ? rc : 0;
 }
 
 
 /*--------------------------------------------------------------------------
-  Update some settings of the CMOS sensor.
+  Update some settings of the image sensor.
   Returns: 0 on success, a negative number otherwise.
   --------------------------------------------------------------------------*/
 static int w9968cf_sensor_update_settings(struct w9968cf_device* cam)
@@ -2242,7 +2270,7 @@
 
 
 /*--------------------------------------------------------------------------
-  Get some current picture settings from the CMOS sensor and update the
+  Get some current picture settings from the image sensor and update the
   internal 'picture' structure of the camera.
   Returns: 0 on success, a negative number otherwise.
   --------------------------------------------------------------------------*/
@@ -2270,10 +2298,10 @@
 		return err;
 	cam->picture.hue = v;
 
-	DBG(5, "Got picture settings from the CMOS sensor.")
+	DBG(5, "Got picture settings from the image sensor")
 
 	PDBGG("Brightness, contrast, hue, colour, whiteness are "
-	      "%d,%d,%d,%d,%d.", cam->picture.brightness,cam->picture.contrast,
+	      "%u,%u,%u,%u,%u", cam->picture.brightness,cam->picture.contrast,
 	      cam->picture.hue, cam->picture.colour, cam->picture.whiteness)
 
 	return 0;
@@ -2281,7 +2309,7 @@
 
 
 /*--------------------------------------------------------------------------
-  Update picture settings of the CMOS sensor.
+  Update picture settings of the image sensor.
   Returns: 0 on success, a negative number otherwise.
   --------------------------------------------------------------------------*/
 static int
@@ -2296,7 +2324,7 @@
 		                                 pict.contrast);
 		if (err)
 			goto fail;
-		DBG(4, "Contrast changed from %d to %d.",
+		DBG(4, "Contrast changed from %u to %u",
 		    cam->picture.contrast, pict.contrast)
 		cam->picture.contrast = pict.contrast;
 	}
@@ -2307,7 +2335,7 @@
 		                                 pict.brightness);
 		if (err)
 			goto fail;
-		DBG(4, "Brightness changed from %d to %d.",
+		DBG(4, "Brightness changed from %u to %u",
 		    cam->picture.brightness, pict.brightness)
 		cam->picture.brightness = pict.brightness;
 	}
@@ -2317,7 +2345,7 @@
 		                                 pict.colour);
 		if (err)
 			goto fail;
-		DBG(4, "Colour changed from %d to %d.",
+		DBG(4, "Colour changed from %u to %u",
 		    cam->picture.colour, pict.colour)
 		cam->picture.colour = pict.colour;
 	}
@@ -2327,7 +2355,7 @@
 		                                 pict.hue);
 		if (err)
 			goto fail;
-		DBG(4, "Hue changed from %d to %d.",
+		DBG(4, "Hue changed from %u to %u",
 		    cam->picture.hue, pict.hue)
 		cam->picture.hue = pict.hue;
 	}
@@ -2335,7 +2363,7 @@
 	return 0;
 
 fail:
-	DBG(4, "Failed to change sensor picture setting.")
+	DBG(4, "Failed to change sensor picture setting")
 	return err;
 }
 
@@ -2346,7 +2374,7 @@
  ****************************************************************************/
 
 /*--------------------------------------------------------------------------
-  This function is called when a supported CMOS sensor is detected.
+  This function is called when a supported image sensor is detected.
   Return 0 if the initialization succeeds, a negative number otherwise.
   --------------------------------------------------------------------------*/
 static int w9968cf_sensor_init(struct w9968cf_device* cam)
@@ -2376,7 +2404,7 @@
 			cam->minheight = 48;
 			break;
 		default:
-			DBG(1, "Not supported CMOS sensor detected for %s.",
+			DBG(1, "Not supported image sensor detected for %s",
 			    symbolic(camlist, cam->id))
 			return -EINVAL;
 	}
@@ -2386,7 +2414,7 @@
 		case CC_OV7620:
 			cam->start_cropx = 287;
 			cam->start_cropy = 35;
-			/* Seems to work around a bug in the CMOS sensor */
+			/* Seems to work around a bug in the image sensor */
 			cam->vs_polarity = 1;
 			cam->hs_polarity = 1;
 			break;
@@ -2405,14 +2433,14 @@
 
 	cam->sensor_initialized = 1;
 
-	DBG(2, "%s CMOS sensor initialized.", symbolic(senlist, cam->sensor))
+	DBG(2, "%s image sensor initialized", symbolic(senlist, cam->sensor))
 	return 0;
 
 error:
 	cam->sensor_initialized = 0;
 	cam->sensor = CC_UNKNOWN;
-	DBG(1, "CMOS sensor initialization failed for %s (/dev/video%d). "
-	       "Try to detach and attach this device again.",
+	DBG(1, "Image sensor initialization failed for %s (/dev/video%d). "
+	       "Try to detach and attach this device again",
 	    symbolic(camlist, cam->id), cam->v4ldev->minor)
 	return err;
 }
@@ -2436,7 +2464,6 @@
 
 	cam->users = 0;
 	cam->disconnected = 0;
-	cam->usbdev = udev;
 	cam->id = mod_id;
 	cam->sensor = CC_UNKNOWN;
 	cam->sensor_initialized = 0;
@@ -2521,6 +2548,7 @@
 		else
 			cam->picture.palette = W9968CF_PALETTE_DECOMP_ON;
 	}
+	cam->picture.depth = w9968cf_valid_depth(cam->picture.palette);
 
 	cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1)
 	                 ? (u8)force_rgb[dev_nr] : W9968CF_FORCE_RGB;
@@ -2533,105 +2561,120 @@
 	cam->window.clipcount = 0;
 	cam->window.flags = 0;
 
-	/* If the video post-processing module is not present, some paramaters
-	   must be overridden: */
-	if (!w9968cf_vppmod_present) {
-		if (cam->decompression == 1)
-			cam->decompression = 2;
-		cam->upscaling = 0;
-		if (cam->picture.palette != VIDEO_PALETTE_UYVY)
-			cam->force_palette = 0;
-		cam->picture.palette = VIDEO_PALETTE_UYVY;
-	}
-
-	cam->picture.depth = w9968cf_valid_depth(cam->picture.palette);
-
-	DBG(3, "%s configured with settings #%d:", 
+	DBG(3, "%s configured with settings #%u:",
 	    symbolic(camlist, cam->id), dev_nr)
 	
-	DBG(3, "- Data packet size for USB isochrnous transfer: %d bytes.",
+	DBG(3, "- Data packet size for USB isochrnous transfer: %u bytes",
 	    wMaxPacketSize[cam->altsetting-1])
 	
-	DBG(3, "- Number of requested video frame buffers: %d", 
+	DBG(3, "- Number of requested video frame buffers: %u",
 	    cam->max_buffers)
 
 	if (cam->double_buffer)
-		DBG(3, "- Hardware double buffering enabled.")
+		DBG(3, "- Hardware double buffering enabled")
 	else 
-		DBG(3, "- Hardware double buffering disabled.")
+		DBG(3, "- Hardware double buffering disabled")
 
 	if (cam->filter_type == 0)
-		DBG(3, "- Video filtering disabled.")
+		DBG(3, "- Video filtering disabled")
 	else if (cam->filter_type == 1)
-		DBG(3, "- Video filtering enabled: type 1-2-1.")
+		DBG(3, "- Video filtering enabled: type 1-2-1")
 	else if (cam->filter_type == 2)
-		DBG(3, "- Video filtering enabled: type 2-3-6-3-2.")
+		DBG(3, "- Video filtering enabled: type 2-3-6-3-2")
 
 	if (cam->clamping)
-		DBG(3, "- Video data clamping (CCIR-601 format) enabled.")
+		DBG(3, "- Video data clamping (CCIR-601 format) enabled")
 	else
-		DBG(3, "- Video data clamping (CCIR-601 format) disabled.")
+		DBG(3, "- Video data clamping (CCIR-601 format) disabled")
 
 	if (cam->largeview)
-		DBG(3, "- Large view enabled.")
+		DBG(3, "- Large view enabled")
 	else
-		DBG(3, "- Large view disabled.")
+		DBG(3, "- Large view disabled")
 
 	if ((cam->decompression) == 0 && (!cam->force_palette))
-		DBG(3, "- Decompression disabled.")
+		DBG(3, "- Decompression disabled")
 	else if ((cam->decompression) == 1 && (!cam->force_palette))
-		DBG(3, "- Decompression forced.")
+		DBG(3, "- Decompression forced")
 	else if ((cam->decompression) == 2 && (!cam->force_palette))
-		DBG(3, "- Decompression allowed.")
+		DBG(3, "- Decompression allowed")
 
 	if (cam->upscaling)
-		DBG(3, "- Software image scaling enabled.")
+		DBG(3, "- Software image scaling enabled")
 	else
-		DBG(3, "- Software image scaling disabled.")
+		DBG(3, "- Software image scaling disabled")
 
 	if (cam->force_palette)
-		DBG(3, "- Image palette forced to %s.",
+		DBG(3, "- Image palette forced to %s",
 		    symbolic(v4l1_plist, cam->picture.palette))
 
 	if (cam->force_rgb)
-		DBG(3, "- RGB component ordering will be used instead of BGR.")
+		DBG(3, "- RGB component ordering will be used instead of BGR")
 
 	if (cam->auto_brt)
-		DBG(3, "- Auto brightness enabled.")
+		DBG(3, "- Auto brightness enabled")
 	else
-		DBG(3, "- Auto brightness disabled.")
+		DBG(3, "- Auto brightness disabled")
 
 	if (cam->auto_exp)
-		DBG(3, "- Auto exposure enabled.")
+		DBG(3, "- Auto exposure enabled")
 	else
-		DBG(3, "- Auto exposure disabled.")
+		DBG(3, "- Auto exposure disabled")
 
 	if (cam->backlight)
-		DBG(3, "- Backlight exposure algorithm enabled.")
+		DBG(3, "- Backlight exposure algorithm enabled")
 	else
-		DBG(3, "- Backlight exposure algorithm disabled.")
+		DBG(3, "- Backlight exposure algorithm disabled")
 
 	if (cam->mirror)
-		DBG(3, "- Mirror enabled.")
+		DBG(3, "- Mirror enabled")
 	else
-		DBG(3, "- Mirror disabled.")
+		DBG(3, "- Mirror disabled")
 
 	if (cam->bandfilt)
-		DBG(3, "- Banding filter enabled.")
+		DBG(3, "- Banding filter enabled")
 	else
-		DBG(3, "- Banding filter disabled.")
+		DBG(3, "- Banding filter disabled")
 
-	DBG(3, "- Power lighting frequency: %d", cam->lightfreq)
+	DBG(3, "- Power lighting frequency: %u", cam->lightfreq)
 
 	if (cam->clockdiv == -1)
-		DBG(3, "- Automatic clock divisor enabled.")
+		DBG(3, "- Automatic clock divisor enabled")
 	else
 		DBG(3, "- Clock divisor: %d", cam->clockdiv)
 
 	if (cam->monochrome)
-		DBG(3, "- CMOS sensor used as monochrome.")
+		DBG(3, "- Image sensor used as monochrome")
 	else
-		DBG(3, "- CMOS sensor not used as monochrome.")
+		DBG(3, "- Image sensor not used as monochrome")
+}
+
+
+/*--------------------------------------------------------------------------
+  If the video post-processing module is not loaded, some parameters
+  must be overridden.
+  --------------------------------------------------------------------------*/
+static void w9968cf_adjust_configuration(struct w9968cf_device* cam)
+{
+	if (!w9968cf_vpp) {
+		if (cam->decompression == 1) {
+			cam->decompression = 2;
+			DBG(2, "Video post-processing module not found: "
+			       "'decompression' parameter forced to 2")
+		}
+		if (cam->upscaling) {
+			cam->upscaling = 0;
+			DBG(2, "Video post-processing module not found: "
+			       "'upscaling' parameter forced to 0")
+		}
+		if (cam->picture.palette != VIDEO_PALETTE_UYVY) {
+			cam->force_palette = 0;
+			DBG(2, "Video post-processing module not found: "
+			       "'force_palette' parameter forced to 0")
+		}
+		cam->picture.palette = VIDEO_PALETTE_UYVY;
+		cam->picture.depth = w9968cf_valid_depth(cam->picture.palette);
+	}
 }
 
 
@@ -2654,8 +2697,6 @@
 	kfree(cam->data_buffer);
 
 	up(&w9968cf_devlist_sem);
-
-	DBG(5, "Resources released.")
 }
 
 
@@ -2669,38 +2710,45 @@
 	struct w9968cf_device* cam;
 	int err;
 
+	/* This the only safe way to prevent race conditions with disconnect */
+	if (!down_read_trylock(&w9968cf_disconnect))
+		return -ERESTARTSYS;
+
 	cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
 
 	down(&cam->dev_sem);
 
 	if (cam->sensor == CC_UNKNOWN) {
-		DBG(2, "No supported CMOS sensor has been detected by the "
+		DBG(2, "No supported image sensor has been detected by the "
 		       "'ovcamchip' module for the %s (/dev/video%d). Make "
-		       "sure it is loaded *before* the 'w9968cf' module.", 
+		       "sure it is loaded *before* (re)connecting the camera.",
 		    symbolic(camlist, cam->id), cam->v4ldev->minor)
 		up(&cam->dev_sem);
+		up_read(&w9968cf_disconnect);
 		return -ENODEV;
 	}
 
 	if (cam->users) {
-		DBG(2, "%s (/dev/video%d) has been already occupied by '%s'.",
+		DBG(2, "%s (/dev/video%d) has been already occupied by '%s'",
 		    symbolic(camlist, cam->id),cam->v4ldev->minor,cam->command)
 		if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) {
 			up(&cam->dev_sem);
+			up_read(&w9968cf_disconnect);
 			return -EWOULDBLOCK;
 		}
-retry:
 		up(&cam->dev_sem);
-		err = wait_event_interruptible(cam->open, cam->disconnected ||
-		                               (cam->users == 0));
-		if (err)
+		err = wait_event_interruptible_exclusive(cam->open,
+		                                         cam->disconnected ||
+		                                         !cam->users);
+		if (err) {
+			up_read(&w9968cf_disconnect);
 			return err;
-		if (cam->disconnected)
+		}
+		if (cam->disconnected) {
+			up_read(&w9968cf_disconnect);
 			return -ENODEV;
+		}
 		down(&cam->dev_sem);
-		/*recheck - there may be several waiters */
-		if (cam->users)
-			goto retry;
 	}
 
 	DBG(5, "Opening '%s', /dev/video%d ...",
@@ -2709,8 +2757,9 @@
 	cam->streaming = 0;
 	cam->misconfigured = 0;
 
-	if (!w9968cf_vppmod_present)
-		w9968cf_vppmod_detect();
+	if (!w9968cf_vpp)
+		if ((err = w9968cf_vppmod_detect(cam)))
+			goto out;
 
 	if ((err = w9968cf_allocate_memory(cam)))
 		goto deallocate_memory;
@@ -2728,15 +2777,19 @@
 
 	init_waitqueue_head(&cam->wait_queue);
 
+	DBG(5, "Video device is open")
+
 	up(&cam->dev_sem);
+	up_read(&w9968cf_disconnect);
 
-	DBG(5, "Video device is open.")
 	return 0;
 
 deallocate_memory:
 	w9968cf_deallocate_memory(cam);
-	DBG(2, "Failed to open the video device.")
+out:
+	DBG(2, "Failed to open the video device")
 	up(&cam->dev_sem);
+	up_read(&w9968cf_disconnect);
 	return err;
 }
 
@@ -2751,6 +2804,8 @@
 
 	w9968cf_stop_transfer(cam);
 
+	w9968cf_vppmod_release(cam);
+
 	if (cam->disconnected) {
 		w9968cf_release_resources(cam);
 		up(&cam->dev_sem);
@@ -2760,10 +2815,9 @@
 
 	cam->users--;
 	w9968cf_deallocate_memory(cam);
+	wake_up_interruptible_nr(&cam->open, 1);
 
-	wake_up_interruptible(&cam->open);
-
-	DBG(5, "Video device closed.")
+	DBG(5, "Video device closed")
 	up(&cam->dev_sem);
 	return 0;
 }
@@ -2785,7 +2839,7 @@
 		return -ERESTARTSYS;
 
 	if (cam->disconnected) {
-		DBG(2, "Device not present.")
+		DBG(2, "Device not present")
 		up(&cam->fileop_sem);
 		return -ENODEV;
 	}
@@ -2817,7 +2871,7 @@
 
 	fr = (cam->frame[0].status == F_READY) ? &cam->frame[0]:&cam->frame[1];
 
-	if (w9968cf_vppmod_present)
+	if (w9968cf_vpp)
 		w9968cf_postprocess_frame(cam, fr);
 
 	if (count > fr->length)
@@ -2832,7 +2886,7 @@
 
 	fr->status = F_UNUSED;
 
-	DBG(5, "%zd bytes read.", count)
+	DBG(5, "%zu bytes read", count)
 
 	up(&cam->fileop_sem);
 	return count;
@@ -2844,25 +2898,25 @@
 	struct w9968cf_device* cam = (struct w9968cf_device*)
 	                             video_get_drvdata(video_devdata(filp));
 	unsigned long vsize = vma->vm_end - vma->vm_start,
-	              psize = cam->nbuffers * w9968cf_get_max_bufsize(cam),
+	              psize = cam->nbuffers * cam->frame[0].size,
 	              start = vma->vm_start,
 	              pos = (unsigned long)cam->frame[0].buffer,
 	              page;
 
 	if (cam->disconnected) {
-		DBG(2, "Device not present.")
+		DBG(2, "Device not present")
 		return -ENODEV;
 	}
 
 	if (cam->misconfigured) {
-		DBG(2, "The camera is misconfigured. Close and open it again.")
+		DBG(2, "The camera is misconfigured. Close and open it again")
 		return -EIO;
 	}
 
-	PDBGG("mmapping %li bytes...", vsize)
+	PDBGG("mmapping %lu bytes...", vsize)
 
-        if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT))
-		return -EAGAIN;
+	if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT))
+		return -EINVAL;
 
 	while (vsize > 0) {
 		page = kvirt_to_pa(pos) + vma->vm_pgoff;
@@ -2871,10 +2925,10 @@
 			return -EAGAIN;
 		start += PAGE_SIZE;
 		pos += PAGE_SIZE;
-		vsize = (vsize > PAGE_SIZE) ? vsize-PAGE_SIZE : 0;
+		vsize -= PAGE_SIZE;
 	}
 
-	DBG(5, "mmap method successfully called.")
+	DBG(5, "mmap method successfully called")
 	return 0;
 }
 
@@ -2892,7 +2946,7 @@
 		return -ERESTARTSYS;
 
 	if (cam->disconnected) {
-		DBG(2, "Device not present.")
+		DBG(2, "Device not present")
 		up(&cam->fileop_sem);
 		return -ENODEV;
 	}
@@ -2903,19 +2957,17 @@
 		return -EIO;
 	}
 
-	err = w9968cf_v4l_ioctl(inode, filp, cmd, (void* )arg);
+	err = w9968cf_v4l_ioctl(inode, filp, cmd, (void __user *)arg);
 
 	up(&cam->fileop_sem);
 	return err;
 }
 
 
-static int 
-w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
-                  unsigned int cmd, void* arg)
+static int w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
+                             unsigned int cmd, void __user * arg)
 {
 	struct w9968cf_device* cam;
-	void __user *user_arg = (void __user *)arg;
 	const char* v4l1_ioctls[] = {
 		"?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", 
 		"GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF",
@@ -2944,22 +2996,24 @@
 		};
 		sprintf(cap.name, "W996[87]CF USB Camera #%d", 
 		        cam->v4ldev->minor);
-		cap.maxwidth = (cam->upscaling && w9968cf_vppmod_present)
-		               ? W9968CF_MAX_WIDTH : cam->maxwidth;
-		cap.maxheight = (cam->upscaling && w9968cf_vppmod_present)
-		                ? W9968CF_MAX_HEIGHT : cam->maxheight;
+		cap.maxwidth = (cam->upscaling && w9968cf_vpp)
+		               ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth) 
+		                 : cam->maxwidth;
+		cap.maxheight = (cam->upscaling && w9968cf_vpp)
+		                ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight)
+		                  : cam->maxheight;
 
-		if (copy_to_user(user_arg, &cap, sizeof(cap)))
+		if (copy_to_user(arg, &cap, sizeof(cap)))
 			return -EFAULT;
 
-		DBG(5, "VIDIOCGCAP successfully called.")
+		DBG(5, "VIDIOCGCAP successfully called")
 		return 0;
 	}
 
 	case VIDIOCGCHAN: /* get video channel informations */
 	{
 		struct video_channel chan;
-		if (copy_from_user(&chan, user_arg, sizeof(chan)))
+		if (copy_from_user(&chan, arg, sizeof(chan)))
 			return -EFAULT;
 
 		if (chan.channel != 0)
@@ -2971,10 +3025,10 @@
 		chan.type = VIDEO_TYPE_CAMERA;
 		chan.norm = VIDEO_MODE_AUTO;
 
-		if (copy_to_user(user_arg, &chan, sizeof(chan)))
+		if (copy_to_user(arg, &chan, sizeof(chan)))
 			return -EFAULT;
 
-		DBG(5, "VIDIOCGCHAN successfully called.")
+		DBG(5, "VIDIOCGCHAN successfully called")
 		return 0;
 	}
 
@@ -2982,13 +3036,13 @@
 	{
 		struct video_channel chan;
 
-		if (copy_from_user(&chan, user_arg, sizeof(chan)))
+		if (copy_from_user(&chan, arg, sizeof(chan)))
 			return -EFAULT;
 
 		if (chan.channel != 0)
 			return -EINVAL;
 
-		DBG(5, "VIDIOCSCHAN successfully called.")
+		DBG(5, "VIDIOCSCHAN successfully called")
 		return 0;
 	}
 
@@ -2997,10 +3051,10 @@
 		if (w9968cf_sensor_get_picture(cam))
 			return -EIO;
 
-		if (copy_to_user(user_arg, &cam->picture, sizeof(cam->picture)))
+		if (copy_to_user(arg, &cam->picture, sizeof(cam->picture)))
 			return -EFAULT;
 
-		DBG(5, "VIDIOCGPICT successfully called.")
+		DBG(5, "VIDIOCGPICT successfully called")
 		return 0;
 	}
 
@@ -3009,19 +3063,19 @@
 		struct video_picture pict;
 		int err = 0;
 
-		if (copy_from_user(&pict, user_arg, sizeof(pict)))
+		if (copy_from_user(&pict, arg, sizeof(pict)))
 			return -EFAULT;
 
-		if ( (cam->force_palette || !w9968cf_vppmod_present) 
+		if ( (cam->force_palette || !w9968cf_vpp) 
 		     && pict.palette != cam->picture.palette ) {
-			DBG(4, "Palette %s rejected. Only %s is allowed.",
+			DBG(4, "Palette %s rejected: only %s is allowed",
 			    symbolic(v4l1_plist, pict.palette),
 			    symbolic(v4l1_plist, cam->picture.palette))
 			return -EINVAL;
 		}
 
 		if (!w9968cf_valid_palette(pict.palette)) {
-			DBG(4, "Palette %s not supported. VIDIOCSPICT failed.",
+			DBG(4, "Palette %s not supported. VIDIOCSPICT failed",
 			    symbolic(v4l1_plist, pict.palette))
 			return -EINVAL;
 		}
@@ -3030,14 +3084,14 @@
 		   if (cam->decompression == 0) {
 		      if (w9968cf_need_decompression(pict.palette)) {
 		         DBG(4, "Decompression disabled: palette %s is not "
-		                "allowed. VIDIOCSPICT failed.",
+		                "allowed. VIDIOCSPICT failed",
 		             symbolic(v4l1_plist, pict.palette))
 		         return -EINVAL;
 		      }
 		   } else if (cam->decompression == 1) {
 		      if (!w9968cf_need_decompression(pict.palette)) {
 		         DBG(4, "Decompression forced: palette %s is not "
-		                "allowed. VIDIOCSPICT failed.",
+		                "allowed. VIDIOCSPICT failed",
 		             symbolic(v4l1_plist, pict.palette))
 		         return -EINVAL;
 		      }
@@ -3045,8 +3099,8 @@
 		}
 
 		if (pict.depth != w9968cf_valid_depth(pict.palette)) {
-			DBG(4, "Requested depth %d bpp is not valid for %s "
-			       "palette: ignored and changed to %d bpp.", 
+			DBG(4, "Requested depth %u bpp is not valid for %s "
+			       "palette: ignored and changed to %u bpp", 
 			    pict.depth, symbolic(v4l1_plist, pict.palette),
 			    w9968cf_valid_depth(pict.palette))
 			pict.depth = w9968cf_valid_depth(pict.palette);
@@ -3079,7 +3133,7 @@
 			return -EIO;
 
 
-		DBG(5, "VIDIOCSPICT successfully called.")
+		DBG(5, "VIDIOCSPICT successfully called")
 		return 0;
 	}
 
@@ -3088,11 +3142,11 @@
 		struct video_window win;
 		int err = 0;
 
-		if (copy_from_user(&win, user_arg, sizeof(win)))
+		if (copy_from_user(&win, arg, sizeof(win)))
 			return -EFAULT;
 
-		DBG(6, "VIDIOCSWIN called: clipcount=%d, flags=%d, "
-		       "x=%d, y=%d, %dx%d", win.clipcount, win.flags,
+		DBG(6, "VIDIOCSWIN called: clipcount=%d, flags=%u, "
+		       "x=%u, y=%u, %ux%u", win.clipcount, win.flags,
 		    win.x, win.y, win.width, win.height)
 
 		if (win.clipcount != 0 || win.flags != 0)
@@ -3100,8 +3154,8 @@
 
 		if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width,
 		                                      (u16*)&win.height))) {
-			DBG(4, "Resolution not supported (%dx%d)."
-			       "VIDIOCSWIN failed.", win.width, win.height)
+			DBG(4, "Resolution not supported (%ux%u). "
+			       "VIDIOCSWIN failed", win.width, win.height)
 			return err;
 		}
 
@@ -3142,10 +3196,10 @@
 
 	case VIDIOCGWIN: /* get current window properties */
 	{
-		if (copy_to_user(user_arg, &cam->window, sizeof(struct video_window)))
+		if (copy_to_user(arg,&cam->window,sizeof(struct video_window)))
 			return -EFAULT;
 
-		DBG(5, "VIDIOCGWIN successfully called.")
+		DBG(5, "VIDIOCGWIN successfully called")
 		return 0;
 	}
 
@@ -3154,16 +3208,16 @@
 		struct video_mbuf mbuf;
 		u8 i;
 
-		mbuf.size = cam->nbuffers * w9968cf_get_max_bufsize(cam);
+		mbuf.size = cam->nbuffers * cam->frame[0].size;
 		mbuf.frames = cam->nbuffers;
 		for (i = 0; i < cam->nbuffers; i++)
 			mbuf.offsets[i] = (unsigned long)cam->frame[i].buffer -
 			                  (unsigned long)cam->frame[0].buffer;
 
-		if (copy_to_user(user_arg, &mbuf, sizeof(mbuf)))
+		if (copy_to_user(arg, &mbuf, sizeof(mbuf)))
 			return -EFAULT;
 
-		DBG(5, "VIDIOCGMBUF successfully called.")
+		DBG(5, "VIDIOCGMBUF successfully called")
 		return 0;
 	}
 
@@ -3173,22 +3227,22 @@
 		struct w9968cf_frame_t* fr;
 		int err = 0;
 
-		if (copy_from_user(&mmap, user_arg, sizeof(mmap)))
+		if (copy_from_user(&mmap, arg, sizeof(mmap)))
 			return -EFAULT;
 
-		DBG(6, "VIDIOCMCAPTURE called: frame #%d, format=%s, %dx%d",
+		DBG(6, "VIDIOCMCAPTURE called: frame #%u, format=%s, %dx%d",
 		    mmap.frame, symbolic(v4l1_plist, mmap.format), 
 		    mmap.width, mmap.height)
 
 		if (mmap.frame >= cam->nbuffers) {
-			DBG(4, "Invalid frame number (%d). "
-			       "VIDIOCMCAPTURE failed.", mmap.frame)
+			DBG(4, "Invalid frame number (%u). "
+			       "VIDIOCMCAPTURE failed", mmap.frame)
 			return -EINVAL;
 		}
 
 		if (mmap.format!=cam->picture.palette && 
-		    (cam->force_palette || !w9968cf_vppmod_present)) {
-			DBG(4, "Palette %s rejected. Only %s is allowed.",
+		    (cam->force_palette || !w9968cf_vpp)) {
+			DBG(4, "Palette %s rejected: only %s is allowed",
 			    symbolic(v4l1_plist, mmap.format),
 			    symbolic(v4l1_plist, cam->picture.palette))
 			return -EINVAL;
@@ -3196,7 +3250,7 @@
 
 		if (!w9968cf_valid_palette(mmap.format)) {
 			DBG(4, "Palette %s not supported. "
-			       "VIDIOCMCAPTURE failed.", 
+			       "VIDIOCMCAPTURE failed", 
 			    symbolic(v4l1_plist, mmap.format))
 			return -EINVAL;
 		}
@@ -3205,14 +3259,14 @@
 		   if (cam->decompression == 0) {
 		      if (w9968cf_need_decompression(mmap.format)) {
 		         DBG(4, "Decompression disabled: palette %s is not "
-		                "allowed. VIDIOCSPICT failed.",
+		                "allowed. VIDIOCSPICT failed",
 		             symbolic(v4l1_plist, mmap.format))
 		         return -EINVAL;
 		      }
 		   } else if (cam->decompression == 1) {
 		      if (!w9968cf_need_decompression(mmap.format)) {
 		         DBG(4, "Decompression forced: palette %s is not "
-		                "allowed. VIDIOCSPICT failed.",
+		                "allowed. VIDIOCSPICT failed",
 		             symbolic(v4l1_plist, mmap.format))
 		         return -EINVAL;
 		      }
@@ -3222,7 +3276,7 @@
 		if ((err = w9968cf_adjust_window_size(cam, (u16*)&mmap.width, 
 		                                      (u16*)&mmap.height))) {
 			DBG(4, "Resolution not supported (%dx%d). "
-			       "VIDIOCMCAPTURE failed.",
+			       "VIDIOCMCAPTURE failed",
 			    mmap.width, mmap.height)
 			return err;
 		}
@@ -3239,7 +3293,7 @@
 			if(*cam->requested_frame
 			   || cam->frame_current->queued) {
 				DBG(6, "VIDIOCMCAPTURE. Change settings for "
-				       "frame #%d: %dx%d, format %s. Wait...",
+				       "frame #%u: %dx%d, format %s. Wait...",
 				    mmap.frame, mmap.width, mmap.height,
 			            symbolic(v4l1_plist, mmap.format))
 				err = wait_event_interruptible
@@ -3274,7 +3328,7 @@
 
 		} else 	if (fr->queued) {
 
-			DBG(6, "Wait until frame #%d is free.", mmap.frame)
+			DBG(6, "Wait until frame #%u is free", mmap.frame)
 			
 			err = wait_event_interruptible(cam->wait_queue, 
 			                               cam->disconnected ||
@@ -3286,7 +3340,7 @@
 		}
 
 		w9968cf_push_frame(cam, mmap.frame);
-		DBG(5, "VIDIOCMCAPTURE(%d): successfully called.", mmap.frame)
+		DBG(5, "VIDIOCMCAPTURE(%u): successfully called", mmap.frame)
 		return 0;
 	}
 
@@ -3296,23 +3350,23 @@
 		struct w9968cf_frame_t* fr;
 		int err = 0;
 
-		if (copy_from_user(&f_num, user_arg, sizeof(f_num)))
+		if (copy_from_user(&f_num, arg, sizeof(f_num)))
 			return -EFAULT;
 
 		if (f_num >= cam->nbuffers) {
-			DBG(4, "Invalid frame number (%d). "
-			       "VIDIOCMCAPTURE failed.", f_num)
+			DBG(4, "Invalid frame number (%u). "
+			       "VIDIOCMCAPTURE failed", f_num)
 			return -EINVAL;
 		}
 
-		DBG(6, "VIDIOCSYNC called for frame #%d", f_num)
+		DBG(6, "VIDIOCSYNC called for frame #%u", f_num)
 
 		fr = &cam->frame[f_num];
 
 		switch (fr->status) {
 		case F_UNUSED:
 			if (!fr->queued) {
-				DBG(4, "VIDIOSYNC: Frame #%d not requested!",
+				DBG(4, "VIDIOSYNC: Frame #%u not requested!",
 				    f_num)
 				return -EFAULT;
 			}
@@ -3330,12 +3384,12 @@
 			break;
 		}
 
-		if (w9968cf_vppmod_present)
+		if (w9968cf_vpp)
 			w9968cf_postprocess_frame(cam, fr);
 
 		fr->status = F_UNUSED;
 
-		DBG(5, "VIDIOCSYNC(%d) successfully called.", f_num)
+		DBG(5, "VIDIOCSYNC(%u) successfully called", f_num)
 		return 0;
 	}
 
@@ -3349,10 +3403,10 @@
 			.teletext = VIDEO_NO_UNIT,
 		};
 
-		if (copy_to_user(user_arg, &unit, sizeof(unit)))
+		if (copy_to_user(arg, &unit, sizeof(unit)))
 			return -EFAULT;
 
-		DBG(5, "VIDIOCGUNIT successfully called.")
+		DBG(5, "VIDIOCGUNIT successfully called")
 		return 0;
 	}
 
@@ -3361,17 +3415,17 @@
 
 	case VIDIOCGFBUF:
 	{
-		if (clear_user(user_arg, sizeof(struct video_buffer)))
+		if (clear_user(arg, sizeof(struct video_buffer)))
 			return -EFAULT;
 
-		DBG(5, "VIDIOCGFBUF successfully called.")
+		DBG(5, "VIDIOCGFBUF successfully called")
 		return 0;
 	}
 
 	case VIDIOCGTUNER:
 	{
 		struct video_tuner tuner;
-		if (copy_from_user(&tuner, user_arg, sizeof(tuner)))
+		if (copy_from_user(&tuner, arg, sizeof(tuner)))
 			return -EFAULT;
 
 		if (tuner.tuner != 0)
@@ -3384,17 +3438,17 @@
 		tuner.mode = VIDEO_MODE_AUTO;
 		tuner.signal = 0xffff;
 
-		if (copy_to_user(user_arg, &tuner, sizeof(tuner)))
+		if (copy_to_user(arg, &tuner, sizeof(tuner)))
 			return -EFAULT;
 
-		DBG(5, "VIDIOCGTUNER successfully called.")
+		DBG(5, "VIDIOCGTUNER successfully called")
 		return 0;
 	}
 
 	case VIDIOCSTUNER:
 	{
 		struct video_tuner tuner;
-		if (copy_from_user(&tuner, user_arg, sizeof(tuner)))
+		if (copy_from_user(&tuner, arg, sizeof(tuner)))
 			return -EFAULT;
 
 		if (tuner.tuner != 0)
@@ -3403,7 +3457,7 @@
 		if (tuner.mode != VIDEO_MODE_AUTO)
 			return -EINVAL;
 
-		DBG(5, "VIDIOCSTUNER successfully called.")
+		DBG(5, "VIDIOCSTUNER successfully called")
 		return 0;
 	}
 
@@ -3423,7 +3477,7 @@
 		       "(type 0x%01X, "
 		       "n. 0x%01X, "
 		       "dir. 0x%01X, " 
-		       "size 0x%02X).",
+		       "size 0x%02X)",
 		    V4L1_IOCTL(cmd),
 		    _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd))
 
@@ -3434,7 +3488,7 @@
 		       "type 0x%01X, "
 		       "n. 0x%01X, "
 		       "dir. 0x%01X, "
-		       "size 0x%02X.",
+		       "size 0x%02X",
 		    V4L1_IOCTL(cmd),
 		    _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd))
 
@@ -3480,15 +3534,27 @@
 	if (udev->descriptor.idVendor  == winbond_id_table[0].idVendor &&
 	    udev->descriptor.idProduct == winbond_id_table[0].idProduct)
 		mod_id = W9968CF_MOD_CLVBWGP; /* see camlist[] table */
-
 	else if (udev->descriptor.idVendor  == winbond_id_table[1].idVendor &&
 	         udev->descriptor.idProduct == winbond_id_table[1].idProduct)
 		mod_id = W9968CF_MOD_GENERIC; /* see camlist[] table */
-
 	else
 		return -ENODEV;
 
-	DBG(2, "%s detected.", symbolic(camlist, mod_id))
+	cam = (struct w9968cf_device*)
+	          kmalloc(sizeof(struct w9968cf_device), GFP_KERNEL);
+	if (!cam)
+		return -ENOMEM;
+
+	memset(cam, 0, sizeof(*cam));
+
+	init_MUTEX(&cam->dev_sem);
+	down(&cam->dev_sem);
+
+	cam->usbdev = udev;
+	/* NOTE: a local copy is used to avoid possible race conditions */
+	memcpy(&cam->dev, &udev->dev, sizeof(struct device));
+
+	DBG(2, "%s detected", symbolic(camlist, mod_id))
 
 	if (simcams > W9968CF_MAX_DEVICES)
 		simcams = W9968CF_SIMCAMS;
@@ -3501,27 +3567,15 @@
 
 	if (sc >= simcams) {
 		DBG(2, "Device rejected: too many connected cameras "
-		       "(max. %d)", simcams)
-		return -EPERM;
-	}
-
-	cam = (struct w9968cf_device*)
-	          kmalloc(sizeof(struct w9968cf_device), GFP_KERNEL);
-
-	if (!cam) {
-		DBG(1, "Couldn't allocate %zd bytes of kernel memory.",
-		    sizeof(struct w9968cf_device))
-		err = -ENOMEM;
+		       "(max. %u)", simcams)
+		err = -EPERM;
 		goto fail;
 	}
-	memset(cam, 0, sizeof(*cam));
 
-	init_MUTEX(&cam->dev_sem);
-	down(&cam->dev_sem);
 
 	/* Allocate 2 bytes of memory for camera control USB transfers */
 	if (!(cam->control_buffer = (u16*)kmalloc(2, GFP_KERNEL))) {
-		DBG(1,"Couldn't allocate memory for camera control transfers.")
+		DBG(1,"Couldn't allocate memory for camera control transfers")
 		err = -ENOMEM;
 		goto fail;
 	}
@@ -3530,7 +3584,7 @@
 	/* Allocate 8 bytes of memory for USB data transfers to the FSB */
 	if (!(cam->data_buffer = (u16*)kmalloc(8, GFP_KERNEL))) {
 		DBG(1, "Couldn't allocate memory for data "
-		       "transfers to the FSB.")
+		       "transfers to the FSB")
 		err = -ENOMEM;
 		goto fail;
 	}
@@ -3539,7 +3593,7 @@
 	/* Register the V4L device */
 	cam->v4ldev = video_device_alloc();
 	if (!cam->v4ldev) {
-		DBG(1, "Could not allocate memory for a V4L structure.")
+		DBG(1, "Could not allocate memory for a V4L structure")
 		err = -ENOMEM;
 		goto fail;
 	}
@@ -3552,19 +3606,20 @@
 	cam->v4ldev->minor = video_nr[dev_nr];
 	cam->v4ldev->release = video_device_release;
 	video_set_drvdata(cam->v4ldev, cam);
+	cam->v4ldev->dev = &cam->dev;
 
 	err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
 	                            video_nr[dev_nr]);
 	if (err) {
-		DBG(1, "V4L device registration failed.")
+		DBG(1, "V4L device registration failed")
 		if (err == -ENFILE && video_nr[dev_nr] == -1)
-			DBG(2, "Couldn't find a free /dev/videoX node.")
+			DBG(2, "Couldn't find a free /dev/videoX node")
 		video_nr[dev_nr] = -1;
 		dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0;
 		goto fail;
 	}
 
-	DBG(2, "V4L device registered as /dev/video%d.", cam->v4ldev->minor)
+	DBG(2, "V4L device registered as /dev/video%d", cam->v4ldev->minor)
 
 	/* Set some basic constants */
 	w9968cf_configure_camera(cam, udev, mod_id, dev_nr);
@@ -3579,22 +3634,19 @@
 
 	w9968cf_i2c_init(cam);
 
-	up(&cam->dev_sem);
-
 	usb_set_intfdata(intf, cam);
+	up(&cam->dev_sem);
 	return 0;
 
 fail: /* Free unused memory */
-	if (cam) {
-		if (cam->control_buffer)
-			kfree(cam->control_buffer);
-		if (cam->data_buffer)
-			kfree(cam->data_buffer);
-		if (cam->v4ldev)
-			video_device_release(cam->v4ldev);
-		up(&cam->dev_sem);
-		kfree(cam);
-	}
+	if (cam->control_buffer)
+		kfree(cam->control_buffer);
+	if (cam->data_buffer)
+		kfree(cam->data_buffer);
+	if (cam->v4ldev)
+		video_device_release(cam->v4ldev);
+	up(&cam->dev_sem);
+	kfree(cam);
 	return err;
 }
 
@@ -3604,28 +3656,26 @@
 	struct w9968cf_device* cam = 
 	   (struct w9968cf_device*)usb_get_intfdata(intf);
 
+	down_write(&w9968cf_disconnect);
+
 	if (cam) {
 		/* Prevent concurrent accesses to data */
 		down(&cam->dev_sem); 
 
-		cam->streaming = 0;
 		cam->disconnected = 1;
 
 		DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id))
 
-		if (waitqueue_active(&cam->open))
-			wake_up_interruptible(&cam->open);
+		wake_up_interruptible_all(&cam->open);
 
 		if (cam->users) {
 			DBG(2, "The device is open (/dev/video%d)! "
 			       "Process name: %s. Deregistration and memory "
 			       "deallocation are deferred on close.",
 			    cam->v4ldev->minor, cam->command)
-
 			cam->misconfigured = 1;
-
-			if (waitqueue_active(&cam->wait_queue))
-				wake_up_interruptible(&cam->wait_queue);
+			w9968cf_stop_transfer(cam);
+			wake_up_interruptible(&cam->wait_queue);
 		} else
 			w9968cf_release_resources(cam);
 
@@ -3635,7 +3685,7 @@
 			kfree(cam);
 	}
 
-	usb_set_intfdata(intf, NULL);
+	up_write(&w9968cf_disconnect);
 }
 
 
@@ -3653,48 +3703,103 @@
  * Module init, exit and intermodule communication                          *
  ****************************************************************************/
 
-static int w9968cf_vppmod_detect(void)
+static int w9968cf_vppmod_detect(struct w9968cf_device* cam)
 {
-	w9968cf_vpp_init_decoder = inter_module_get("w9968cf_init_decoder");
-
-	if (!w9968cf_vpp_init_decoder) {
+	if (!w9968cf_vpp)
 		if (vppmod_load)
-			w9968cf_vpp_init_decoder = inter_module_get_request
-		                                  ( "w9968cf_init_decoder",
-			                            "w9968cf-vpp" );
-		if (!w9968cf_vpp_init_decoder) {
-			w9968cf_vppmod_present = 0;
-			DBG(4, "Video post-processing module not detected.")
-			return -ENODEV;
-		}
+			request_module("w9968cf-vpp");
+
+	down(&w9968cf_vppmod_lock);
+
+	if (!w9968cf_vpp) {
+		DBG(4, "Video post-processing module not detected")
+		w9968cf_adjust_configuration(cam);
+		goto out;
+	}
+
+	if (!try_module_get(w9968cf_vpp->owner)) {
+		DBG(1, "Couldn't increment the reference count of "
+		       "the video post-processing module")
+		up(&w9968cf_vppmod_lock);
+		return -ENOSYS;
 	}
 
-	w9968cf_vpp_check_headers = inter_module_get("w9968cf_check_headers");
-	w9968cf_vpp_decode = inter_module_get("w9968cf_decode");
-	w9968cf_vpp_swap_yuvbytes = inter_module_get("w9968cf_swap_yuvbytes");
-	w9968cf_vpp_uyvy_to_rgbx = inter_module_get("w9968cf_uyvy_to_rgbx");
-	w9968cf_vpp_scale_up = inter_module_get("w9968cf_scale_up");
+	w9968cf_vpp->busy++;
+
+	DBG(5, "Video post-processing module detected")
+
+out:
+	up(&w9968cf_vppmod_lock);
+	return 0;
+}
+
+
+static void w9968cf_vppmod_release(struct w9968cf_device* cam)
+{
+	down(&w9968cf_vppmod_lock);
+
+	if (w9968cf_vpp && w9968cf_vpp->busy) {
+		module_put(w9968cf_vpp->owner);
+		w9968cf_vpp->busy--;
+		wake_up(&w9968cf_vppmod_wait);
+		DBG(5, "Video post-processing module released")
+	}
+
+	up(&w9968cf_vppmod_lock);
+}
+
+
+int w9968cf_vppmod_register(struct w9968cf_vpp_t* vpp)
+{
+	down(&w9968cf_vppmod_lock);
 
-	w9968cf_vppmod_present = 1;
+	if (w9968cf_vpp) {
+		KDBG(1, "Video post-processing module already registered")
+		up(&w9968cf_vppmod_lock);
+		return -EINVAL;
+	}
 
-	/* Initialization */
-	(*w9968cf_vpp_init_decoder)();
+	w9968cf_vpp = vpp;
+	w9968cf_vpp->busy = 0;
 
-	DBG(2, "Video post-processing module detected.")
+	KDBG(2, "Video post-processing module registered")
+	up(&w9968cf_vppmod_lock);
 	return 0;
 }
 
 
-static void w9968cf_vppmod_release(void)
+int w9968cf_vppmod_deregister(struct w9968cf_vpp_t* vpp)
 {
-	inter_module_put("w9968cf_init_decoder");
-	inter_module_put("w9968cf_check_headers");
-	inter_module_put("w9968cf_decode");
-	inter_module_put("w9968cf_swap_yuvbytes");
-	inter_module_put("w9968cf_uyvy_to_rgbx");
-	inter_module_put("w9968cf_scale_up");
+	down(&w9968cf_vppmod_lock);
+
+	if (!w9968cf_vpp) {
+		up(&w9968cf_vppmod_lock);
+		return -EINVAL;
+	}
 
-	DBG(2, "Video post-processing module released.")
+	if (w9968cf_vpp != vpp) {
+		KDBG(1, "Only the owner can unregister the video "
+		        "post-processing module")
+		up(&w9968cf_vppmod_lock);
+		return -EINVAL;
+	}
+
+	if (w9968cf_vpp->busy) {
+		KDBG(2, "Video post-processing module busy. Wait for it to be "
+		        "released...")
+		up(&w9968cf_vppmod_lock);
+		wait_event(w9968cf_vppmod_wait, !w9968cf_vpp->busy);
+		w9968cf_vpp = NULL;
+		goto out;
+	}
+
+	w9968cf_vpp = NULL;
+
+	up(&w9968cf_vppmod_lock);
+
+out:
+	KDBG(2, "Video post-processing module unregistered")
+	return 0;
 }
 
 
@@ -3702,18 +3807,14 @@
 {
 	int err;
 
-	DBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION)
-	DBG(3, W9968CF_MODULE_AUTHOR)
-
-	init_MUTEX(&w9968cf_devlist_sem);
+	KDBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION)
+	KDBG(3, W9968CF_MODULE_AUTHOR)
 
-	w9968cf_vppmod_detect();
+	if (ovmod_load)
+		request_module("ovcamchip");
 
-	if ((err = usb_register(&w9968cf_usb_driver))) {
-		if (w9968cf_vppmod_present)
-			w9968cf_vppmod_release();
+	if ((err = usb_register(&w9968cf_usb_driver)))
 		return err;
-	}
 
 	return 0;
 }
@@ -3724,12 +3825,13 @@
 	/* w9968cf_usb_disconnect() will be called */
 	usb_deregister(&w9968cf_usb_driver);
 
-	if (w9968cf_vppmod_present)
-		w9968cf_vppmod_release();
-
-	DBG(2, W9968CF_MODULE_NAME" deregistered.")
+	KDBG(2, W9968CF_MODULE_NAME" deregistered")
 }
 
 
 module_init(w9968cf_module_init);
 module_exit(w9968cf_module_exit);
+
+
+EXPORT_SYMBOL(w9968cf_vppmod_register);
+EXPORT_SYMBOL(w9968cf_vppmod_deregister);
diff -Nru a/drivers/usb/media/w9968cf.h b/drivers/usb/media/w9968cf.h
--- a/drivers/usb/media/w9968cf.h	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/media/w9968cf.h	2004-07-13 13:12:22 -07:00
@@ -24,21 +24,26 @@
 #include <linux/videodev.h>
 #include <linux/usb.h>
 #include <linux/i2c.h>
+#include <linux/device.h>
 #include <linux/spinlock.h>
 #include <linux/list.h>
 #include <linux/wait.h>
 #include <linux/config.h>
 #include <linux/param.h>
+#include <linux/types.h>
 #include <asm/semaphore.h>
-#include <asm/types.h>
+#include <asm/rwsem.h>
 
-#include "w9968cf_externaldef.h"
+#include <media/ovcamchip.h>
+
+#include "w9968cf_vpp.h"
 
 
 /****************************************************************************
  * Default values                                                           *
  ****************************************************************************/
 
+#define W9968CF_OVMOD_LOAD      1  /* automatic 'ovcamchip' module loading */
 #define W9968CF_VPPMOD_LOAD     1  /* automatic 'w9968cf-vpp' module loading */
 
 /* Comment/uncomment the following line to enable/disable debugging messages */
@@ -95,8 +100,8 @@
 
 #define W9968CF_FORCE_RGB        0  /* read RGB instead of BGR, yes=1/no=0 */
 
-#define W9968CF_MAX_WIDTH      800 /* should be >= 640 */
-#define W9968CF_MAX_HEIGHT     600 /* should be >= 480 */
+#define W9968CF_MAX_WIDTH      800 /* Has effect if up-scaling is on */
+#define W9968CF_MAX_HEIGHT     600 /* Has effect if up-scaling is on */
 #define W9968CF_WIDTH          320 /* from 128 to 352, multiple of 16 */
 #define W9968CF_HEIGHT         240 /* from  96 to 288, multiple of 16 */
 
@@ -130,13 +135,11 @@
 
 #define W9968CF_MODULE_NAME     "V4L driver for W996[87]CF JPEG USB " \
                                 "Dual Mode Camera Chip"
-#define W9968CF_MODULE_VERSION  "v1.25-basic"
+#define W9968CF_MODULE_VERSION  "1:1.32-basic"
 #define W9968CF_MODULE_AUTHOR   "(C) 2002-2004 Luca Risolia"
 #define W9968CF_AUTHOR_EMAIL    "<luca.risolia@studio.unibo.it>"
 #define W9968CF_MODULE_LICENSE  "GPL"
 
-static u8 w9968cf_vppmod_present; /* status flag: yes=1, no=0 */
-
 static const struct usb_device_id winbond_id_table[] = {
 	{
 		/* Creative Labs Video Blaster WebCam Go Plus */
@@ -151,18 +154,19 @@
 	{ } /* terminating entry */
 };
 
-MODULE_DEVICE_TABLE(usb, winbond_id_table);
-
 /* W996[87]CF camera models, internal ids: */
 enum w9968cf_model_id {
 	W9968CF_MOD_GENERIC = 1, /* Generic W996[87]CF based device */
 	W9968CF_MOD_CLVBWGP = 11,/*Creative Labs Video Blaster WebCam Go Plus*/
-	W9968CF_MOD_ADPA5R = 21, /* Aroma Digi Pen ADG-5000 Refurbished */
-	W9986CF_MOD_AU = 31,     /* AVerTV USB */
-	W9968CF_MOD_CLVBWG = 34, /* Creative Labs Video Blaster WebCam Go */
-	W9968CF_MOD_DLLDK = 37,  /* Die Lebon LDC-D35A Digital Kamera */
+	W9968CF_MOD_ADPVDMA = 21, /* Aroma Digi Pen VGA Dual Mode ADG-5000 */
+	W9986CF_MOD_AAU = 31,     /* AVerMedia AVerTV USB */
+	W9968CF_MOD_CLVBWG = 34,  /* Creative Labs Video Blaster WebCam Go */
+	W9968CF_MOD_LL = 37,      /* Lebon LDC-035A */
 	W9968CF_MOD_EEEMC = 40,   /* Ezonics EZ-802 EZMega Cam */
+	W9968CF_MOD_OOE = 42,     /* OmniVision OV8610-EDE */
 	W9968CF_MOD_ODPVDMPC = 43,/* OPCOM Digi Pen VGA Dual Mode Pen Camera */
+	W9968CF_MOD_PDPII = 46,   /* Pretec Digi Pen-II */
+	W9968CF_MOD_PDP480 = 49,  /* Pretec DigiPen-480 */
 };
 
 enum w9968cf_frame_status {
@@ -173,9 +177,10 @@
 };
 
 struct w9968cf_frame_t {
-	#define W9968CF_HW_BUF_SIZE 640*480*2 /* buf.size of original frames */
 	void* buffer;
+	unsigned long size;
 	u32 length;
+	int number;
 	enum w9968cf_frame_status status;
 	struct w9968cf_frame_t* next;
 	u8 queued;
@@ -189,12 +194,19 @@
 	VPP_UYVY_TO_RGBX = 0x08,
 };
 
-static struct list_head w9968cf_dev_list; /* head of V4L registered cameras list */
-static LIST_HEAD(w9968cf_dev_list);
-struct semaphore w9968cf_devlist_sem; /* semaphore for list traversal */
+static struct w9968cf_vpp_t* w9968cf_vpp;
+static DECLARE_MUTEX(w9968cf_vppmod_lock);
+static DECLARE_WAIT_QUEUE_HEAD(w9968cf_vppmod_wait);
+
+static LIST_HEAD(w9968cf_dev_list); /* head of V4L registered cameras list */
+static DECLARE_MUTEX(w9968cf_devlist_sem); /* semaphore for list traversal */
+
+static DECLARE_RWSEM(w9968cf_disconnect); /* prevent races with open() */
 
 /* Main device driver structure */
 struct w9968cf_device {
+	struct device dev; /* device structure */
+
 	enum w9968cf_model_id id;   /* private device identifier */
 
 	struct video_device* v4ldev; /* -> V4L structure */
@@ -207,10 +219,10 @@
 	u16* data_buffer;                    /* -> data to send to the FSB */
 
 	struct w9968cf_frame_t frame[W9968CF_MAX_BUFFERS];
-	struct w9968cf_frame_t frame_tmp;  /* temporary frame */
+	struct w9968cf_frame_t frame_tmp; /* temporary frame */
+	struct w9968cf_frame_t frame_vpp; /* helper frame.*/
 	struct w9968cf_frame_t* frame_current; /* -> frame being grabbed */
 	struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS];
-	void* vpp_buffer; /*-> helper buf.for video post-processing routines */
 
 	u8 max_buffers,   /* number of requested buffers */
 	   force_palette, /* yes=1/no=0 */
@@ -233,7 +245,7 @@
 	    hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */
 	    vs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */
 	    start_cropx, /* pixels from HS inactive edge to 1st cropped pixel*/
-	    start_cropy; /* pixels from VS incative edge to 1st cropped pixel*/
+	    start_cropy; /* pixels from VS inactive edge to 1st cropped pixel*/
 
 	enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */
 
@@ -246,13 +258,13 @@
 
 	u8 sensor_initialized; /* flag: yes=1, no=0 */
 
-	/* Determined by CMOS sensor type: */
+	/* Determined by the image sensor type: */
 	int sensor,       /* type of image sensor chip (CC_*) */
-	    monochrome;   /* CMOS sensor is (probably) monochrome */
-	u16 maxwidth,     /* maximum width supported by the CMOS sensor */
-	    maxheight,    /* maximum height supported by the CMOS sensor */
-	    minwidth,     /* minimum width supported by the CMOS sensor */
-	    minheight;    /* minimum height supported by the CMOS sensor */
+	    monochrome;   /* image sensor is (probably) monochrome */
+	u16 maxwidth,     /* maximum width supported by the image sensor */
+	    maxheight,    /* maximum height supported by the image sensor */
+	    minwidth,     /* minimum width supported by the image sensor */
+	    minheight;    /* minimum height supported by the image sensor */
 	u8  auto_brt,     /* auto brightness enabled flag */
 	    auto_exp,     /* auto exposure enabled flag */
 	    backlight,    /* backlight exposure algorithm flag */
@@ -270,8 +282,9 @@
 	                 fileop_sem; /* for read and ioctl */
 	spinlock_t urb_lock,   /* for submit_urb() and unlink_urb() */
 	           flist_lock; /* for requested frame list accesses */
-	char command[16]; /* name of the program holding the device */
 	wait_queue_head_t open, wait_queue;
+
+	char command[16]; /* name of the program holding the device */
 };
 
 
@@ -280,31 +293,47 @@
  ****************************************************************************/
 
 #undef DBG
+#undef KDBG
 #ifdef W9968CF_DEBUG
-#	define DBG(level, fmt, args...) \
-{ \
-if ( ((specific_debug) && (debug == (level))) || \
-     ((!specific_debug) && (debug >= (level))) ) { \
-	if ((level) == 1) \
-		err(fmt, ## args); \
-	else if ((level) == 2 || (level) == 3) \
-		info(fmt, ## args); \
-	else if ((level) == 4) \
-		warn(fmt, ## args); \
-	else if ((level) >= 5) \
-		info("[%s:%d] " fmt, \
-		     __FUNCTION__, __LINE__ , ## args); \
-} \
+/* For device specific debugging messages */
+#	define DBG(level, fmt, args...)                                       \
+{                                                                             \
+	if ( ((specific_debug) && (debug == (level))) ||                      \
+	     ((!specific_debug) && (debug >= (level))) ) {                    \
+		if ((level) == 1)                                             \
+			dev_err(&cam->dev, fmt "\n", ## args);                \
+		else if ((level) == 2 || (level) == 3)                        \
+			dev_info(&cam->dev, fmt "\n", ## args);               \
+		else if ((level) == 4)                                        \
+			dev_warn(&cam->dev, fmt "\n", ## args);               \
+		else if ((level) >= 5)                                        \
+			dev_info(&cam->dev, "[%s:%d] " fmt "\n",              \
+			         __FUNCTION__, __LINE__ , ## args);           \
+	}                                                                     \
+}
+/* For generic kernel (not device specific) messages */
+#	define KDBG(level, fmt, args...)                                      \
+{                                                                             \
+	if ( ((specific_debug) && (debug == (level))) ||                      \
+	     ((!specific_debug) && (debug >= (level))) ) {                    \
+		if ((level) >= 1 && (level) <= 4)                             \
+			pr_info("w9968cf: " fmt "\n", ## args);               \
+		else if ((level) >= 5)                                        \
+			pr_debug("w9968cf: [%s:%d] " fmt "\n", __FUNCTION__,  \
+			         __LINE__ , ## args);                         \
+	}                                                                     \
 }
 #else
 	/* Not debugging: nothing */
 #	define DBG(level, fmt, args...) do {;} while(0);
+#	define KDBG(level, fmt, args...) do {;} while(0);
 #endif
 
 #undef PDBG
+#define PDBG(fmt, args...)                                                    \
+dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args);
+
 #undef PDBGG
-#define PDBG(fmt, args...) info("[%s:%d] "fmt, \
-	                        __PRETTY_FUNCTION__, __LINE__ , ## args);
 #define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */
 
 #endif /* _W9968CF_H_ */
diff -Nru a/drivers/usb/media/w9968cf_externaldef.h b/drivers/usb/media/w9968cf_externaldef.h
--- a/drivers/usb/media/w9968cf_externaldef.h	2004-07-13 13:12:22 -07:00
+++ /dev/null	Wed Dec 31 16:00:00 196900
@@ -1,94 +0,0 @@
-/***************************************************************************
- * Various definitions for compatibility with OVCAMCHIP external module.   *
- * This file is part of the W996[87]CF driver for Linux.                   *
- *                                                                         *
- * The definitions have been taken from the OVCAMCHIP module written by    *
- * Mark McClelland.                                                        *
- *                                                                         *
- * This program is free software; you can redistribute it and/or modify    *
- * it under the terms of the GNU General Public License as published by    *
- * the Free Software Foundation; either version 2 of the License, or       *
- * (at your option) any later version.                                     *
- *                                                                         *
- * This program is distributed in the hope that it will be useful,         *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
- * GNU General Public License for more details.                            *
- *                                                                         *
- * You should have received a copy of the GNU General Public License       *
- * along with this program; if not, write to the Free Software             *
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
- ***************************************************************************/
-
-#ifndef _W9968CF_EXTERNALDEF_H_
-#define _W9968CF_EXTERNALDEF_H_
-
-#include <linux/videodev.h>
-#include <linux/i2c.h>
-#include <asm/ioctl.h>
-#include <asm/types.h>
-
-#ifndef I2C_DRIVERID_OVCAMCHIP
-#	define I2C_DRIVERID_OVCAMCHIP 0xf00f
-#endif
-
-/* Controls */
-enum {
-	OVCAMCHIP_CID_CONT,       /* Contrast */
-	OVCAMCHIP_CID_BRIGHT,     /* Brightness */
-	OVCAMCHIP_CID_SAT,        /* Saturation */
-	OVCAMCHIP_CID_HUE,        /* Hue */
-	OVCAMCHIP_CID_EXP,        /* Exposure */
-	OVCAMCHIP_CID_FREQ,       /* Light frequency */
-	OVCAMCHIP_CID_BANDFILT,   /* Banding filter */
-	OVCAMCHIP_CID_AUTOBRIGHT, /* Auto brightness */
-	OVCAMCHIP_CID_AUTOEXP,    /* Auto exposure */
-	OVCAMCHIP_CID_BACKLIGHT,  /* Back light compensation */
-	OVCAMCHIP_CID_MIRROR,     /* Mirror horizontally */
-};
-
-/* I2C addresses */
-#define OV7xx0_SID   (0x42 >> 1)
-#define OV6xx0_SID   (0xC0 >> 1)
-
-/* Sensor types */
-enum {
-	CC_UNKNOWN,
-	CC_OV76BE,
-	CC_OV7610,
-	CC_OV7620,
-	CC_OV7620AE,
-	CC_OV6620,
-	CC_OV6630,
-	CC_OV6630AE,
-	CC_OV6630AF,
-};
-
-/* API */
-struct ovcamchip_control {
-	__u32 id;
-	__s32 value;
-};
-
-struct ovcamchip_window {
-	int x;
-	int y;
-	int width;
-	int height;
-	int format;
-	int quarter;  /* Scale width and height down 2x */
-
-	/* This stuff will be removed eventually */
-	int clockdiv; /* Clock divisor setting */
-};
-
-/* Commands. 
-   You must call OVCAMCHIP_CMD_INITIALIZE before any of other commands */
-#define OVCAMCHIP_CMD_Q_SUBTYPE  _IOR  (0x88, 0x00, int)
-#define OVCAMCHIP_CMD_INITIALIZE _IOW  (0x88, 0x01, int)
-#define OVCAMCHIP_CMD_S_CTRL     _IOW  (0x88, 0x02, struct ovcamchip_control)
-#define OVCAMCHIP_CMD_G_CTRL     _IOWR (0x88, 0x03, struct ovcamchip_control)
-#define OVCAMCHIP_CMD_S_MODE     _IOW  (0x88, 0x04, struct ovcamchip_window)
-#define OVCAMCHIP_MAX_CMD        _IO   (0x88, 0x3f)
-
-#endif /* _W9968CF_EXTERNALDEF_H_ */
diff -Nru a/drivers/usb/media/w9968cf_vpp.h b/drivers/usb/media/w9968cf_vpp.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/usb/media/w9968cf_vpp.h	2004-07-13 13:12:22 -07:00
@@ -0,0 +1,43 @@
+/***************************************************************************
+ * Interface for video post-processing functions for the W996[87]CF driver *
+ * for Linux.                                                              *
+ *                                                                         *
+ * Copyright (C) 2002-2004 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    *
+ * the Free Software Foundation; either version 2 of the License, or       *
+ * (at your option) any later version.                                     *
+ *                                                                         *
+ * This program is distributed in the hope that it will be useful,         *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ * GNU General Public License for more details.                            *
+ *                                                                         *
+ * You should have received a copy of the GNU General Public License       *
+ * along with this program; if not, write to the Free Software             *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
+ ***************************************************************************/
+
+#ifndef _W9968CF_VPP_H_
+#define _W9968CF_VPP_H_
+
+#include <linux/module.h>
+#include <asm/types.h>
+
+struct w9968cf_vpp_t {
+	struct module* owner;
+	int (*check_headers)(const unsigned char*, const unsigned long);
+	int (*decode)(const char*, const unsigned long, const unsigned,
+	              const unsigned, char*);
+	void (*swap_yuvbytes)(void*, unsigned long);
+	void (*uyvy_to_rgbx)(u8*, unsigned long, u8*, u16, u8);
+	void (*scale_up)(u8*, u8*, u16, u16, u16, u16, u16);
+
+	u8 busy; /* read-only flag: module is/is not in use */
+};
+
+extern int w9968cf_vppmod_register(struct w9968cf_vpp_t*);
+extern int w9968cf_vppmod_deregister(struct w9968cf_vpp_t*);
+
+#endif /* _W9968CF_VPP_H_ */
diff -Nru a/drivers/usb/misc/emi26_fw.h b/drivers/usb/misc/emi26_fw.h
--- a/drivers/usb/misc/emi26_fw.h	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/misc/emi26_fw.h	2004-07-13 13:12:22 -07:00
@@ -12,6 +12,10 @@
  * and which may not be reproduced, used, sold or transferred to
  * any third party without Emagic's written consent. All Rights Reserved.
  *
+ * Permission is hereby granted for the distribution of this firmware 
+ * image as part of a Linux or other Open Source operating system kernel 
+ * in text or binary form as required. 
+ *
  * This firmware may not be modified and may only be used with the
  * Emagic EMI 2|6 Audio Interface. Distribution and/or Modification of
  * any driver which includes this firmware, in whole or in part,
diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
--- a/drivers/usb/net/usbnet.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/net/usbnet.c	2004-07-13 13:12:22 -07:00
@@ -454,6 +454,15 @@
 #define AX_MCAST_FILTER_SIZE		8
 #define AX_MAX_MCAST			64
 
+#define AX_INTERRUPT_BUFSIZE		8
+
+/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
+struct ax8817x_data {
+	u8 multi_filter[AX_MCAST_FILTER_SIZE];
+	struct urb *int_urb;
+	u8 *int_buf;
+};
+
 static int ax8817x_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
 			    u16 size, void *data)
 {
@@ -496,6 +505,30 @@
 	usb_free_urb(urb);
 }
 
+static void ax8817x_interrupt_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct usbnet *dev = (struct usbnet *)urb->context;
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	int link;
+
+	if (urb->status < 0) {
+		printk(KERN_DEBUG "ax8817x_interrupt_complete() failed with %d",
+			urb->status);
+	} else {
+		if (data->int_buf[5] == 0x90) {
+			link = data->int_buf[2] & 0x01;
+			if (netif_carrier_ok(dev->net) != link) {
+				if (link)
+					netif_carrier_on(dev->net);
+				else
+					netif_carrier_off(dev->net);
+				devdbg(dev, "ax8817x - Link Status is: %d", link);
+			}
+		}
+		usb_submit_urb(data->int_urb, GFP_KERNEL);
+	}
+}
+
 static void ax8817x_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
 				    u16 size, void *data)
 {
@@ -535,6 +568,7 @@
 static void ax8817x_set_multicast(struct net_device *net)
 {
 	struct usbnet *dev = (struct usbnet *) net->priv;
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
 	u8 rx_ctl = 0x8c;
 
 	if (net->flags & IFF_PROMISC) {
@@ -549,25 +583,24 @@
 		 * for our 8 byte filter buffer
 		 * to avoid allocating memory that
 		 * is tricky to free later */
-		u8 *multi_filter = (u8 *)&dev->data;
 		struct dev_mc_list *mc_list = net->mc_list;
 		u32 crc_bits;
 		int i;
 
-		memset(multi_filter, 0, AX_MCAST_FILTER_SIZE);
+		memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
 
 		/* Build the multicast hash filter. */
 		for (i = 0; i < net->mc_count; i++) {
 			crc_bits =
 			    ether_crc(ETH_ALEN,
 				      mc_list->dmi_addr) >> 26;
-			multi_filter[crc_bits >> 3] |=
+			data->multi_filter[crc_bits >> 3] |=
 			    1 << (crc_bits & 7);
 			mc_list = mc_list->next;
 		}
 
 		ax8817x_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
-				   AX_MCAST_FILTER_SIZE, multi_filter);
+				   AX_MCAST_FILTER_SIZE, data->multi_filter);
 
 		rx_ctl |= 0x10;
 	}
@@ -669,13 +702,6 @@
 	info->eedump_len = 0x3e;
 }
 
-static u32 ax8817x_get_link (struct net_device *net)
-{
-	struct usbnet *dev = (struct usbnet *)net->priv;
-
-	return (u32)mii_link_ok(&dev->mii);
-}
-
 static int ax8817x_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
 {
 	struct usbnet *dev = (struct usbnet *)net->priv;
@@ -695,7 +721,7 @@
    devices that may be connected at the same time. */
 static struct ethtool_ops ax8817x_ethtool_ops = {
 	.get_drvinfo		= ax8817x_get_drvinfo,
-	.get_link		= ax8817x_get_link,
+	.get_link		= ethtool_op_get_link,
 	.get_msglevel		= usbnet_get_msglevel,
 	.set_msglevel		= usbnet_set_msglevel,
 	.get_wol		= ax8817x_get_wol,
@@ -709,13 +735,34 @@
 {
 	int ret;
 	u8 buf[6];
-	u16 *buf16 = (u16 *) buf;
 	int i;
 	unsigned long gpio_bits = dev->driver_info->data;
+	struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
 
 	dev->in = usb_rcvbulkpipe(dev->udev, 3);
 	dev->out = usb_sndbulkpipe(dev->udev, 2);
 
+	// allocate irq urb
+	if ((data->int_urb = usb_alloc_urb (0, GFP_KERNEL)) == 0) {
+		dbg ("%s: cannot allocate interrupt URB",
+			dev->net->name);
+		return -ENOMEM;
+	}
+	
+	if ((data->int_buf = kmalloc(AX_INTERRUPT_BUFSIZE, GFP_KERNEL)) == NULL) {
+		dbg ("%s: cannot allocate memory for interrupt buffer",
+			dev->net->name);
+		usb_free_urb(data->int_urb);
+		return -ENOMEM;
+	}
+	memset(data->int_buf, 0, AX_INTERRUPT_BUFSIZE);
+
+	usb_fill_int_urb (data->int_urb, dev->udev,
+		usb_rcvintpipe (dev->udev, 1),
+		data->int_buf, AX_INTERRUPT_BUFSIZE,
+		ax8817x_interrupt_complete, dev,
+		dev->udev->speed == USB_SPEED_HIGH ? 8 : 100);
+
 	/* Toggle the GPIOs in a manufacturer/model specific way */
 	for (i = 2; i >= 0; i--) {
 		if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
@@ -756,49 +803,37 @@
 	dev->mii.reg_num_mask = 0x1f;
 	dev->mii.phy_id = buf[1];
 
-	if ((ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, &buf)) < 0) {
-		dbg("Failed to go to software MII mode: %02x", ret);
-		return ret;
-	}
-
-	*buf16 = cpu_to_le16(BMCR_RESET);
-	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG,
-				     dev->mii.phy_id, MII_BMCR, 2, buf16)) < 0) {
-		dbg("Failed to write MII reg - MII_BMCR: %02x", ret);
-		return ret;
-	}
-
-	/* Advertise that we can do full-duplex pause */
-	*buf16 = cpu_to_le16(ADVERTISE_ALL | ADVERTISE_CSMA | 0x0400);
-	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG,
-			   	     dev->mii.phy_id, MII_ADVERTISE, 
-				     2, buf16)) < 0) {
-		dbg("Failed to write MII_REG advertisement: %02x", ret);
-		return ret;
-	}
+	dev->net->set_multicast_list = ax8817x_set_multicast;
+	dev->net->ethtool_ops = &ax8817x_ethtool_ops;
 
-	*buf16 = cpu_to_le16(BMCR_ANENABLE | BMCR_ANRESTART);
-	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG,
-			  	     dev->mii.phy_id, MII_BMCR, 
-				     2, buf16)) < 0) {
-		dbg("Failed to write MII reg autonegotiate: %02x", ret);
+	ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR,
+			cpu_to_le16(BMCR_RESET));
+	ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+			cpu_to_le16(ADVERTISE_ALL | ADVERTISE_CSMA | 0x0400));
+	mii_nway_restart(&dev->mii);
+
+	if((ret = usb_submit_urb(data->int_urb, GFP_KERNEL)) < 0) {
+		dbg("Failed to submit interrupt URB: %02x", ret);
+		usb_free_urb(data->int_urb);
 		return ret;
 	}
 
-	if ((ret = ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf)) < 0) {
-		dbg("Failed to set hardware MII: %02x", ret);
-		return ret;
-	}
+	return 0;
+}
 
-	dev->net->set_multicast_list = ax8817x_set_multicast;
-	dev->net->ethtool_ops = &ax8817x_ethtool_ops;
+static void ax8817x_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
 
-	return 0;
+	usb_unlink_urb(data->int_urb);
+	usb_free_urb(data->int_urb);
+	kfree(data->int_buf);
 }
 
 static const struct driver_info ax8817x_info = {
 	.description = "ASIX AX8817x USB 2.0 Ethernet",
 	.bind = ax8817x_bind,
+	.unbind = ax8817x_unbind,
 	.flags =  FLAG_ETHER,
 	.data = 0x00130103,
 };
@@ -806,6 +841,7 @@
 static const struct driver_info dlink_dub_e100_info = {
 	.description = "DLink DUB-E100 USB Ethernet",
 	.bind = ax8817x_bind,
+	.unbind = ax8817x_unbind,
 	.flags =  FLAG_ETHER,
 	.data = 0x009f9d9f,
 };
@@ -813,6 +849,7 @@
 static const struct driver_info netgear_fa120_info = {
 	.description = "Netgear FA-120 USB Ethernet",
 	.bind = ax8817x_bind,
+	.unbind = ax8817x_unbind,
 	.flags =  FLAG_ETHER,
 	.data = 0x00130103,
 };
@@ -820,6 +857,7 @@
 static const struct driver_info hawking_uf200_info = {
 	.description = "Hawking UF200 USB Ethernet",
 	.bind = ax8817x_bind,
+	.unbind = ax8817x_unbind,
 	.flags =  FLAG_ETHER,
 	.data = 0x001f1d1f,
 };
@@ -3210,6 +3248,10 @@
 }, {
 	// Buffalo LUA-U2-KTX
 	USB_DEVICE (0x0411, 0x003d),
+	.driver_info =  (unsigned long) &ax8817x_info,
+}, {
+	// Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter"
+	USB_DEVICE (0x6189, 0x182d),
 	.driver_info =  (unsigned long) &ax8817x_info,
 },
 #endif
diff -Nru a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
--- a/drivers/usb/serial/Kconfig	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/Kconfig	2004-07-13 13:12:22 -07:00
@@ -20,13 +20,6 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called usbserial.
 
-config USB_SERIAL_DEBUG
-	bool "USB Serial Converter verbose debug"
-	depends on USB_SERIAL=y
-	help
-	  Say Y here if you want verbose debug messages from the USB Serial
-	  Drivers sent to the kernel debug log.
-
 config USB_SERIAL_CONSOLE
 	bool "USB Serial Console device support (EXPERIMENTAL)"
 	depends on USB_SERIAL=y && EXPERIMENTAL
diff -Nru a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
--- a/drivers/usb/serial/Makefile	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/Makefile	2004-07-13 13:12:22 -07:00
@@ -9,25 +9,26 @@
 usbserial-obj-$(CONFIG_USB_SERIAL_CONSOLE)	+= console.o
 usbserial-obj-$(CONFIG_USB_EZUSB)		+= ezusb.o
 
-obj-$(CONFIG_USB_SERIAL_VISOR)			+= visor.o
-obj-$(CONFIG_USB_SERIAL_IPAQ)			+= ipaq.o
-obj-$(CONFIG_USB_SERIAL_WHITEHEAT)		+= whiteheat.o
-obj-$(CONFIG_USB_SERIAL_FTDI_SIO)		+= ftdi_sio.o
-obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA)		+= keyspan_pda.o
-obj-$(CONFIG_USB_SERIAL_XIRCOM)			+= keyspan_pda.o
-obj-$(CONFIG_USB_SERIAL_KEYSPAN)		+= keyspan.o
-obj-$(CONFIG_USB_SERIAL_OMNINET)		+= omninet.o
-obj-$(CONFIG_USB_SERIAL_DIGI_ACCELEPORT)	+= digi_acceleport.o
+usbserial-objs	:= usb-serial.o generic.o bus.o $(usbserial-obj-y)
+
 obj-$(CONFIG_USB_SERIAL_BELKIN)			+= belkin_sa.o
-obj-$(CONFIG_USB_SERIAL_EMPEG)			+= empeg.o
-obj-$(CONFIG_USB_SERIAL_MCT_U232)		+= mct_u232.o
+obj-$(CONFIG_USB_SERIAL_CYBERJACK)		+= cyberjack.o
+obj-$(CONFIG_USB_SERIAL_DIGI_ACCELEPORT)	+= digi_acceleport.o
 obj-$(CONFIG_USB_SERIAL_EDGEPORT)		+= io_edgeport.o
 obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI)		+= io_ti.o
-obj-$(CONFIG_USB_SERIAL_PL2303)			+= pl2303.o
-obj-$(CONFIG_USB_SERIAL_KOBIL_SCT)		+= kobil_sct.o
-obj-$(CONFIG_USB_SERIAL_CYBERJACK)		+= cyberjack.o
+obj-$(CONFIG_USB_SERIAL_EMPEG)			+= empeg.o
+obj-$(CONFIG_USB_SERIAL_FTDI_SIO)		+= ftdi_sio.o
+obj-$(CONFIG_USB_SERIAL_IPAQ)			+= ipaq.o
 obj-$(CONFIG_USB_SERIAL_IR)			+= ir-usb.o
+obj-$(CONFIG_USB_SERIAL_KEYSPAN)		+= keyspan.o
+obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA)		+= keyspan_pda.o
 obj-$(CONFIG_USB_SERIAL_KLSI)			+= kl5kusb105.o
+obj-$(CONFIG_USB_SERIAL_KOBIL_SCT)		+= kobil_sct.o
+obj-$(CONFIG_USB_SERIAL_MCT_U232)		+= mct_u232.o
+obj-$(CONFIG_USB_SERIAL_OMNINET)		+= omninet.o
+obj-$(CONFIG_USB_SERIAL_PL2303)			+= pl2303.o
 obj-$(CONFIG_USB_SERIAL_SAFE)			+= safe_serial.o
+obj-$(CONFIG_USB_SERIAL_VISOR)			+= visor.o
+obj-$(CONFIG_USB_SERIAL_WHITEHEAT)		+= whiteheat.o
+obj-$(CONFIG_USB_SERIAL_XIRCOM)			+= keyspan_pda.o
 
-usbserial-objs	:= usb-serial.o generic.o bus.o $(usbserial-obj-y)
diff -Nru a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c
--- a/drivers/usb/serial/belkin_sa.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/belkin_sa.c	2004-07-13 13:12:22 -07:00
@@ -75,16 +75,11 @@
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
- 	static int debug = 1;
-#else
- 	static int debug;
-#endif
-
 #include "usb-serial.h"
 #include "belkin_sa.h"
 
+static int debug;
+
 /*
  * Version Information
  */
@@ -276,7 +271,7 @@
 		goto exit;
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 
 	/* Handle known interrupt data */
 	/* ignore data[0] and data[1] */
@@ -614,6 +609,5 @@
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
-
diff -Nru a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
--- a/drivers/usb/serial/bus.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/bus.c	2004-07-13 13:12:22 -07:00
@@ -14,13 +14,6 @@
 #include <linux/tty.h>
 #include <linux/module.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
 
 static int usb_serial_device_match (struct device *dev, struct device_driver *drv)
diff -Nru a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
--- a/drivers/usb/serial/cyberjack.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/cyberjack.c	2004-07-13 13:12:22 -07:00
@@ -35,16 +35,11 @@
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
+#include "usb-serial.h"
 
 #define CYBERJACK_LOCAL_BUF_SIZE 32
 
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
-#include "usb-serial.h"
+static int debug;
 
 /*
  * Version Information
@@ -243,7 +238,7 @@
 		memcpy (priv->wrbuf+priv->wrfilled, buf, count);
 	}  
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, count,
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count,
 		priv->wrbuf+priv->wrfilled);
 	priv->wrfilled += count;
 
@@ -318,7 +313,7 @@
 	if (urb->status)
 		return;
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 
 	/* React only to interrupts signaling a bulk_in transfer */
 	if( (urb->actual_length==4) && (data[0]==0x01) ) {
@@ -374,14 +369,12 @@
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 	
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 	if (urb->status) {
-		usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
 		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
 		return;
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
-
 	tty = port->tty;
 	if (urb->actual_length) {
 		for (i = 0; i < urb->actual_length ; ++i) {
@@ -520,6 +513,5 @@
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
-
diff -Nru a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
--- a/drivers/usb/serial/digi_acceleport.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/digi_acceleport.c	2004-07-13 13:12:22 -07:00
@@ -246,16 +246,8 @@
 #include <linux/workqueue.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
 
-
 /* Defines */
 
 /*
@@ -480,6 +472,8 @@
 
 /* Statics */
 
+static int debug;
+
 static struct usb_device_id id_table_combined [] = {
 	{ USB_DEVICE(DIGI_VENDOR_ID, DIGI_2_ID) },
 	{ USB_DEVICE(DIGI_VENDOR_ID, DIGI_4_ID) },
@@ -2068,6 +2062,5 @@
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
-
diff -Nru a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c
--- a/drivers/usb/serial/empeg.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/empeg.c	2004-07-13 13:12:22 -07:00
@@ -63,15 +63,10 @@
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
 
+static int debug;
+
 /*
  * Version Information
  */
@@ -249,7 +244,7 @@
 			memcpy (urb->transfer_buffer, current_position, transfer_size);
 		}
 
-		usb_serial_debug_data (__FILE__, __FUNCTION__, transfer_size, urb->transfer_buffer);
+		usb_serial_debug_data(debug, &port->dev, __FUNCTION__, transfer_size, urb->transfer_buffer);
 
 		/* build up our urb */
 		usb_fill_bulk_urb (
@@ -365,7 +360,7 @@
 		return;
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 
 	tty = port->tty;
 
@@ -609,6 +604,5 @@
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
-
diff -Nru a/drivers/usb/serial/ezusb.c b/drivers/usb/serial/ezusb.c
--- a/drivers/usb/serial/ezusb.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/ezusb.c	2004-07-13 13:12:22 -07:00
@@ -16,13 +16,6 @@
 #include <linux/tty.h>
 #include <linux/module.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
 
 /* EZ-USB Control and Status Register.  Bit 0 controls 8051 reset */
@@ -35,7 +28,7 @@
 
 	/* dbg("ezusb_writememory %x, %d", address, length); */
 	if (!serial->dev) {
-		dbg("%s - no physical device present, failing.", __FUNCTION__);
+		err("%s - no physical device present, failing.", __FUNCTION__);
 		return -ENODEV;
 	}
 
@@ -52,12 +45,12 @@
 
 int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit)
 {
-	int	response;
-	dbg("%s - %d", __FUNCTION__, reset_bit);
+	int response;
+
+	/* dbg("%s - %d", __FUNCTION__, reset_bit); */
 	response = ezusb_writememory (serial, CPUCS_REG, &reset_bit, 1, 0xa0);
-	if (response < 0) {
+	if (response < 0)
 		dev_err(&serial->dev->dev, "%s- %d failed\n", __FUNCTION__, reset_bit);
-	}
 	return response;
 }
 
diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
--- a/drivers/usb/serial/ftdi_sio.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/ftdi_sio.c	2004-07-13 13:12:22 -07:00
@@ -253,12 +253,6 @@
 #include <asm/uaccess.h>
 #include <linux/usb.h>
 #include <linux/serial.h>
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
 #include "ftdi_sio.h"
 
@@ -269,6 +263,8 @@
 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>"
 #define DRIVER_DESC "USB FTDI Serial Converters Driver"
 
+static int debug;
+
 static struct usb_device_id id_table_sio [] = {
 	{ USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
 	{ }						/* Terminating entry */
@@ -1492,7 +1488,7 @@
 		}
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, transfer_size, buffer);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, transfer_size, buffer);
 
 	/* fill the buffer and send it */
 	usb_fill_bulk_urb(urb, port->serial->dev, 
@@ -1658,7 +1654,7 @@
 
         /* The first two bytes of every read packet are status */
 	if (urb->actual_length > 2) {
-		usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+		usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 	} else {
                 dbg("Status only: %03oo %03oo",data[0],data[1]);
         }
@@ -2241,6 +2237,6 @@
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
 
diff -Nru a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
--- a/drivers/usb/serial/generic.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/generic.c	2004-07-13 13:12:22 -07:00
@@ -19,16 +19,10 @@
 #include <linux/moduleparam.h>
 #include <linux/usb.h>
 #include <asm/uaccess.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
 
-	
+static int debug;
+
 #ifdef CONFIG_USB_SERIAL_GENERIC
 static __u16 vendor  = 0x05f9;
 static __u16 product = 0xffff;
@@ -169,6 +163,7 @@
 {
 	struct usb_serial *serial = port->serial;
 	int result;
+	unsigned char *data;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
@@ -193,8 +188,8 @@
 		else {
 			memcpy (port->write_urb->transfer_buffer, buf, count);
 		}
-
-		usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
+		data = port->write_urb->transfer_buffer;
+		usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, data);
 
 		/* set up our urb */
 		usb_fill_bulk_urb (port->write_urb, serial->dev,
@@ -267,7 +262,7 @@
 		return;
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 
 	tty = port->tty;
 	if (tty && urb->actual_length) {
diff -Nru a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
--- a/drivers/usb/serial/io_edgeport.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/io_edgeport.c	2004-07-13 13:12:22 -07:00
@@ -259,15 +259,7 @@
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
-
 #include "io_edgeport.h"
 #include "io_ionsp.h"		/* info for the iosp messages */
 #include "io_16654.h"		/* 16654 UART defines */
@@ -299,19 +291,13 @@
 #define IMAGE_VERSION_NAME	OperationalCodeImageVersion_GEN2
 #include "io_fw_down2.h"	/* Define array OperationalCodeImage[] */
 
-
 #define MAX_NAME_LEN		64
 
-
 #define CHASE_TIMEOUT		(5*HZ)		/* 5 seconds */
 #define OPEN_TIMEOUT		(5*HZ)		/* 5 seconds */
 #define COMMAND_TIMEOUT		(5*HZ)		/* 5 seconds */
 
-#ifndef SERIAL_MAGIC
-	#define SERIAL_MAGIC	0x6702
-#endif
-#define PORT_MAGIC		0x7301
-
+static int debug;
 
 /* receive port state */
 enum RXSTATE {
@@ -793,7 +779,7 @@
 
 	// process this interrupt-read even if there are no ports open
 	if (length) {
-		usb_serial_debug_data (__FILE__, __FUNCTION__, length, data);
+		usb_serial_debug_data(debug, &edge_serial->serial->dev->dev, __FUNCTION__, length, data);
 
 		if (length > 1) {
 			bytes_avail = data[0] | (data[1] << 8);
@@ -869,7 +855,7 @@
 	if (urb->actual_length) {
 		raw_data_length = urb->actual_length;
 
-		usb_serial_debug_data (__FILE__, __FUNCTION__, raw_data_length, data);
+		usb_serial_debug_data(debug, &edge_serial->serial->dev->dev, __FUNCTION__, raw_data_length, data);
 
 		/* decrement our rxBytes available by the number that we just got */
 		edge_serial->rxBytesAvail -= raw_data_length;
@@ -1327,7 +1313,7 @@
 	} else {
 		memcpy(&fifo->fifo[fifo->head], data, firsthalf);
 	}  
-	usb_serial_debug_data (__FILE__, __FUNCTION__, firsthalf, &fifo->fifo[fifo->head]);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, firsthalf, &fifo->fifo[fifo->head]);
 
 	// update the index and size
 	fifo->head  += firsthalf;
@@ -1348,7 +1334,7 @@
 		} else {
 			memcpy(&fifo->fifo[fifo->head], &data[firsthalf], secondhalf);
 		}
-		usb_serial_debug_data (__FILE__, __FUNCTION__, secondhalf, &fifo->fifo[fifo->head]);
+		usb_serial_debug_data(debug, &port->dev, __FUNCTION__, secondhalf, &fifo->fifo[fifo->head]);
 		// update the index and size
 		fifo->count += secondhalf;
 		fifo->head  += secondhalf;
@@ -1424,7 +1410,7 @@
 	count = fifo->count;
 	buffer = kmalloc (count+2, GFP_ATOMIC);
 	if (buffer == NULL) {
-		dev_err(&edge_serial->serial->dev->dev, "%s - no more kernel memory...\n", __FUNCTION__);
+		dev_err(&edge_port->port->dev, "%s - no more kernel memory...\n", __FUNCTION__);
 		edge_port->write_in_progress = FALSE;
 		return;
 	}
@@ -1448,9 +1434,8 @@
 		fifo->count -= secondhalf;
 	}
 
-	if (count) {
-		usb_serial_debug_data (__FILE__, __FUNCTION__, count, &buffer[2]);
-	}
+	if (count)
+		usb_serial_debug_data(debug, &edge_port->port->dev, __FUNCTION__, count, &buffer[2]);
 
 	/* fill up the urb with all of our data and submit it */
 	usb_fill_bulk_urb (urb, edge_serial->serial->dev, 
@@ -2443,7 +2428,7 @@
 	struct urb *urb;
 	int timeout;
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, length, buffer);
+	usb_serial_debug_data(debug, &edge_port->port->dev, __FUNCTION__, length, buffer);
 
 	/* Allocate our next urb */
 	urb = usb_alloc_urb (0, GFP_ATOMIC);
@@ -3074,6 +3059,5 @@
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
-
diff -Nru a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
--- a/drivers/usb/serial/io_ti.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/io_ti.c	2004-07-13 13:12:22 -07:00
@@ -35,19 +35,13 @@
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
-
 #include "io_16654.h"
 #include "io_usbvend.h"
 #include "io_ti.h"
 
+static int debug;
+
 /*
  * Version Information
  */
@@ -315,8 +309,8 @@
 		}
 
 		if (read_length > 1) {
-			usb_serial_debug_data (__FILE__, __FUNCTION__,
-					       read_length, buffer);
+			usb_serial_debug_data(debug, &dev->dev, __FUNCTION__,
+					      read_length, buffer);
 		}
 
 		/* Update pointers/length */
@@ -357,7 +351,7 @@
 	}
 
 	dbg ("%s - start_address = %x, length = %d", __FUNCTION__, start_address, length);
-	usb_serial_debug_data (__FILE__, __FUNCTION__, length, buffer);
+	usb_serial_debug_data(debug, &serial->serial->dev->dev, __FUNCTION__, length, buffer);
 
 	serial->TiReadI2C = 1;
 
@@ -390,7 +384,7 @@
 	}
 
   	dbg ("%s - start_sddr = %x, length = %d", __FUNCTION__, start_address, length);
-	usb_serial_debug_data (__FILE__, __FUNCTION__, length, buffer);
+	usb_serial_debug_data(debug, &serial->serial->dev->dev, __FUNCTION__, length, buffer);
 
 	return status;
 }
@@ -412,7 +406,7 @@
 		write_length = length;
 
 	dbg ("%s - BytesInFirstPage Addr = %x, length = %d", __FUNCTION__, start_address, write_length);
-	usb_serial_debug_data (__FILE__, __FUNCTION__, write_length, buffer);
+	usb_serial_debug_data(debug, &serial->serial->dev->dev, __FUNCTION__, write_length, buffer);
 
 	/* Write first page */
 	be_start_address = cpu_to_be16 (start_address);
@@ -439,7 +433,7 @@
 			write_length = length;
 
 		dbg ("%s - Page Write Addr = %x, length = %d", __FUNCTION__, start_address, write_length);
-		usb_serial_debug_data (__FILE__, __FUNCTION__, write_length, buffer);
+		usb_serial_debug_data(debug, &serial->serial->dev->dev, __FUNCTION__, write_length, buffer);
 
 		/* Write next page */
 		be_start_address = cpu_to_be16 (start_address);
@@ -1669,7 +1663,7 @@
 		goto exit;
 	}
 		
-	usb_serial_debug_data (__FILE__, __FUNCTION__, length, data);
+	usb_serial_debug_data(debug, &edge_serial->serial->dev->dev, __FUNCTION__, length, data);
 		
 	if (length != 2) {
 		dbg ("%s - expecting packet of size 2, got %d", __FUNCTION__, length);
@@ -1761,7 +1755,7 @@
 
 	tty = edge_port->port->tty;
 	if (tty && urb->actual_length) {
-		usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+		usb_serial_debug_data(debug, &edge_port->port->dev, __FUNCTION__, urb->actual_length, data);
 
 		if (edge_port->close_pending) {
 			dbg ("%s - close is pending, dropping data on the floor.", __FUNCTION__);
@@ -2045,7 +2039,7 @@
 		memcpy (port->write_urb->transfer_buffer, data, count);
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer);
 
 	/* set up our urb */
 	usb_fill_bulk_urb (port->write_urb, port->serial->dev,
@@ -2684,9 +2678,9 @@
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
 
-MODULE_PARM(ignore_cpu_rev, "i");
+module_param(ignore_cpu_rev, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(ignore_cpu_rev, "Ignore the cpu revision when connecting to a device");
 
diff -Nru a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
--- a/drivers/usb/serial/ipaq.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/ipaq.c	2004-07-13 13:12:22 -07:00
@@ -56,13 +56,6 @@
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug = 0;
-#endif
-
 #include "usb-serial.h"
 #include "ipaq.h"
 
@@ -76,7 +69,8 @@
 #define DRIVER_AUTHOR "Ganesh Varadarajan <ganesh@veritas.com>"
 #define DRIVER_DESC "USB PocketPC PDA driver"
 
-static int	product, vendor;
+static __u16 product, vendor;
+static int debug;
 
 /* Function prototypes for an ipaq */
 static int  ipaq_open (struct usb_serial_port *port, struct file *filp);
@@ -315,7 +309,7 @@
 		return;
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 
 	tty = port->tty;
 	if (tty && urb->actual_length) {
@@ -396,7 +390,7 @@
 	} else {
 		memcpy(pkt->data, buf, count);
 	}
-	usb_serial_debug_data(__FILE__, __FUNCTION__, count, pkt->data);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, pkt->data);
 
 	pkt->len = count;
 	pkt->written = 0;
@@ -579,11 +573,11 @@
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
 
-MODULE_PARM(vendor, "h");
+module_param(vendor, ushort, 0);
 MODULE_PARM_DESC(vendor, "User specified USB idVendor");
 
-MODULE_PARM(product, "h");
+module_param(product, ushort, 0);
 MODULE_PARM_DESC(product, "User specified USB idProduct");
diff -Nru a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
--- a/drivers/usb/serial/ir-usb.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/ir-usb.c	2004-07-13 13:12:22 -07:00
@@ -58,13 +58,6 @@
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
 
 /*
@@ -101,9 +94,11 @@
 	u8	bMaxUnicastList;
 } __attribute__ ((packed));
 
+static int debug;
+
 /* if overridden by the user, then use their value for the size of the read and
  * write urbs */
-static int buffer_size = 0;
+static int buffer_size;
 /* if overridden by the user, then use the specified number of XBOFs */
 static int xbof = -1;
 
@@ -404,7 +399,8 @@
 	}
 
 	usb_serial_debug_data (
-		__FILE__,
+		debug,
+		&port->dev,
 		__FUNCTION__,
 		urb->actual_length,
 		urb->transfer_buffer);
@@ -439,7 +435,8 @@
 				ir_baud = *data & 0x0f;
 
 			usb_serial_debug_data (
-				__FILE__,
+				debug,
+				&port->dev,
 				__FUNCTION__,
 				urb->actual_length,
 				data);
@@ -614,10 +611,10 @@
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
-MODULE_PARM(xbof, "i");
+module_param(xbof, int, 0);
 MODULE_PARM_DESC(xbof, "Force specific number of XBOFs");
-MODULE_PARM(buffer_size, "i");
+module_param(buffer_size, int, 0);
 MODULE_PARM_DESC(buffer_size, "Size of the transfer buffers");
 
diff -Nru a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
--- a/drivers/usb/serial/keyspan.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/keyspan.c	2004-07-13 13:12:22 -07:00
@@ -107,20 +107,12 @@
 #include <linux/module.h>
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-	#define DEBUG
-#else
-	static int debug;
-	#undef DEBUG
-#endif
-
 #include <linux/usb.h>
-
 #include "usb-serial.h"
 #include "keyspan.h"
 
+static int debug;
+
 /*
  * Version Information
  */
@@ -2362,6 +2354,6 @@
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
 
diff -Nru a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
--- a/drivers/usb/serial/keyspan_pda.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/keyspan_pda.c	2004-07-13 13:12:22 -07:00
@@ -80,12 +80,7 @@
 #include <asm/uaccess.h>
 #include <linux/usb.h>
 
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
+static int debug;
 
 struct ezusb_hex_record {
 	__u16 address;
@@ -781,7 +776,7 @@
 	usb_set_serial_port_data(serial->port[0], priv);
 	init_waitqueue_head(&serial->port[0]->write_wait);
 	INIT_WORK(&priv->wakeup_work, (void *)keyspan_pda_wakeup_write,
-			(void *)(&serial->port[0]));
+			(void *)(serial->port[0]));
 	INIT_WORK(&priv->unthrottle_work,
 			(void *)keyspan_pda_request_unthrottle,
 			(void *)(serial));
@@ -909,6 +904,6 @@
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
 
diff -Nru a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
--- a/drivers/usb/serial/kl5kusb105.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/kl5kusb105.c	2004-07-13 13:12:22 -07:00
@@ -56,16 +56,10 @@
 #include <linux/module.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
- 	static int debug = 1;
-#else
- 	static int debug;
-#endif
-
 #include "usb-serial.h"
 #include "kl5kusb105.h"
 
+static int debug;
 
 /*
  * Version Information
@@ -659,7 +653,8 @@
 	} else if (urb->actual_length <= 2) {
 		dbg("%s - size %d URB not understood", __FUNCTION__,
 		    urb->actual_length);
-		usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+		usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+				      urb->actual_length, data);
 	} else {
 		int i;
 		int bytes_sent = ((__u8 *) data)[0] +
@@ -671,8 +666,8 @@
 		 * intermixed tty_flip_buffer_push()s
 		 * FIXME
 		 */ 
-		usb_serial_debug_data (__FILE__, __FUNCTION__,
-				       urb->actual_length, data);
+		usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+				      urb->actual_length, data);
 
 		if (bytes_sent + 2 > urb->actual_length) {
 			dbg("%s - trying to read more data than available"
@@ -1051,11 +1046,7 @@
 MODULE_LICENSE("GPL"); 
 
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "enable extensive debugging messages");
-/* FIXME: implement
-MODULE_PARM(num_urbs, "i");
-MODULE_PARM_DESC(num_urbs, "number of URBs to use in write pool");
-*/
 
 /* vim: set sts=8 ts=8 sw=8: */
diff -Nru a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
--- a/drivers/usb/serial/kobil_sct.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/kobil_sct.c	2004-07-13 13:12:22 -07:00
@@ -48,18 +48,10 @@
 #include <asm/uaccess.h>
 #include <linux/usb.h>
 #include <linux/ioctl.h>
-
-
+#include "usb-serial.h"
 #include "kobil_sct.h"
-//#include "../core/usb-debug.c"
 
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
-#include "usb-serial.h"
+static int debug;
 
 /* Version Information */
 #define DRIVER_VERSION "21/05/2004"
@@ -456,7 +448,7 @@
 		memcpy (priv->buf + priv->filled, buf, count);
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, count, priv->buf + priv->filled);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, priv->buf + priv->filled);
 
 	priv->filled = priv->filled + count;
 
@@ -788,5 +780,5 @@
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE( "GPL" );
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
diff -Nru a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
--- a/drivers/usb/serial/mct_u232.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/mct_u232.c	2004-07-13 13:12:22 -07:00
@@ -76,17 +76,9 @@
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
- 	static int debug = 1;
-#else
- 	static int debug;
-#endif
-
 #include "usb-serial.h"
 #include "mct_u232.h"
 
-
 /*
  * Version Information
  */
@@ -105,6 +97,8 @@
 static int write_blocking; /* disabled by default */
 #endif
 
+static int debug;
+
 /*
  * Function prototypes
  */
@@ -523,7 +517,7 @@
 	while (count > 0) {
 		size = (count > port->bulk_out_size) ? port->bulk_out_size : count;
 		
-		usb_serial_debug_data (__FILE__, __FUNCTION__, size, buf);
+		usb_serial_debug_data(debug, &port->dev, __FUNCTION__, size, buf);
 		
 		if (from_user) {
 			if (copy_from_user(port->write_urb->transfer_buffer, buf, size)) {
@@ -631,7 +625,7 @@
 		return;
 	}
 	
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 
 	/*
 	 * Work-a-round: handle the 'usual' bulk-in pipe here
@@ -912,11 +906,11 @@
 MODULE_LICENSE("GPL");
 
 #ifdef FIX_WRITE_RETURN_CODE_PROBLEM
-MODULE_PARM(write_blocking, "i");
+module_param(write_blocking, int, 0);
 MODULE_PARM_DESC(write_blocking, 
 		 "The write function will block to write out all data");
 #endif
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
 
diff -Nru a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
--- a/drivers/usb/serial/omninet.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/omninet.c	2004-07-13 13:12:22 -07:00
@@ -47,15 +47,9 @@
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
 
+static int debug;
 
 /*
  * Version Information
@@ -280,7 +274,7 @@
 		memcpy (wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET, buf, count);
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, count, wport->write_urb->transfer_buffer);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, wport->write_urb->transfer_buffer);
 
 	header->oh_seq 	= od->od_outseq++;
 	header->oh_len 	= count;
@@ -373,6 +367,5 @@
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
-
diff -Nru a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
--- a/drivers/usb/serial/pl2303.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/pl2303.c	2004-07-13 13:12:22 -07:00
@@ -49,13 +49,6 @@
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
 #include "pl2303.h"
 
@@ -65,7 +58,7 @@
 #define DRIVER_VERSION "v0.11"
 #define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"
 
-
+static int debug;
 
 static struct usb_device_id id_table [] = {
 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
@@ -251,7 +244,7 @@
 		memcpy (port->write_urb->transfer_buffer, buf, count);
 	}
 	
-	usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer);
 
 	port->write_urb->transfer_buffer_length = count;
 	port->write_urb->dev = port->serial->dev;
@@ -716,7 +709,7 @@
 	}
 
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
 
 	if (urb->actual_length < UART_STATE)
 		goto exit;
@@ -770,7 +763,7 @@
 		return;
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 
 	/* get tty_flag from status */
 	tty_flag = TTY_NORMAL;
diff -Nru a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
--- a/drivers/usb/serial/safe_serial.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/safe_serial.c	2004-07-13 13:12:22 -07:00
@@ -72,18 +72,14 @@
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
+#include "usb-serial.h"
 
 
-#ifndef CONFIG_USB_SERIAL_DEBUG
-#define CONFIG_USB_SERIAL_DEBUG 0
-#endif
 #ifndef CONFIG_USB_SAFE_PADDED
 #define CONFIG_USB_SAFE_PADDED 0
 #endif
 
-static int debug = CONFIG_USB_SERIAL_DEBUG;
-#include "usb-serial.h"		// must follow the declaration of debug
-
+static int debug;
 static int safe = 1;
 static int padded = CONFIG_USB_SAFE_PADDED;
 
@@ -102,19 +98,20 @@
 #if ! defined(CONFIG_USBD_SAFE_SERIAL_VENDOR)
 static __u16 vendor;		// no default
 static __u16 product;		// no default
-MODULE_PARM (vendor, "i");
-MODULE_PARM (product, "i");
-MODULE_PARM_DESC (vendor, "User specified USB idVendor (required)");
-MODULE_PARM_DESC (product, "User specified USB idProduct (required)");
+module_param(vendor, ushort, 0);
+MODULE_PARM_DESC(vendor, "User specified USB idVendor (required)");
+module_param(product, ushort, 0);
+MODULE_PARM_DESC(product, "User specified USB idProduct (required)");
 #endif
 
-MODULE_PARM (debug, "i");
-MODULE_PARM (safe, "i");
-MODULE_PARM (padded, "i");
-
-MODULE_PARM_DESC (debug, "Debug enabled or not");
-MODULE_PARM_DESC (safe, "Turn Safe Encapsulation On/Off");
-MODULE_PARM_DESC (padded, "Pad to full wMaxPacketSize On/Off");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+module_param(safe, bool, 0);
+MODULE_PARM_DESC(safe, "Turn Safe Encapsulation On/Off");
+
+module_param(padded, bool, 0);
+MODULE_PARM_DESC(padded, "Pad to full wMaxPacketSize On/Off");
 
 #define CDC_DEVICE_CLASS                        0x02
 
@@ -346,7 +343,7 @@
 		port->write_urb->transfer_buffer_length = count;
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer);
 #ifdef ECHO_TX
 	{
 		int i;
diff -Nru a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
--- a/drivers/usb/serial/usb-serial.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/usb-serial.c	2004-07-13 13:12:22 -07:00
@@ -336,14 +336,6 @@
 #include <linux/smp_lock.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
-
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
 #include "pl2303.h"
 
@@ -354,7 +346,6 @@
 #define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux/"
 #define DRIVER_DESC "USB Serial Driver core"
 
-
 /* Driver structure we register with the USB core */
 static struct usb_driver usb_serial_driver = {
 	.owner =	THIS_MODULE,
@@ -370,9 +361,9 @@
    drivers depend on it.
 */
 
-static struct usb_serial	*serial_table[SERIAL_TTY_MINORS];	/* initially all NULL */
+static int debug;
+static struct usb_serial *serial_table[SERIAL_TTY_MINORS];	/* initially all NULL */
 static LIST_HEAD(usb_serial_driver_list);
-
 
 struct usb_serial *usb_serial_get_by_index(unsigned index)
 {
diff -Nru a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h
--- a/drivers/usb/serial/usb-serial.h	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/usb-serial.h	2004-07-13 13:12:22 -07:00
@@ -294,20 +294,20 @@
 extern struct bus_type usb_serial_bus_type;
 extern struct tty_driver *usb_serial_tty_driver;
 
-static inline void usb_serial_debug_data (const char *file, const char *function, int size, const unsigned char *data)
+static inline void usb_serial_debug_data(int debug,
+					 struct device *dev,
+					 const char *function, int size,
+					 const unsigned char *data)
 {
 	int i;
 
-	if (!debug)
-		return;
-	
-	printk (KERN_DEBUG "%s: %s - length = %d, data = ", file, function, size);
-	for (i = 0; i < size; ++i) {
-		printk ("%.2x ", data[i]);
+	if (debug) {
+		dev_printk(KERN_DEBUG, dev, "%s - length = %d, data = ", function, size);
+		for (i = 0; i < size; ++i)
+			printk ("%.2x ", data[i]);
+		printk ("\n");
 	}
-	printk ("\n");
 }
-
 
 /* Use our own dbg macro */
 #undef dbg
diff -Nru a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
--- a/drivers/usb/serial/visor.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/visor.c	2004-07-13 13:12:22 -07:00
@@ -155,13 +155,6 @@
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
 #include "visor.h"
 
@@ -195,6 +188,7 @@
 static int palm_os_4_probe (struct usb_serial *serial, const struct usb_device_id *id);
 
 /* Parameters that may be passed into the module. */
+static int debug;
 static __u16 vendor;
 static __u16 product;
 
@@ -504,7 +498,7 @@
 		memcpy (buffer, buf, count);
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, count, buffer);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, buffer);
 
 	usb_fill_bulk_urb (urb, serial->dev,
 			   usb_sndbulkpipe (serial->dev,
@@ -590,7 +584,7 @@
 		return;
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 
 	tty = port->tty;
 	if (tty && urb->actual_length) {
@@ -621,6 +615,7 @@
 
 static void visor_read_int_callback (struct urb *urb, struct pt_regs *regs)
 {
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 	int result;
 
 	switch (urb->status) {
@@ -647,8 +642,8 @@
 	 * Rumor has it this endpoint is used to notify when data
 	 * is ready to be read from the bulk ones.
 	 */
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length,
-			       urb->transfer_buffer);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+			      urb->actual_length, urb->transfer_buffer);
 
 exit:
 	result = usb_submit_urb (urb, GFP_ATOMIC);
@@ -799,7 +794,8 @@
 		dev_err(dev, "%s - error %d getting connection info\n",
 			__FUNCTION__, retval);
 	else
-		usb_serial_debug_data (__FILE__, __FUNCTION__, retval, transfer_buffer);
+		usb_serial_debug_data(debug, &serial->dev->dev, __FUNCTION__,
+				      retval, transfer_buffer);
 
 	kfree (transfer_buffer);
 	return 0;
diff -Nru a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
--- a/drivers/usb/serial/whiteheat.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/serial/whiteheat.c	2004-07-13 13:12:22 -07:00
@@ -80,17 +80,12 @@
 #include <linux/usb.h>
 #include <linux/serial_reg.h>
 #include <linux/serial.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
 #include "usb-serial.h"
 #include "whiteheat_fw.h"		/* firmware for the ConnectTech WhiteHEAT device */
 #include "whiteheat.h"			/* WhiteHEAT specific commands */
 
+static int debug;
+
 #ifndef CMSPAR
 #define CMSPAR 0
 #endif
@@ -747,7 +742,7 @@
 			memcpy (urb->transfer_buffer, buf + sent, bytes);
 		}
 
-		usb_serial_debug_data (__FILE__, __FUNCTION__, bytes, urb->transfer_buffer);
+		usb_serial_debug_data(debug, &port->dev, __FUNCTION__, bytes, urb->transfer_buffer);
 
 		urb->dev = serial->dev;
 		urb->transfer_buffer_length = bytes;
@@ -975,10 +970,6 @@
 		dbg ("nonzero urb status: %d", urb->status);
 		return;
 	}
-
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
-
-	return;
 }
 
 
@@ -997,7 +988,7 @@
 		return;
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	usb_serial_debug_data(debug, &command_port->dev, __FUNCTION__, urb->actual_length, data);
 
 	command_info = usb_get_serial_port_data(command_port);
 	if (!command_info) {
@@ -1059,7 +1050,7 @@
 		return;
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 
 	spin_lock(&info->lock);
 	list_add_tail(&wrap->list, &info->rx_urb_q);
@@ -1519,8 +1510,8 @@
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(urb_pool_size, "i");
+module_param(urb_pool_size, int, 0);
 MODULE_PARM_DESC(urb_pool_size, "Number of urbs to use for buffering");
 
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
diff -Nru a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
--- a/drivers/usb/storage/scsiglue.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/storage/scsiglue.c	2004-07-13 13:12:22 -07:00
@@ -261,7 +261,7 @@
 static int bus_reset( Scsi_Cmnd *srb )
 {
 	struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
-	int result;
+	int result, rc;
 
 	US_DEBUGP("%s called\n", __FUNCTION__);
 	if (us->sm_state != US_STATE_IDLE) {
@@ -286,8 +286,16 @@
 		result = -EBUSY;
 		US_DEBUGP("Refusing to reset a multi-interface device\n");
 	} else {
-		result = usb_reset_device(us->pusb_dev);
-		US_DEBUGP("usb_reset_device returns %d\n", result);
+		rc = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
+		if (rc < 0) {
+			US_DEBUGP("unable to lock device for reset: %d\n", rc);
+			result = rc;
+		} else {
+			result = usb_reset_device(us->pusb_dev);
+			if (rc)
+				usb_unlock_device(us->pusb_dev);
+			US_DEBUGP("usb_reset_device returns %d\n", result);
+		}
 	}
 	up(&(us->dev_semaphore));
 
diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
--- a/drivers/usb/storage/transport.c	2004-07-13 13:12:22 -07:00
+++ b/drivers/usb/storage/transport.c	2004-07-13 13:12:22 -07:00
@@ -260,9 +260,7 @@
 		USB_ENDPOINT_HALT, endp,
 		NULL, 0, 3*HZ);
 
-	/* reset the toggles and endpoint flags */
-	usb_endpoint_running(us->pusb_dev, usb_pipeendpoint(pipe),
-		usb_pipeout(pipe));
+	/* reset the endpoint toggle */
 	usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe),
 		usb_pipeout(pipe), 0);
 
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h	2004-07-13 13:12:22 -07:00
+++ b/include/linux/usb.h	2004-07-13 13:12:22 -07:00
@@ -61,6 +61,13 @@
 	int extralen;
 };
 
+enum usb_interface_condition {
+	USB_INTERFACE_UNBOUND = 0,
+	USB_INTERFACE_BINDING,
+	USB_INTERFACE_BOUND,
+	USB_INTERFACE_UNBINDING,
+};
+
 /**
  * struct usb_interface - what usb device drivers talk to
  * @altsetting: array of interface structures, one for each alternate
@@ -75,6 +82,8 @@
  *	be unused.  The driver should set this value in the probe()
  *	function of the driver, after it has been assigned a minor
  *	number from the USB core by calling usb_register_dev().
+ * @condition: binding state of the interface: not bound, binding
+ *	(in probe()), bound to a driver, or unbinding (in disconnect())
  * @dev: driver model's view of this device
  * @class_dev: driver model's class view of this device.
  *
@@ -113,6 +122,7 @@
 	unsigned num_altsetting;	/* number of alternate settings */
 
 	int minor;			/* minor number this interface is bound to */
+	enum usb_interface_condition condition;		/* state of binding */
 	struct device dev;		/* interface specific device info */
 	struct class_device *class_dev;
 };
@@ -279,6 +289,17 @@
 
 struct usb_tt;
 
+/**
+ * struct usb_device - kernel's representation of a USB device
+ *
+ * FIXME: Write the kerneldoc!
+ *
+ * WARNING: Drivers should not attempt to manipulate usbdev->serialize
+ * directly.  Instead use usb_lock_device() and usb_unlock_device().
+ *
+ * Usbcore drivers should not set usbdev->state directly.  Instead use
+ * usb_set_device_state().
+ */
 struct usb_device {
 	int		devnum;		/* Address on USB bus */
 	char		devpath [16];	/* Use in messages: /port/port/... */
@@ -291,8 +312,6 @@
 	struct semaphore serialize;
 
 	unsigned int toggle[2];		/* one bit for each endpoint ([0] = IN, [1] = OUT) */
-	unsigned int halted[2];		/* endpoint halts; one bit per endpoint # & direction; */
-					/* [0] = IN, [1] = OUT */
 	int epmaxpacketin[16];		/* INput endpoint specific maximums */
 	int epmaxpacketout[16];		/* OUTput endpoint specific maximums */
 
@@ -332,9 +351,15 @@
 extern struct usb_device *usb_get_dev(struct usb_device *dev);
 extern void usb_put_dev(struct usb_device *dev);
 
-/* mostly for devices emulating SCSI over USB */
+/* for device locking -- don't manipulate usbdev->serialize directly! */
+extern void usb_lock_device(struct usb_device *udev);
+extern int usb_trylock_device(struct usb_device *udev);
+extern int usb_lock_device_for_reset(struct usb_device *udev,
+		struct usb_interface *iface);
+extern void usb_unlock_device(struct usb_device *udev);
+
+/* USB port reset for device reinitialization */
 extern int usb_reset_device(struct usb_device *dev);
-extern int __usb_reset_device(struct usb_device *dev);
 
 extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id);
 
@@ -355,7 +380,7 @@
  * may need to explicitly claim that lock.
  *
  */
-static int inline usb_interface_claimed(struct usb_interface *iface) {
+static inline int usb_interface_claimed(struct usb_interface *iface) {
 	return (iface->dev.driver != NULL);
 }
 
@@ -657,7 +682,7 @@
  * calling usb_alloc_urb() and freed with a call to usb_free_urb().
  * Initialization may be done using various usb_fill_*_urb() functions.  URBs
  * are submitted using usb_submit_urb(), and pending requests may be canceled
- * using usb_unlink_urb().
+ * using usb_unlink_urb() or usb_kill_urb().
  *
  * Data Transfer Buffers:
  *
@@ -684,7 +709,9 @@
  * All URBs submitted must initialize dev, pipe,
  * transfer_flags (may be zero), complete, timeout (may be zero).
  * The URB_ASYNC_UNLINK transfer flag affects later invocations of
- * the usb_unlink_urb() routine.
+ * the usb_unlink_urb() routine.  Note: Failure to set URB_ASYNC_UNLINK
+ * with usb_unlink_urb() is deprecated.  For synchronous unlinks use
+ * usb_kill_urb() instead.
  *
  * All URBs must also initialize 
  * transfer_buffer and transfer_buffer_length.  They may provide the
@@ -762,6 +789,8 @@
 	void *hcpriv;			/* private data for host controller */
 	struct list_head urb_list;	/* list pointer to all active urbs */
 	int bandwidth;			/* bandwidth for INT/ISO request */
+	atomic_t use_count;		/* concurrent submissions counter */
+	u8 reject;			/* submissions will fail */
 
 	/* public, documented fields in the urb that can be used by drivers */
 	struct usb_device *dev; 	/* (in) pointer to associated device */
@@ -897,6 +926,7 @@
 extern struct urb *usb_get_urb(struct urb *urb);
 extern int usb_submit_urb(struct urb *urb, int mem_flags);
 extern int usb_unlink_urb(struct urb *urb);
+extern void usb_kill_urb(struct urb *urb);
 
 #define HAVE_USB_BUFFERS
 void *usb_buffer_alloc (struct usb_device *dev, size_t size,
@@ -1070,10 +1100,6 @@
 #define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1)
 #define	usb_dotoggle(dev, ep, out)  ((dev)->toggle[out] ^= (1 << (ep)))
 #define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << (ep))) | ((bit) << (ep)))
-
-/* Endpoint halt control/status ... likewise USE WITH CAUTION */
-#define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep)))
-#define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep)))
 
 
 static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)
diff -Nru a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h
--- a/include/linux/usb_gadget.h	2004-07-13 13:12:22 -07:00
+++ b/include/linux/usb_gadget.h	2004-07-13 13:12:22 -07:00
@@ -6,7 +6,7 @@
  * master many USB gadgets, but the gadgets are only slaved to one host.
  *
  *
- * (c) Copyright 2002-2003 by David Brownell
+ * (C) Copyright 2002-2004 by David Brownell
  * All Rights Reserved.
  *
  * This software is licensed under the GNU GPL version 2.
@@ -73,8 +73,6 @@
  */
 	// NOTE this is analagous to 'struct urb' on the host side,
 	// except that it's thinner and promotes more pre-allocation.
-	//
-	// ISSUE should this be allocated through the device?
 
 struct usb_request {
 	void			*buf;
@@ -116,8 +114,8 @@
 		dma_addr_t *dma, int gfp_flags);
 	void (*free_buffer) (struct usb_ep *ep, void *buf, dma_addr_t dma,
 		unsigned bytes);
-	// NOTE:  on 2.5, drivers may also use dma_map() and
-	// dma_sync_single_*() to manage dma overhead.
+	// NOTE:  on 2.6, drivers may also use dma_map() and
+	// dma_sync_single_*() to directly manage dma overhead. 
 
 	int (*queue) (struct usb_ep *ep, struct usb_request *req,
 		int gfp_flags);
@@ -453,7 +451,10 @@
 struct usb_gadget_ops {
 	int	(*get_frame)(struct usb_gadget *);
 	int	(*wakeup)(struct usb_gadget *);
-	int	(*set_selfpowered) (struct usb_gadget *, int value);
+	int	(*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
+	int	(*vbus_session) (struct usb_gadget *, int is_active);
+	int	(*vbus_draw) (struct usb_gadget *, unsigned mA);
+	int	(*pullup) (struct usb_gadget *, int is_on);
 	int	(*ioctl)(struct usb_gadget *,
 				unsigned code, unsigned long param);
 };
@@ -467,6 +468,17 @@
  * @speed: Speed of current connection to USB host.
  * @is_dualspeed: True if the controller supports both high and full speed
  *	operation.  If it does, the gadget driver must also support both.
+ * @is_otg: True if the USB device port uses a Mini-AB jack, so that the
+ *	gadget driver must provide a USB OTG descriptor.
+ * @is_a_peripheral: False unless is_otg, the "A" end of a USB cable
+ *	is in the Mini-AB jack, and HNP has been used to switch roles
+ *	so that the "A" device currently acts as A-Peripheral, not A-Host.
+ * @a_hnp_support: OTG device feature flag, indicating that the A-Host
+ *	supports HNP at this port.
+ * @a_alt_hnp_support: OTG device feature flag, indicating that the A-Host
+ *	only supports HNP on a different root port.
+ * @b_hnp_enable: OTG device feature flag, indicating that the A-Host
+ *	enabled HNP support.
  * @name: Identifies the controller hardware type.  Used in diagnostics
  * 	and sometimes configuration.
  * @dev: Driver model state for this abstract device.
@@ -480,9 +492,14 @@
  *
  * Except for the driver data, all fields in this structure are
  * read-only to the gadget driver.  That driver data is part of the
- * "driver model" infrastructure in 2.5 (and later) kernels, and for
+ * "driver model" infrastructure in 2.6 (and later) kernels, and for
  * earlier systems is grouped in a similar structure that's not known
  * to the rest of the kernel.
+ *
+ * Values of the three OTG device feature flags are updated before the
+ * setup() call corresponding to USB_REQ_SET_CONFIGURATION, and before
+ * driver suspend() calls.  They are valid only when is_otg, and when the
+ * device is acting as a B-Peripheral (so is_a_peripheral is false).
  */
 struct usb_gadget {
 	/* readonly to gadget driver */
@@ -491,6 +508,11 @@
 	struct list_head		ep_list;	/* of usb_ep */
 	enum usb_device_speed		speed;
 	unsigned			is_dualspeed:1;
+	unsigned			is_otg:1;
+	unsigned			is_a_peripheral:1;
+	unsigned			b_hnp_enable:1;
+	unsigned			a_hnp_support:1;
+	unsigned			a_alt_hnp_support:1;
 	const char			*name;
 	struct device			dev;
 };
@@ -525,6 +547,10 @@
  * doesn't support such attempts, or its support has not been enabled
  * by the usb host.  Drivers must return device descriptors that report
  * their ability to support this, or hosts won't enable it.
+ *
+ * This may also try to use SRP to wake the host and start enumeration,
+ * even if OTG isn't otherwise in use.  OTG devices may also start
+ * remote wakeup even when hosts don't explicitly enable it.
  */
 static inline int usb_gadget_wakeup (struct usb_gadget *gadget)
 {
@@ -568,6 +594,107 @@
 	return gadget->ops->set_selfpowered (gadget, 0);
 }
 
+/**
+ * usb_gadget_vbus_connect - Notify controller that VBUS is powered
+ * @gadget:The device which now has VBUS power.
+ *
+ * This call is used by a driver for an external transceiver (or GPIO)
+ * that detects a VBUS power session starting.  Common responses include
+ * resuming the controller, activating the D+ (or D-) pullup to let the
+ * host detect that a USB device is attached, and starting to draw power
+ * (8mA or possibly more, especially after SET_CONFIGURATION).
+ *
+ * Returns zero on success, else negative errno.
+ */
+static inline int
+usb_gadget_vbus_connect(struct usb_gadget *gadget)
+{
+	if (!gadget->ops->vbus_session)
+		return -EOPNOTSUPP;
+	return gadget->ops->vbus_session (gadget, 1);
+}
+
+/**
+ * usb_gadget_vbus_draw - constrain controller's VBUS power usage
+ * @gadget:The device whose VBUS usage is being described
+ * @mA:How much current to draw, in milliAmperes.  This should be twice
+ *	the value listed in the configuration descriptor bMaxPower field.
+ *
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume.  For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static inline int
+usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+	if (!gadget->ops->vbus_draw)
+		return -EOPNOTSUPP;
+	return gadget->ops->vbus_draw (gadget, mA);
+}
+
+/**
+ * usb_gadget_vbus_disconnect - notify controller about VBUS session end
+ * @gadget:the device whose VBUS supply is being described
+ *
+ * This call is used by a driver for an external transceiver (or GPIO)
+ * that detects a VBUS power session ending.  Common responses include
+ * reversing everything done in usb_gadget_vbus_connect().
+ *
+ * Returns zero on success, else negative errno.
+ */
+static inline int
+usb_gadget_vbus_disconnect(struct usb_gadget *gadget)
+{
+	if (!gadget->ops->vbus_session)
+		return -EOPNOTSUPP;
+	return gadget->ops->vbus_session (gadget, 0);
+}
+
+/**
+ * usb_gadget_connect - software-controlled connect to USB host
+ * @gadget:the peripheral being connected
+ *
+ * Enables the D+ (or potentially D-) pullup.  The host will start
+ * enumerating this gadget when the pullup is active and a VBUS session
+ * is active (the link is powered).  This pullup is always enabled unless
+ * usb_gadget_disconnect() has been used to disable it.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static inline int
+usb_gadget_connect (struct usb_gadget *gadget)
+{
+	if (!gadget->ops->pullup)
+		return -EOPNOTSUPP;
+	return gadget->ops->pullup (gadget, 1);
+}
+
+/**
+ * usb_gadget_disconnect - software-controlled disconnect from USB host
+ * @gadget:the peripheral being disconnected
+ *
+ * Disables the D+ (or potentially D-) pullup, which the host may see
+ * as a disconnect (when a VBUS session is active).  Not all systems
+ * support software pullup controls.
+ *
+ * This routine may be used during the gadget driver bind() call to prevent
+ * the peripheral from ever being visible to the USB host, unless later
+ * usb_gadget_connect() is called.  For example, user mode components may
+ * need to be activated before the system can talk to hosts.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static inline int
+usb_gadget_disconnect (struct usb_gadget *gadget)
+{
+	if (!gadget->ops->pullup)
+		return -EOPNOTSUPP;
+	return gadget->ops->pullup (gadget, 0);
+}
+
+
 
 /*-------------------------------------------------------------------------*/
 
@@ -601,6 +728,12 @@
  * means the driver will handle setup() requests needed to enumerate (and
  * meet "chapter 9" requirements) then do some useful work.
  *
+ * If gadget->is_otg is true, the gadget driver must provide an OTG
+ * descriptor during enumeration, or else fail the bind() call.  In such
+ * cases, no USB traffic may flow until both bind() returns without
+ * having called usb_gadget_disconnect(), and the USB host stack has
+ * initialized.
+ *
  * Drivers use hardware-specific knowledge to configure the usb hardware.
  * endpoint addressing is only one of several hardware characteristics that
  * are in descriptors the ep0 implementation returns from setup() calls.
@@ -634,6 +767,12 @@
  * the (remote) host can't do that any longer; or an error state might
  * be cleared, to make the device behave identically whether or not
  * power is maintained.
+ *
+ * If the OTG b_hnp_enabled flag is set during a suspend() call, the
+ * device may use HNP to switch from "B-Peripheral" to "B-Host" mode
+ * (or back from "A-Peripheral" mode to the original "A-Host") if
+ * the gadget driver calls usb_gadget_disconnect() before the device
+ * is resumed.
  */
 struct usb_gadget_driver {
 	char			*function;
diff -Nru a/include/linux/videodev.h b/include/linux/videodev.h
--- a/include/linux/videodev.h	2004-07-13 13:12:22 -07:00
+++ b/include/linux/videodev.h	2004-07-13 13:12:22 -07:00
@@ -429,8 +429,9 @@
 #define VID_HARDWARE_CPIA2	33
 #define VID_HARDWARE_VICAM      34
 #define VID_HARDWARE_SF16FMR2	35
-#define VID_HARDWARE_W9968CF    36
+#define VID_HARDWARE_W9968CF	36
 #define VID_HARDWARE_SAA7114H   37
+#define VID_HARDWARE_SN9C102	38
 #endif /* __LINUX_VIDEODEV_H */
 
 /*
