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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 .) # # * 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 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: David Brownell # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # # 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 # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 # Signed-off-by: Greg Kroah-Hartman # # 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 + +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: +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 . + +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 +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 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 #include #include +#include #include #include @@ -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 = ð_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 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 - 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 . + + See 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 * + * * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "" +#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 * + * * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 * + * * + * 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 +#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 ", + .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 * + * * + * 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 +#include +#include +#include +#include +#include + +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 */ + + /* 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 * + * * + * 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 ", + .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 * + * * + * 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 ", + .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 #include -#include #include +#include #include #include #include #include #include #include -#include #include #include #include #include +#include #include #include +#include +#include #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 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 #include #include +#include #include #include #include #include #include +#include #include -#include +#include -#include "w9968cf_externaldef.h" +#include + +#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 "" #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 -#include -#include -#include - -#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 * + * * + * 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 +#include + +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 #include #include - -#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 #include #include - -#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 #include #include +#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 #include #include - -#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 #include #include - -#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 #include #include - -#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 #include #include -#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 , Bill Ryder , Kuba Ober " #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 #include #include - -#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 #include #include - -#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 #include #include - -#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 #include #include - -#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 " #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 #include #include - -#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 #include #include - -#ifdef CONFIG_USB_SERIAL_DEBUG - static int debug = 1; - #define DEBUG -#else - static int debug; - #undef DEBUG -#endif - #include - #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 #include -#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 #include #include - -#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 #include #include - - +#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 #include #include - -#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 #include #include - -#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 #include #include - -#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 #include #include +#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 #include #include - - -#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 #include #include - -#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 #include #include - -#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 */ /*