## Automatically generated incremental diff ## From: linux-2.4.22-bk41 ## To: linux-2.4.22-bk42 ## Robot: $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $ diff -urN linux-2.4.22-bk41/CREDITS linux-2.4.22-bk42/CREDITS --- linux-2.4.22-bk41/CREDITS 2003-10-26 02:49:49.000000000 -0800 +++ linux-2.4.22-bk42/CREDITS 2003-10-26 02:49:52.000000000 -0800 @@ -2590,6 +2590,13 @@ S: Tucson, Arizona S: USA +N: Luca Risolia +E: luca_ing@libero.it +D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chip +S: Via Libertà 41/a +S: Osio Sotto, 24046, Bergamo +S: Italy + N: William E. Roadcap E: roadcapw@cfw.com W: http://www.cfw.com/~roadcapw @@ -3439,4 +3446,4 @@ # alphabetically. Leonard used to be very proud of being the # last entry, and he'll get positively pissed if he can't even # be second-to-last. (and this file really _is_ supposed to be -# in alphabetic order) +# in alphabetic order) diff -urN linux-2.4.22-bk41/Documentation/Configure.help linux-2.4.22-bk42/Documentation/Configure.help --- linux-2.4.22-bk41/Documentation/Configure.help 2003-10-26 02:49:49.000000000 -0800 +++ linux-2.4.22-bk42/Documentation/Configure.help 2003-10-26 02:49:52.000000000 -0800 @@ -14663,6 +14663,25 @@ The module will be called usb-ohci.o. If you want to compile it as a module, say M here and read . +SL811HS (x86, StrongARM) support +CONFIG_USB_SL811HS + Embedded Open Host Controller SL811HS from CYPRESS SEMICONDUCTOR INC. + + + Board USB1104 in i386 architecture with PC/104-bus. + + + + StrongARM is currently not testet and not for PC/104-bus! + StrongARM need a special hardware with Chip Select directly from CPU. + See also SL811HS_ALT. + +SL811HS_ALT (x86, StrongARM) support +CONFIG_USB_SL811HS_ALT + Embedded Open Host Controller SL811HS from CYPRESS SEMICONDUCTOR INC. + Alternate with isochornous mode and better interrupt handling. + See also SL811HS. + USB Human Interface Device (full HID) support CONFIG_USB_HID Say Y here if you want full HID support to connect keyboards, @@ -15276,6 +15295,32 @@ The module will be called ov511.o. If you want to compile it as a module, say M here and read . +USB W996[87]CF Camera support +CONFIG_USB_W9968CF + Say Y here if you want support for cameras based on + Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. + + This driver has an optional plugin, which is distributed as a + separate module only (released under GPL). It contains code that + allows you to use higher resolutions and framerates, and can't + be included into the official Linux kernel for performance + purposes. + At the moment the driver needs a third-part 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. + Information on this API and pointers to "v4l" programs may be found + on the WWW at . + + 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 . + USB Communication Class Ethernet device support CONFIG_USB_CDCETHER This driver supports devices conforming to the Communication Device @@ -15776,6 +15821,88 @@ The module will be called speedtch.o. If you want to compile it as a module, say M here and read . +CONFIG_USB_GADGET + USB is a master/slave protocol, organized with one master + host (such as a PC) controlling up to 127 peripheral devices. + The USB hardware is asymmetric, which makes it easier to set up: + you can't connect two "to-the-host" connectors to each other. + + Linux can run in the host, or in the peripheral. In both cases + you need a low level bus controller driver, and some software + talking to it. Peripheral controllers are often discrete silicon, + or are integrated with the CPU in a microcontroller. The more + familiar host side controllers have names like like "EHCI", "OHCI", + or "UHCI", and are usually integrated into southbridges on PC + motherboards. + + Enable this configuration option if you want to run Linux inside + a USB peripheral device. Configure one hardware driver for your + peripheral/device side bus controller, and a "gadget driver" for + your peripheral protocol. (If you use modular gadget drivers, + you may configure more than one.) + + If in doubt, say "N" and don't enable these drivers; most people + don't have this kind of hardware (except maybe inside Linux PDAs). + +CONFIG_USB_NET2280 + NetChip 2280 is a PCI based USB peripheral controller which + supports both full and high speed USB 2.0 data transfers. + + It has six configurable endpoints, as well as endpoint zero + (for control transfers) and several endpoints with dedicated + functions. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "net2280" and force all + gadget drivers to also be dynamically linked. + +CONFIG_USB_ZERO + Gadget Zero is a two-configuration device. It either sinks and + sources bulk data; or it loops back a configurable number of + transfers. It also implements control requests, for "chapter 9" + conformance. The driver needs only two bulk-capable endpoints, so + it can work on top of most device-side usb controllers. It's + useful for testing, and is also a working example showing how + USB "gadget drivers" can be written. + + Make this be the first driver you try using on top of any new + USB peripheral controller driver. Then you can use host-side + test software, like the "usbtest" driver, to put your hardware + and its driver through a basic set of functional tests. + + Gadget Zero also works with the host-side "usb-skeleton" driver, + and with many kinds of host-side test software. You may need + to tweak product and vendor IDs before host software knows about + this device, and arrange to select an appropriate configuration. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_zero". + +CONFIG_USB_ETH + This driver implements Ethernet style communication, in either + of two ways: + + - The "Communication Device Class" (CDC) Ethernet Control Model. + That protocol is often avoided with pure Ethernet adapters, in + favor of simpler vendor-specific hardware, but is widely + supported by firmware for smart network devices. + + - On hardware can't implement that protocol, a simpler approach + is used, placing fewer demands on USB. + + Within the USB device, this gadget driver exposes a network device + "usbX", where X depends on what other networking devices you have. + Treat it like a two-node Ethernet link: host, and gadget. + + The Linux-USB host-side "usbnet" driver interoperates with this + driver, so that deep I/O queues can be supported. On 2.4 kernels, + use "CDCEther" instead, if you're using the CDC option. That CDC + mode should also interoperate with standard CDC Ethernet class + drivers on other host operating systems. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_ether". + Always do synchronous disk IO for UBD CONFIG_BLK_DEV_UBD_SYNC The User-Mode Linux port includes a driver called UBD which will let diff -urN linux-2.4.22-bk41/Documentation/usb/sl811hc.txt linux-2.4.22-bk42/Documentation/usb/sl811hc.txt --- linux-2.4.22-bk41/Documentation/usb/sl811hc.txt 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/Documentation/usb/sl811hc.txt 2003-10-26 02:49:52.000000000 -0800 @@ -0,0 +1,331 @@ +README for embedded host controller SL811 (i386) +================================================ + +Original drivers from Pei Liu for ARM architecture only. +Documentaion and readme for Architecture x86 (ADNP/1486) premealy. + + +Kernel configuration: +--------------------- +o Patch USB drivers into your kerneltree + cd Your_kernel_root + + gunzip linux-2.4.20-usb-1.patch.gz + patch -p1 -T < linux-2.4.20-usb*.patch + OR + gunzip -dc linux-2.4.20-usb*.patch.gz | patch -p1 -T + +o Load default configuration for CP486SX/2 with ADNP/1486 and USB + arch/i386/adnp1486-usb104-SSV20030516 + +o Run "make configure" and set / verify this entries + Code maturity level options ---> + [*] Prompt for development and/or incomplete code/drivers + General setup ---> + [*] PCI support + (BIOS) PCI access mode + [ ] PCI device name database + SCSI support ---> + SCSI support + SCSI disk support + (8) Maximum number of SCSI disks that can be loaded as modules + ... Disable all other options ... + SCSI low-level drivers ---> + ... Disable all low level drivers ... + Input core support ---> + Input core support (Need only for Keyboard or Mouse) + Keyboard support + Mouse support + Character devices ---> + [*] Virtual terminal (Need only for Keyboard) + Console drivers ---> + [*] VGA text console (Need only for Key/Mouse) + Frame-buffer support ---> (... or use FB) + USB support ---> + Support for USB + [ ] USB verbose debug messages (Optional) + [*] Preliminary USB device filesystem (Optional for debugging) + --- USB Host Controller Drivers + SL811HS Alternate (support isochronous mode) + ... + SL811HS (x86, StrongARM) support (old driver) + Disable all others "USB Host Controller Drivers" + --- USB Device Class drivers + USB Mass Storage support + ... and some aditional devices ... + USB Printer support + USB HIDBP Keyboard (basic) support + USB HIDBP Mouse (basic) support + USB Serial Converter support ---> + USB Serial Converter support + USB FTDI Single Port Serial Driver +o We have no PCI- and no SCSI-System, but all USB-drivers need CONFIG_PCI=y. + USB-Floppy driver need the SCSI-Subsystem, so we must enable this global + and disable all low level drivers in this menu. +--- OR --- + Download origanal kerneltree, and patch comlplete ADNP + with USB support and load configuration for this: + bunzip2 -c linux-2.4.20.tar.bz2 | tar xvf - + gunzip -c linux-2.4.20-SSV20030516.diff.gz | patch -p1 -T + Configuration: arch/i386/adnp1486-usb104-SSV20030516 + +o Compile the kernel + make dep + make ROOT_DEV=/dev/ram0 zImage + make modules + export INSTALL_MOD_PATH="`pwd`/_install" ; make modules_install + + +Default device configuration hc_sl811.o for USB1/104: +----------------------------------------------------- +io = 0x220 +irq = 12 +Remember: Second Controller was handled internal with IO offset +2. + + +Installation hc_sl811.o for CF1/USB: +------------------------------------ +Compact Flash to USB adapter in io address of ide driver. It is for embedded +deviced only. +Please disable IDE driver in kernel configuration or do not load IDE drivers! +Change MAX_CONTROLERS = 1 into source asm/sl811-hw.h, recompile driver! + First Controller only + insmod hc_sl811.o io=0x1F0 irq=14 + Second Controller only + insmod hc_sl811.o io=0x3F6 irq=14 + +Driver hc_sl811.o can not handle both controllers at same time. +This driver need a address-offset of 2 between controllers. +Please use alternate driver sl811.o instand. + + +Installation Alternate driver sl811.o: +-------------------------------------- +This driver have a better interrupt handler, but don't tested with all devices. + +Install both controllers on USB1-104 (default): + insmod sl811.o io=0x220,0x222 irq=12,12 + +Install both controllers on CF/USB1: + insmod sl811.o io=0x1f0,0x3f6 irq=14,14 + +Second controller can disable with specific IOBASE=0 for this controller. + + +General about USB: +------------------ +Please install first the driver for hardware, +and than plugin the hardware into first USB port. + +If your hardware find no driver the usbcore give ub a massage for missing +driver on conole or in file /proc/kmsg such as: + new USB device -1.9, assigned address 7 + USB device 7 (vend/prod 0x403/0x8372) is not claimed by any active drive +In this case remove the hardware from USB port, install the driver and +than plugin hardware again. +A list of driver for this missing hardware can found in file +/_install/lib/modules/2.4.20/modules.usbmap +Search the number 8372 in this file an verify the vendor ID. So you will +find the driver name "ftdi_sio" in this file. +If your hardware not listen in this file. Look into source and search your +numbers in source. +More read file:/Documentation/usb/proc_usb_info.txt + +Drivers are all under contructions. So some drivers make a kernel panic. In +this case read all about the drivers dokumentaiona and the drivers source. +Some drivers need a other kernel driver, but not strictly checked in kenel +configuration. Here can help the ksymsoops. + + + +Install a Floppy (NEC UF0001) or USB Stick Fujitsu/Siemens/iomega: +------------------------------ +Copy files to target (FTP) and load all drivers. +Load Generic USB-Handler + insmod usbcore.o +Load USB-Host controller, parameters are optional (default urb_debug=0 io=220 irq=12) + insmod hc_sl811.o +Run the USB-Filesystem + mount -t usbdevfs usbdevfs /proc/bus/usb +Load drivers for disk storage and file systems + insmod scsi_mod.o + insmod usb-storage.o + insmod fat.o + insmod vfat.o + insmod sd_mod.o +Create node for floppy + mknod /dev/sda b 8 0 + +Put a disk into your floppy anth than plugin a USB-Floppy (such NEC Model UF0001) +into first USB-Port. Some Messages will be list on console or in file /proc/kmsg. +The disk is power on and the SCSI driver will search some partions on disk. Floppy +have no partions, so must use the first SCSI device without a partion number for mount. +Than mount the floppy: + mount /dev/sda /mnt -t vfat + +If you see a partions check with valid partion 1, you should mount this partion. +Mostly Memory Sticks are formated with one partion. But if Windows format it again, +no partions is use. + +You see that: + Partition check: + sda: sda1 +Than mount with follow steps: + mknod /dev/sda1 b 8 1 + mount /dev/sda1 /mnt -t vfat + +Create complete list of nodes for SCSI-devices: + # First inserted device + echo -n "Create /dev/sda... " + mknod /dev/sda b 8 0 + for i in 1 2 3 4 5 6 7 + do + echo -n "$i " + mknod sda$i c 8 $i + done + # Second inserted device + echo -n "Create /dev/sdb... " + mknod /dev/sdb b 8 16 + mknod /dev/sdb1 b 8 17 + echo " done" + # Set some rights + chown root.disk sd* + chmod 660 sd* + + +Install a Keyboard: +------------------- +Copy files to target (FTP) and load all drivers. +Load Generic USB-Handler + insmod usbcore.o +Load USB-Host controller, parameters are optional (default urb_debug=0 io=220 irq=12) + insmod hc_sl811.o +Run the USB-Filesystem + mount -t usbdevfs usbdevfs /proc/bus/usb +Load drivers for USB-Keyboard + insmod input.o + insmod keybdev.o + insmod usbkbd.o +Now you can plugin Keyboard into first USB-Port and login on first console. + +Something stuff: +"Undefined Symbols handle_scancode, keyboard_tasklet, kbd_ledfunc" at install ??? +USB keyboard need PC-style keyboard driver, because the USB driver +simulate standard AT-Keycodes. A normaly AT- or PS/2-Keyboard must not +exist for this. The driver says normaly error (Timeout) on boot. +You must enable CONFIG_VT in kernel konfiguration! +Character devices ---> + [*] Virtual terminal + [ ] Support for console on virtual terminal + +Read + + +Install a Mouse: +---------------- +Load Generic USB-Handle + insmod usbcore.o +Load USB-Host controller + insmod hc_sl811.o +Run the USB-Filesystem + mount -t usbdevfs usbdevfs /proc/bus/usb +Load Generic Input device + insmod input.o +Load USB-Mouse driver + insmod input.o + insmod mousedev.o + insmod usbmouse.o + +Read + + +Install a serial adapter (Sample FTDI): +--------------------------------------- +Load Generic USB-Handle + insmod usbcore.o +Load USB-Host controller + insmod hc_sl811.o +Run the USB-Filesystem + mount -t usbdevfs usbdevfs /proc/bus/usb +Load Generic derial device and hardware specific device + insmod usbserial.o + insmod ftdi_sio.o +Create node entry for this device + mknod /dev/ttyUSB0 c 188 0 +Than plugin the hardware into first USB port and +use serial device on /dev/ttyUSB0, such call a login: + /sbin/getty 115200 ttyUSB0 vt100 & + + +USB-Utils: +---------- +- usb-0.6-7.rpm, usb-0.6-7.src.rpm + /usr/sbin/lsusb, /usr/share/usb.ids + Good tool to list devices parameters. + You must load usbcore.o, hc_sl811.o and proc-usb before + program works right (use script usb.sh). + More details: Install usb-0.6-7.rpm on Your desktop and use "man lsusb". + + +Known Bugs: +----------- + +PL2302 Profilic USB to serial converter will not work with hc_sl811.c (Bulk/Timeout). +USB Floppy will not work with alternate driver sl811.o (Sector not found) + + +CHANGELOG: +---------- +* Fri 03 Okt 2003 hne +- Patch for 2.4.23-pre6 +- Only low level port io in hardware include as inline functions. +- Move hc_sl811 and sl811 into host directory. +- sl811 for two controllers (alternate x86 only). + +* Mit 24 Sep 2003 hne +- Misplaced "host/uhci.o" in Makefile. +- Move all x86/arm arch depens from main sl811.c to sl811-hw.h. + +* Die 23 Sep 2003 hne +- Put arm and x86 architectur into separate file in include directory. +- Modifications for both controllers on CF/USB1, alternate sl811 only. + Parameter for CF/USB1: "io=0x1f0,0x3f6 irq=14". + +* Fri 19 Sep 2003 hne +- First version for both controllers on USB1-104. +- Alternative driver sl811.c from kernel 2.4.22 (thanks Yinah), + also for 2.4.20. USB Sticks works, Floppy not. + +* Die 02 Sep 2003 hne +- IO range only 2 address. For CF1USB we need io addres 3F6 and 3F7, + but do not use 3f8 (ttyS0). + +* Mon 11 Aug 2003 hne +- Comments for using iomega Memory Stick + +* Don 12 Jun 2003 hne +- Added Bus-Name for Kernel 2.4.20, no pattern_test at unload driver. +- more doc + +* Fri May 16 2003 hne +- More comments, new patchfile, include usb-konfiguration as file + +* Wed May 14 2003 hne +- Patch error: Old Sources was in Kerneltree! + +* Mon Mar 17 2003 hne +- Copy usb SL811 from 2.4.19-rc into 2.4.20 kerneltree +- Add SL811 in Config and Make + +* 18.11.2002 hne +- hc_sl811_rh.c: + rh_unlink_urb: Use usb_dec_dev_use instand of usb_put_dev. Function + usb_put_dev isn't known in this module. Some others have a macro for + this. What is right usb_put_dev or usb_dec_dev_use? +- hc_sl811.c: + Split into 3 files. Arcitectures store in hc_sl811-arm.c and hc_sl811-x86.c + Correct release_region() for both io address, so we can unload modul and + load again without reboot. + All IO access use 8 bit Data and register number (type __u8). + All functions static. + Only x86: base_addr renamed to io. data_reg_addr not used. diff -urN linux-2.4.22-bk41/Documentation/usb/w9968cf.txt linux-2.4.22-bk42/Documentation/usb/w9968cf.txt --- linux-2.4.22-bk41/Documentation/usb/w9968cf.txt 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/Documentation/usb/w9968cf.txt 2003-10-26 02:49:52.000000000 -0800 @@ -0,0 +1,486 @@ + + W996[87]CF JPEG USB Dual Mode Camera Chip driver for Linux 2.4 + ============================================================== + + - Documentation - + + +Index +===== +1. Copyright +2. License +3. Overview +4. Supported devices +5. Kernel configuration and third-part module compilation +6. Module loading +7. Module paramaters +8. Credits + + +1. Copyright +============ +Copyright (C) 2002 2003 by Luca Risolia + + +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 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. + +The driver relies on the Video4Linux, USB and I2C core modules of the Linux +kernel, version 2.4.19 or greater, and is not compatible in any way with +previous versions. 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 driver is split into two modules: the basic one, "w9968cf", is needed for +the supported devices to work; the second one, "w9968cf-vpp", is an optional +module, which provides some useful video post-processing functions like video +decoding, up-scaling and colour conversions (these routines can't be included +into official kernels). Once the driver is installed, every time an application +tries to open a recognized device, "w9968cf" checks the presence of the +"w9968cf-vpp" module and loads it automatically by default. + +Up to 32 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. + +To change the default settings for each camera, many paramaters can be passed +through command line when the module is loaded into memory. + +It is recommended to install the latest and full featured version of the +W996[87]CF driver, which can be found at: +http://go.lamarinapunto.com/ + +The "ovcamchip" module is part of the OV511 driver, version 2.25, which can be +downloaded from internet: +http://alpha.dyndns.org/ov511/ +To know how to patch, compile and load it, read the "Kernel configuration" +paragraph. + + +4. Supported devices +==================== +At the moment, known W996[87]CF 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 + +If you know any other W996[87]CF 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" +module work. +For a list of supported CMOS sensors, please visit the module author homepage: +http://alpha.dyndns.org/ov511/ + +Possible external microcontrollers of those webcams are not supported: this +means that still images can't be downloaded from the device memory. + +Furthermore, it's worth to note that I was only able to run tests on my +"Creative Labs Video Blaster WebCam Go". Donations of other models, for +additional testing and full support, would be much appreciated. + + +5. Kernel configuration and third-part module compilation +========================================================= +As noted above, kernel 2.4.19 is the minimum for this driver; for it to work +properly, the driver needs kernel support for Video4Linux, USB and I2C, and a +third-part module for the CMOS sensor. + +The following options of the kernel configuration file must be enabled and +corresponding modules must be compiled: + + # Multimedia devices + # + CONFIG_VIDEO_DEV=m + + # I2C support + # + CONFIG_I2C=m + +The I2C core module can be compiled statically in the kernel as well. + + # USB support + # + CONFIG_USB=m + +In addition, depending on the hardware being used, just one of the modules +below is necessary: + + # USB Host Controller Drivers + # + CONFIG_USB_EHCI_HCD=m + CONFIG_USB_UHCI=m + CONFIG_USB_UHCI_ALT=m + CONFIG_USB_OHCI=m + +Also, make sure "Enforce bandwidth allocation" is NOT enabled. + +The /proc filesystem can be optionally built into the kernel: + + # File systems + # + CONFIG_PROC_FS=y + + # Video For Linux + # + CONFIG_VIDEO_PROC_FS=y + + # USB Multimedia devices + # + CONFIG_USB_W9968CF=m + +The last module we need is "ovcamchip.o". To obtain it, you have to download +the OV511 driver, version 2.25 - don't use other versions - which is available +at http://alpha.dyndns.org/ov511/ . Then you have to download the latest +version of the full featured W996[87]CF driver, which contains a patch for the +"ovcamchip" module; it is available at http://go.lamarinapunto.com . +Once you have obtained the packages, decompress, patch and compile the +"ovcamchip" module. In other words: + + [user@localhost home]$ tar xvzf w9968cf-x.x.tar.gz + [user@localhost home]$ tar xvjf ov511-2.25.tar.bz2 + [user@localhost home]$ cd ov511-2.25 + [user@localhost ov511-2.25]$ patch -p1 < \ + /path/to/w9968cf-x.x/ov511-2.25.patch + [user@localhost ov511-2.25]$ make + +It's worth to note that the full featured version of the W996[87]CF driver +can also be installed overwriting the one in the kernel; in this case, read the +documentation included in the package. + +If everything went well, the W996[87]CF driver can be immediatly used (see next +paragraph). + + +6. Module loading +================= +To use the driver, it is necessary to load the "w9968cf" module into memory +after every other module required; they are named, in order: "videodev", +"usbcore", then "ehci-hcd", "usb-uhci", "uhci", "usb-ohci" (just one), and also +"i2c-core" and "ovcamchip". + +Loading can be done this way, from root: + + [root@localhost home]# modprobe i2c-core + [root@localhost ov511-x.xx]# insmod ./ovcamchip.o + [root@localhost home]# modprobe w9968cf + +At this point the devices should be recognized. There are two ways of verifying +that the loading process has gone well: the first is to analyze kernel +messages: + + [user@localhost home]$ dmesg + +A second way is to retrieve informations from the entries that have just been +created in the /proc/video/w9968cf/ directory; this feature works if and only +if the kernel has been built with the /proc filesystem support. +As an example, the following command will print the list of registered cameras: + + [user@localhost home]$ cat /proc/video/w9968cf/global + +There are a lot of parameters the module can use to change the default +settings for each device. To list every possible parameter with a brief +explanation about them and which syntax to use, it is recommended to run the +"modinfo" command: + + [root@locahost home]# modinfo w9968cf + + +7. Module paramaters +==================== + +Module paramaters are listed below: +------------------------------------------------------------------------------- +Name: vppmod_load +Type: int +Syntax: <0|1> +Description: Automatic 'w9968cf-vpp' module loading: 0 disabled, 1 enabled. + 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 + capabilities to the 'w9968cf' module,like software up-scaling, + colour conversions and video decoding. +Default: 1 +------------------------------------------------------------------------------- +Name: simcams +Type: int +Syntax: +Description: Number of cameras allowed to stream simultaneously. + n may vary from 0 to 32. +Default: 32 +------------------------------------------------------------------------------- +Name: video_nr +Type: int array (min = 0, max = 32) +Syntax: <-1|n[,...]> +Description: Specify V4L minor mode number. + -1 = use next available + n = use minor number n + You can specify 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: packet_size +Type: int array (min = 0, max = 32) +Syntax: +Description: Specify the maximum data payload size in bytes for alternate + settings, for each device. n is scaled between 63 and 1023. +Default: 1023 +------------------------------------------------------------------------------- +Name: max_buffers +Type: int array (min = 0, max = 32) +Syntax: +Description: Only for advanced users. + Specify the maximum number of video frame buffers to allocate + for each device, from 2 to 32. +Default: 2 +------------------------------------------------------------------------------- +Name: double_buffer +Type: int array (min = 0, max = 32) +Syntax: <0|1[,...]> +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 at all, or try to + decrease the 'clockdiv' module paramater value. +Default: 1 for every device. +------------------------------------------------------------------------------- +Name: clamping +Type: int array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Video data clamping: 0 disabled, 1 enabled. +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: filter_type +Type: int array (min = 0, max = 32) +Syntax: <0|1|2[,...]> +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. +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: largeview +Type: int array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Large view: 0 disabled, 1 enabled. +Default: 1 for every device. +------------------------------------------------------------------------------- +Name: upscaling +Type: int array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Software scaling (for non-compressed video only): + 0 disabled, 1 enabled. + 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. +------------------------------------------------------------------------------- +Name: decompression +Type: int array (min = 0, max = 32) +Syntax: <0|1|2[,...]> +Description: Software video decompression: + 0 = disables decompression + (doesn't allow formats needing decompression). + 1 = forces decompression + (allows formats needing decompression only). + 2 = allows any permitted formats. + Formats supporting (de)compressed video are YUV422P and + 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, this paramater is set to, + forcing decompression is not allowed; in this case this + paramater is set to 2. +------------------------------------------------------------------------------- +Name: force_palette +Type: int array (min = 0, max = 32) +Syntax: <0|9|10|13|15|8|7|1|6|3|4|5[,...]> +Description: Force picture palette. + In order: + 0 = Off - allows any of the following formats: + 9 = UYVY 16 bpp - Original video, compression disabled + 10 = YUV420 12 bpp - Original video, compression enabled + 13 = YUV422P 16 bpp - Original video, compression enabled + 15 = YUV420P 12 bpp - Original video, compression enabled + 8 = YUVY 16 bpp - Software conversion from UYVY + 7 = YUV422 16 bpp - Software conversion from UYVY + 1 = GREY 8 bpp - Software conversion from UYVY + 6 = RGB555 16 bpp - Software conversion from UYVY + 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'. +Default: 0 for every device. Initial palette is 9 (UYVY). +Note: If 'w9968cf-vpp' is not loaded, this paramater is set to 9. +------------------------------------------------------------------------------- +Name: force_rgb +Type: int array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Read RGB video data instead of BGR: + 1 = use RGB component ordering. + 0 = use BGR component ordering. + This parameter has effect when using RGBX palettes only. +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: autobright +Type: long array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: CMOS sensor automatically changes brightness: + 0 = no, 1 = yes +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: autoexp +Type: long array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: CMOS sensor automatically changes exposure: + 0 = no, 1 = yes +Default: 1 for every device. +------------------------------------------------------------------------------- +Name: lightfreq +Type: long array (min = 0, max = 32) +Syntax: <50|60[,...]> +Description: Light frequency in Hz: + 50 for European and Asian lighting, 60 for American lighting. +Default: 50 for every device. +------------------------------------------------------------------------------- +Name: bandingfilter +Type: long array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Banding filter to reduce effects of fluorescent + lighting: + 0 disabled, 1 enabled. + This filter tries to reduce the pattern of horizontal + light/dark bands caused by some (usually fluorescent) lighting. +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: clockdiv +Type: long array (min = 0, max = 32) +Syntax: <-1|n[,...]> +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. +Default: -1 for every device. +------------------------------------------------------------------------------- +Name: backlight +Type: long array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Objects are lit from behind: + 0 = no, 1 = yes +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: mirror +Type: long array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Reverse image horizontally: + 0 = no, 1 = yes +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: sensor_mono +Type: long array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: The CMOS sensor is monochrome: + 0 = no, 1 = yes +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: brightness +Type: long array (min = 0, max = 32) +Syntax: +Description: Set picture brightness (0-65535). + This parameter has no effect if 'autobright' is enabled. +Default: 31000 for every device. +------------------------------------------------------------------------------- +Name: hue +Type: long array (min = 0, max = 32) +Syntax: +Description: Set picture hue (0-65535). +Default: 32768 for every device. +------------------------------------------------------------------------------- +Name: colour +Type: long array (min = 0, max = 32) +Syntax: +Description: Set picture saturation (0-65535). +Default: 32768 for every device. +------------------------------------------------------------------------------- +Name: contrast +Type: long array (min = 0, max = 32) +Syntax: +Description: Set picture contrast (0-65535). +Default: 50000 for every device. +------------------------------------------------------------------------------- +Name: whiteness +Type: long array (min = 0, max = 32) +Syntax: +Description: Set picture whiteness (0-65535). +Default: 32768 for every device. +------------------------------------------------------------------------------- +Name: debug +Type: int +Syntax: +Description: Debugging information level, from 0 to 6: + 0 = none (be cautious) + 1 = critical errors + 2 = significant informations + 3 = configuration or general messages + 4 = warnings + 5 = called functions + 6 = function internals + Level 5 and 6 are useful for testing only, when just one + device is used. +Default: 2 +------------------------------------------------------------------------------- +Name: specific_debug +Type: int +Syntax: <0|1> +Description: Enable or disable specific debugging messages: + 0 = print messages concerning every level <= 'debug' level. + 1 = print messages concerning the level indicated by 'debug'. +Default: 0 +------------------------------------------------------------------------------- + + +8. Credits +========== +The development would not have proceed much further without having looked at +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 + been taken from the OV511 driver by Mark McClelland; + +- memory management code has been copied from the bttv driver by Ralph Metzler, + Marcus Metzler and Gerd Knorr; + +- the low-level I2C read function has been written by Frédéric Jouault, who + also gave me commented logs about sniffed USB traffic taken from another + driver for another system; + +- the low-level I2C fast write function has been written by Piotr Czerczak; diff -urN linux-2.4.22-bk41/MAINTAINERS linux-2.4.22-bk42/MAINTAINERS --- linux-2.4.22-bk41/MAINTAINERS 2003-10-26 02:49:49.000000000 -0800 +++ linux-2.4.22-bk42/MAINTAINERS 2003-10-26 02:49:52.000000000 -0800 @@ -2068,6 +2068,13 @@ L: linux-usb-devel@lists.sourceforge.net S: Maintained +USB W9968CF DRIVER +P: Luca Risolia +M: luca_ing@libero.it +L: linux-usb-devel@lists.sourceforge.net +W: http://go.lamarinapunto.com +S: Maintained + VFAT FILESYSTEM: P: Gordon Chaffee M: chaffee@cs.berkeley.edu diff -urN linux-2.4.22-bk41/Makefile linux-2.4.22-bk42/Makefile --- linux-2.4.22-bk41/Makefile 2003-10-26 02:49:49.000000000 -0800 +++ linux-2.4.22-bk42/Makefile 2003-10-26 02:49:52.000000000 -0800 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 22 -EXTRAVERSION = -bk41 +EXTRAVERSION = -bk42 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff -urN linux-2.4.22-bk41/drivers/usb/Config.in linux-2.4.22-bk42/drivers/usb/Config.in --- linux-2.4.22-bk41/drivers/usb/Config.in 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/Config.in 2003-10-26 02:49:55.000000000 -0800 @@ -78,6 +78,7 @@ dep_tristate ' USB Philips Cameras' CONFIG_USB_PWC $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' USB SE401 Camera support' CONFIG_USB_SE401 $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' USB STV680 (Pencam) Camera support' CONFIG_USB_STV680 $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' USB W996[87]CF Camera support' CONFIG_USB_W9968CF $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_I2C dep_tristate ' USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)' CONFIG_USB_VICAM $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB diff -urN linux-2.4.22-bk41/drivers/usb/Makefile linux-2.4.22-bk42/drivers/usb/Makefile --- linux-2.4.22-bk41/drivers/usb/Makefile 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.22-bk42/drivers/usb/Makefile 2003-10-26 02:49:55.000000000 -0800 @@ -77,6 +77,9 @@ obj-y += host/usb-ohci.o endif +subdir-$(CONFIG_USB_SL811HS_ALT) += host +subdir-$(CONFIG_USB_SL811HS) += host + obj-$(CONFIG_USB_MOUSE) += usbmouse.o obj-$(CONFIG_USB_HID) += hid.o obj-$(CONFIG_USB_KBD) += usbkbd.o @@ -100,6 +103,7 @@ obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_VICAM) += vicam.o usbvideo.o obj-$(CONFIG_USB_OV511) += ov511.o +obj-$(CONFIG_USB_W9968CF) += w9968cf.o obj-$(CONFIG_USB_SE401) += se401.o obj-$(CONFIG_USB_STV680) += stv680.o obj-$(CONFIG_USB_PEGASUS) += pegasus.o diff -urN linux-2.4.22-bk41/drivers/usb/brlvger.c linux-2.4.22-bk42/drivers/usb/brlvger.c --- linux-2.4.22-bk41/drivers/usb/brlvger.c 2002-11-28 15:53:14.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/brlvger.c 2003-10-26 02:49:55.000000000 -0800 @@ -623,14 +623,14 @@ int firstpart = 6 - off; #ifdef WRITE_DEBUG - dbg3("off: %d, rs: %d, count: %d, firstpart: %d", + dbg3("off: %lld, rs: %d, count: %d, firstpart: %d", off, rs, count, firstpart); #endif firstpart = (firstpart < count) ? firstpart : count; #ifdef WRITE_DEBUG - dbg3("off: %d", off); + dbg3("off: %lld", off); dbg3("firstpart: %d", firstpart); #endif @@ -650,7 +650,7 @@ off +=2; #ifdef WRITE_DEBUG - dbg3("off: %d, rs: %d, count: %d, firstpart: %d, " + dbg3("off: %lld, rs: %d, count: %d, firstpart: %d, " "written: %d", off, rs, count, firstpart, written); #endif } diff -urN linux-2.4.22-bk41/drivers/usb/gadget/Config.in linux-2.4.22-bk42/drivers/usb/gadget/Config.in --- linux-2.4.22-bk41/drivers/usb/gadget/Config.in 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/gadget/Config.in 2003-10-26 02:49:55.000000000 -0800 @@ -5,6 +5,8 @@ # Long term, this likely doesn't all belong in one directory # Plan to split it up eventually. # +# CAREFUL! Some versions of "xconfig" don't execute this correctly. +# mainmenu_option next_comment comment 'Support for USB gadgets' @@ -18,11 +20,14 @@ comment 'USB Peripheral Controller Drivers' # assume all the dependencies may be undefined ("== true", yeech) + define_tristate CONFIG_USB_GADGET_CONTROLLER n if [ "$CONFIG_PCI" = "y" ] ; then tristate ' NetChip 2280 support' CONFIG_USB_NET2280 define_tristate CONFIG_USB_GADGET_CONTROLLER $CONFIG_USB_NET2280 fi + # pxa2xx_udc, goku_udc, and others also work on 2.4 ... + if [ "$CONFIG_USB_GADGET_CONTROLLER" = "y" -o "$CONFIG_USB_GADGET_CONTROLLER" = "m" ] ; then # @@ -44,25 +49,28 @@ if [ "$CONFIG_USB_ZERO" = "y" -o "$CONFIG_USB_ZERO" = "m" ]; then if [ "$CONFIG_USB_NET2280" = "y" -o "$CONFIG_USB_NET2280" = "m" ]; then define_bool CONFIG_USB_ZERO_NET2280 y - fi - if [ "$CONFIG_USB_PXA2XX" = "y" -o "$CONFIG_USB_PXA2XX" = "m" ]; then + else if [ "$CONFIG_USB_PXA2XX" = "y" -o "$CONFIG_USB_PXA2XX" = "m" ]; then define_bool CONFIG_USB_ZERO_PXA2XX y - fi + else if [ "$CONFIG_USB_GOKU" = "y" -o "$CONFIG_USB_GOKU" = "m" ]; then + define_bool CONFIG_USB_ZERO_GOKU y + fi fi fi # ... fi - dep_tristate ' Ethernet Gadget (EXPERIMENTAL)' CONFIG_USB_ETH $CONFIG_USB_GADGET_CONTROLLER + dep_tristate ' Ethernet Gadget (EXPERIMENTAL)' CONFIG_USB_ETH $CONFIG_USB_GADGET_CONTROLLER $CONFIG_NET if [ "$CONFIG_USB_ETH" = "y" -o "$CONFIG_USB_ETH" = "m" ]; then if [ "$CONFIG_USB_NET2280" = "y" -o "$CONFIG_USB_NET2280" = "m" ]; then define_bool CONFIG_USB_ETH_NET2280 y - fi - if [ "$CONFIG_USB_PXA2XX" = "y" -o "$CONFIG_USB_PXA2XX" = "m" ]; then + else if [ "$CONFIG_USB_PXA2XX" = "y" -o "$CONFIG_USB_PXA2XX" = "m" ]; then define_bool CONFIG_USB_ETH_PXA2XX y - fi + else if [ "$CONFIG_USB_GOKU" = "y" -o "$CONFIG_USB_GOKU" = "m" ]; then + define_bool CONFIG_USB_ETH_GOKU y + fi fi fi # ... fi - # ... or other gadget drivers: printer class, storage, hid, etc ... + + # ... or other gadget drivers: printer class, hid, etc ... fi fi diff -urN linux-2.4.22-bk41/drivers/usb/gadget/ether.c linux-2.4.22-bk42/drivers/usb/gadget/ether.c --- linux-2.4.22-bk41/drivers/usb/gadget/ether.c 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/gadget/ether.c 2003-10-26 02:49:55.000000000 -0800 @@ -23,8 +23,8 @@ // #define VERBOSE #include -#include #include +#include #include #include #include @@ -87,11 +87,12 @@ #endif /* 2.5 modified and renamed these */ - +#ifndef INIT_WORK #define work_struct tq_struct #define INIT_WORK INIT_TQUEUE #define schedule_work schedule_task #define flush_scheduled_work flush_scheduled_tasks +#endif /*-------------------------------------------------------------------------*/ @@ -117,28 +118,6 @@ /*-------------------------------------------------------------------------*/ -/* This driver keeps a variable number of requests queued, more at - * high speeds. (Numbers are just educated guesses, untuned.) - * Shrink the queue if memory is tight, or make it bigger to - * handle bigger traffic bursts between IRQs (assuming hw dma queues) - */ - -static unsigned qmult = 4; - -#define HS_FACTOR 5 - -#define qlen(gadget) \ - (qmult*((gadget->speed == USB_SPEED_HIGH) ? HS_FACTOR : 1)) - -/* defer IRQs on highspeed TX */ -#define TX_DELAY 8 - - -MODULE_PARM (qmult, "i"); -MODULE_PARM_DESC (qmult, "rx/tx buffering factor"); - -/*-------------------------------------------------------------------------*/ - /* Thanks to NetChip Technologies for donating this product ID. * * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! @@ -187,6 +166,7 @@ */ #ifdef CONFIG_USB_ETH_NET2280 #define CHIP "net2280" +#define DEFAULT_QLEN 4 /* has dma chaining */ #define DRIVER_VERSION_NUM 0x0111 #define EP0_MAXPACKET 64 static const char EP_OUT_NAME [] = "ep-a"; @@ -232,7 +212,7 @@ /* supports remote wakeup, but this driver doesn't */ /* no hw optimizations to apply */ -#define hw_optimize(g) do {} while (0); +#define hw_optimize(g) do {} while (0) #endif /* @@ -255,7 +235,28 @@ /* doesn't support remote wakeup? */ /* no hw optimizations to apply */ -#define hw_optimize(g) do {} while (0); +#define hw_optimize(g) do {} while (0) +#endif + +/* + * Toshiba TC86C001 ("Goku-S") UDC + * + * This has three semi-configurable full speed bulk/interrupt endpoints. + */ +#ifdef CONFIG_USB_ETH_GOKU +#define CHIP "goku" +#define DRIVER_VERSION_NUM 0x0116 +#define EP0_MAXPACKET 8 +static const char EP_OUT_NAME [] = "ep1-bulk"; +#define EP_OUT_NUM 1 +static const char EP_IN_NAME [] = "ep2-bulk"; +#define EP_IN_NUM 2 +static const char EP_STATUS_NAME [] = "ep3-bulk"; +#define EP_STATUS_NUM 3 +#define SELFPOWER USB_CONFIG_ATT_SELFPOWER +/* doesn't support remote wakeup */ + +#define hw_optimize(g) do {} while (0) #endif /*-------------------------------------------------------------------------*/ @@ -313,9 +314,32 @@ /*-------------------------------------------------------------------------*/ +#ifndef DEFAULT_QLEN +#define DEFAULT_QLEN 2 /* double buffering by default */ +#endif + +#ifdef HIGHSPEED + +static unsigned qmult = 5; +MODULE_PARM (qmult, "i"); + + +/* for dual-speed hardware, use deeper queues at highspeed */ +#define qlen(gadget) \ + (DEFAULT_QLEN*((gadget->speed == USB_SPEED_HIGH) ? qmult : 1)) + +/* also defer IRQs on highspeed TX */ +#define TX_DELAY DEFAULT_QLEN + +#else /* !HIGHSPEED ... full speed: */ +#define qlen(gadget) DEFAULT_QLEN +#endif + + +/*-------------------------------------------------------------------------*/ + #define xprintk(d,level,fmt,args...) \ - printk(level "%s %s: " fmt , shortname , (d)->gadget->dev.bus_id , \ - ## args) + printk(level "%s: " fmt , (d)->net->name , ## args) #ifdef DEBUG #undef DEBUG @@ -892,6 +916,8 @@ static void eth_reset_config (struct eth_dev *dev) { + struct usb_request *req; + if (dev->config == 0) return; @@ -900,17 +926,30 @@ netif_stop_queue (dev->net); netif_carrier_off (dev->net); - /* just disable endpoints, forcing completion of pending i/o. - * all our completion handlers free their requests in this case. + /* disable endpoints, forcing (synchronous) completion of + * pending i/o. then free the requests. */ if (dev->in_ep) { usb_ep_disable (dev->in_ep); + while (likely (!list_empty (&dev->tx_reqs))) { + req = container_of (dev->tx_reqs.next, + struct usb_request, list); + list_del (&req->list); + usb_ep_free_request (dev->in_ep, req); + } dev->in_ep = 0; } if (dev->out_ep) { usb_ep_disable (dev->out_ep); + while (likely (!list_empty (&dev->rx_reqs))) { + req = container_of (dev->rx_reqs.next, + struct usb_request, list); + list_del (&req->list); + usb_ep_free_request (dev->out_ep, req); + } dev->out_ep = 0; } + #ifdef EP_STATUS_NUM if (dev->status_ep) { usb_ep_disable (dev->status_ep); @@ -1676,7 +1715,7 @@ { struct eth_dev *dev = (struct eth_dev *) net->priv; - DEBUG (dev, "%s\n", __FUNCTION__); + VDEBUG (dev, "%s\n", __FUNCTION__); netif_stop_queue (net); DEBUG (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", @@ -1690,6 +1729,7 @@ usb_ep_disable (dev->out_ep); if (netif_carrier_ok (dev->net)) { DEBUG (dev, "host still using in/out endpoints\n"); + // FIXME idiom may leave toggle wrong here usb_ep_enable (dev->in_ep, dev->in); usb_ep_enable (dev->out_ep, dev->out); } @@ -1708,7 +1748,6 @@ eth_unbind (struct usb_gadget *gadget) { struct eth_dev *dev = get_gadget_data (gadget); - struct usb_request *req; DEBUG (dev, "unbind\n"); @@ -1721,19 +1760,6 @@ dev->req = 0; } - while (!list_empty (&dev->tx_reqs)) { - req = container_of (dev->tx_reqs.next, - struct usb_request, list); - list_del (&req->list); - usb_ep_free_request (dev->in_ep, req); - } - while (!list_empty (&dev->rx_reqs)) { - req = container_of (dev->rx_reqs.next, - struct usb_request, list); - list_del (&req->list); - usb_ep_free_request (dev->out_ep, req); - } - unregister_netdev (dev->net); dev_put (dev->net); @@ -1816,10 +1842,6 @@ dev->gadget = gadget; set_gadget_data (gadget, dev); gadget->ep0->driver_data = dev; - INFO (dev, "%s, " CHIP ", version: " DRIVER_VERSION "\n", driver_desc); -#ifdef DEV_CONFIG_CDC - INFO (dev, "CDC host enet %s\n", ethaddr); -#endif /* two kinds of host-initiated state changes: * - iff DATA transfer is active, carrier is "on" @@ -1830,8 +1852,16 @@ // SET_NETDEV_DEV (dev->net, &gadget->dev); status = register_netdev (dev->net); - if (status == 0) + if (status == 0) { + + INFO (dev, "%s, " CHIP ", version: " DRIVER_VERSION "\n", + driver_desc); +#ifdef DEV_CONFIG_CDC + INFO (dev, "CDC host enet %s\n", ethaddr); +#endif return status; + } + pr_debug("%s: register_netdev failed, %d\n", shortname, status); fail: eth_unbind (gadget); return status; diff -urN linux-2.4.22-bk41/drivers/usb/gadget/zero.c linux-2.4.22-bk42/drivers/usb/gadget/zero.c --- linux-2.4.22-bk41/drivers/usb/gadget/zero.c 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/gadget/zero.c 2003-10-26 02:49:55.000000000 -0800 @@ -63,8 +63,8 @@ // #define VERBOSE #include -#include #include +#include #include #include #include @@ -181,7 +181,7 @@ /* supports remote wakeup, but this driver doesn't */ /* no hw optimizations to apply */ -#define hw_optimize(g) do {} while (0); +#define hw_optimize(g) do {} while (0) #endif /* @@ -208,7 +208,26 @@ /* doesn't support remote wakeup? */ /* no hw optimizations to apply */ -#define hw_optimize(g) do {} while (0); +#define hw_optimize(g) do {} while (0) +#endif + +/* + * Toshiba TC86C001 ("Goku-S") UDC + * + * This has three semi-configurable full speed bulk/interrupt endpoints. + */ +#ifdef CONFIG_USB_ZERO_GOKU +#define CHIP "goku" +#define DRIVER_VERSION_NUM 0x0116 +#define EP0_MAXPACKET 8 +static const char EP_OUT_NAME [] = "ep1-bulk"; +#define EP_OUT_NUM 1 +static const char EP_IN_NAME [] = "ep2-bulk"; +#define EP_IN_NUM 2 +#define SELFPOWER USB_CONFIG_ATT_SELFPOWER +/* doesn't support remote wakeup */ + +#define hw_optimize(g) do {} while (0) #endif /*-------------------------------------------------------------------------*/ diff -urN linux-2.4.22-bk41/drivers/usb/hc_simple.c linux-2.4.22-bk42/drivers/usb/hc_simple.c --- linux-2.4.22-bk41/drivers/usb/hc_simple.c 2002-11-28 15:53:14.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/hc_simple.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,1072 +0,0 @@ -/*-------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------* - * simple generic USB HCD frontend Version 0.9.5 (10/28/2001) - * for embedded HCs (SL811HS) - * - * USB URB handling, hci_ hcs_ - * URB queueing, qu_ - * Transfer scheduling, sh_ - * - * - *-------------------------------------------------------------------------* - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - *-------------------------------------------------------------------------*/ - -/* main lock for urb access */ -static spinlock_t usb_urb_lock = SPIN_LOCK_UNLOCKED; - -/*-------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------*/ -/* URB HCD API function layer - * * * */ - -/*************************************************************************** - * Function Name : hcs_urb_queue - * - * This function initializes the urb status and length before queueing the - * urb. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * - * Return: 0 - **************************************************************************/ -static inline int hcs_urb_queue (hci_t * hci, struct urb * urb) -{ - int i; - - DBGFUNC ("enter hcs_urb_queue\n"); - if (usb_pipeisoc (urb->pipe)) { - DBGVERBOSE ("hcs_urb_queue: isoc pipe\n"); - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].actual_length = 0; - urb->iso_frame_desc[i].status = -EXDEV; - } - - /* urb->next hack : 1 .. resub, 0 .. single shot */ - /* urb->interval = urb->next ? 1 : 0; */ - } - - urb->status = -EINPROGRESS; - urb->actual_length = 0; - urb->error_count = 0; - - if (usb_pipecontrol (urb->pipe)) - hc_flush_data_cache (hci, urb->setup_packet, 8); - if (usb_pipeout (urb->pipe)) - hc_flush_data_cache (hci, urb->transfer_buffer, - urb->transfer_buffer_length); - - qu_queue_urb (hci, urb); - - return 0; -} - -/*************************************************************************** - * Function Name : hcs_return_urb - * - * This function the return path of URB back to the USB core. It calls the - * the urb complete function if exist, and also handles the resubmition of - * interrupt URBs. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * resub_ok = resubmit flag: 1 = submit urb again, 0 = not submit - * - * Return: 0 - **************************************************************************/ -static int hcs_return_urb (hci_t * hci, struct urb * urb, int resub_ok) -{ - struct usb_device *dev = urb->dev; - int resubmit = 0; - - DBGFUNC ("enter hcs_return_urb, urb pointer = 0x%x, " - "transferbuffer point = 0x%x, " - " setup packet pointer = 0x%x, context pointer = 0x%x \n", - (__u32 *) urb, (__u32 *) urb->transfer_buffer, - (__u32 *) urb->setup_packet, (__u32 *) urb->context); - if (urb_debug) - urb_print (urb, "RET", usb_pipeout (urb->pipe)); - - resubmit = urb->interval && resub_ok; - - urb->dev = urb->hcpriv = NULL; - - if (urb->complete) { - urb->complete (urb); /* call complete */ - } - - if (resubmit) { - /* requeue the URB */ - urb->dev = dev; - hcs_urb_queue (hci, urb); - } - - return 0; -} - -/*************************************************************************** - * Function Name : hci_submit_urb - * - * This function is called by the USB core API when an URB is available to - * process. This function does the following - * - * 1) Check the validity of the URB - * 2) Parse the device number from the URB - * 3) Pass the URB to the root hub routine if its intended for the hub, else - * queue the urb for the attached device. - * - * Input: urb = USB request block data structure - * - * Return: 0 if success or error code - **************************************************************************/ -static int hci_submit_urb (struct urb * urb) -{ - hci_t *hci; - unsigned int pipe = urb->pipe; - unsigned long flags; - int ret; - - DBGFUNC ("enter hci_submit_urb, pipe = 0x%x\n", urb->pipe); - 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 */ - - if (usb_pipedevice (pipe) == hci->rh.devnum) { - if (urb_debug > 1) - urb_print (urb, "SUB-RH", usb_pipein (pipe)); - - return rh_submit_urb (urb); - } - - /* queue the URB to its endpoint-queue */ - - spin_lock_irqsave (&usb_urb_lock, flags); - ret = hcs_urb_queue (hci, urb); - if (ret != 0) { - /* error on return */ - DBGERR - ("hci_submit_urb: return err, ret = 0x%x, urb->status = 0x%x\n", - ret, urb->status); - } - - spin_unlock_irqrestore (&usb_urb_lock, flags); - - return ret; - -} - -/*************************************************************************** - * Function Name : hci_unlink_urb - * - * This function mark the URB to unlink - * - * Input: urb = USB request block data structure - * - * Return: 0 if success or error code - **************************************************************************/ -static int hci_unlink_urb (struct urb * urb) -{ - unsigned long flags; - hci_t *hci; - DECLARE_WAITQUEUE (wait, current); - void *comp = NULL; - - DBGFUNC ("enter hci_unlink_urb\n"); - - if (!urb) /* just to be sure */ - return -EINVAL; - - if (!urb->dev || !urb->dev->bus) - return -ENODEV; - - hci = (hci_t *) urb->dev->bus->hcpriv; - - /* a request to the virtual root hub */ - if (usb_pipedevice (urb->pipe) == hci->rh.devnum) { - return rh_unlink_urb (urb); - } - - if (urb_debug) - urb_print (urb, "UNLINK", 1); - - spin_lock_irqsave (&usb_urb_lock, flags); - - if (!list_empty (&urb->urb_list) && urb->status == -EINPROGRESS) { - /* URB active? */ - - if (urb-> - transfer_flags & (USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED)) { - /* asynchron with callback */ - - list_del (&urb->urb_list); /* relink the urb to the del list */ - list_add (&urb->urb_list, &hci->del_list); - spin_unlock_irqrestore (&usb_urb_lock, flags); - - } else { - /* synchron without callback */ - - add_wait_queue (&hci->waitq, &wait); - - set_current_state (TASK_UNINTERRUPTIBLE); - comp = urb->complete; - urb->complete = NULL; - - list_del (&urb->urb_list); /* relink the urb to the del list */ - list_add (&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); - } - } 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 & USB_ASYNC_UNLINK)) { - urb->status = -ENOENT; - urb->actual_length = 0; - urb->complete (urb); - urb->status = 0; - } else { - urb->status = -ENOENT; - } - } - - return 0; -} - -/*************************************************************************** - * Function Name : hci_alloc_dev - * - * This function allocates private data space for the usb device and - * initialize the endpoint descriptor heads. - * - * Input: usb_dev = pointer to the usb device - * - * Return: 0 if success or error code - **************************************************************************/ -static int hci_alloc_dev (struct usb_device *usb_dev) -{ - struct hci_device *dev; - int i; - - DBGFUNC ("enter hci_alloc_dev\n"); - dev = kmalloc (sizeof (*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - memset (dev, 0, sizeof (*dev)); - - for (i = 0; i < 32; i++) { - INIT_LIST_HEAD (&(dev->ed[i].urb_queue)); - dev->ed[i].pipe_head = NULL; - } - - usb_dev->hcpriv = dev; - - DBGVERBOSE ("USB HC dev alloc %d bytes\n", sizeof (*dev)); - - return 0; - -} - -/*************************************************************************** - * Function Name : hci_free_dev - * - * This function de-allocates private data space for the usb devic - * - * Input: usb_dev = pointer to the usb device - * - * Return: 0 - **************************************************************************/ -static int hci_free_dev (struct usb_device *usb_dev) -{ - DBGFUNC ("enter hci_free_dev\n"); - - if (usb_dev->hcpriv) - kfree (usb_dev->hcpriv); - - usb_dev->hcpriv = NULL; - - return 0; -} - -/*************************************************************************** - * Function Name : hci_get_current_frame_number - * - * This function get the current USB frame number - * - * Input: usb_dev = pointer to the usb device - * - * Return: frame number - **************************************************************************/ -static int hci_get_current_frame_number (struct usb_device *usb_dev) -{ - hci_t *hci = usb_dev->bus->hcpriv; - DBGFUNC ("enter hci_get_current_frame_number, frame = 0x%x \r\n", - hci->frame_number); - - return (hci->frame_number); -} - -/*************************************************************************** - * List of all io-functions - **************************************************************************/ - -static struct usb_operations hci_device_operations = { - allocate: hci_alloc_dev, - deallocate: hci_free_dev, - get_frame_number: hci_get_current_frame_number, - submit_urb: hci_submit_urb, - unlink_urb: hci_unlink_urb, -}; - -/*************************************************************************** - * URB queueing: - * - * For each type of transfer (INTR, BULK, ISO, CTRL) there is a list of - * active URBs. - * (hci->intr_list, hci->bulk_list, hci->iso_list, hci->ctrl_list) - * For every endpoint the head URB of the queued URBs is linked to one of - * those lists. - * - * The rest of the queued URBs of an endpoint are linked into a - * private URB list for each endpoint. (hci_dev->ed [endpoint_io].urb_queue) - * hci_dev->ed [endpoint_io].pipe_head .. points to the head URB which is - * in one of the active URB lists. - * - * The index of an endpoint consists of its number and its direction. - * - * The state of an intr and iso URB is 0. - * For ctrl URBs the states are US_CTRL_SETUP, US_CTRL_DATA, US_CTRL_ACK - * Bulk URBs states are US_BULK and US_BULK0 (with 0-len packet) - * - **************************************************************************/ - -/*************************************************************************** - * Function Name : qu_urb_timeout - * - * This function is called when the URB timeout. The function unlinks the - * URB. - * - * Input: lurb: URB - * - * Return: none - **************************************************************************/ -#ifdef HC_URB_TIMEOUT -static void qu_urb_timeout (unsigned long lurb) -{ - struct urb *urb = (struct urb *) lurb; - - DBGFUNC ("enter qu_urb_timeout\n"); - urb->transfer_flags |= USB_TIMEOUT_KILLED; - hci_unlink_urb (urb); -} -#endif - -/*************************************************************************** - * Function Name : qu_pipeindex - * - * This function gets the index of the pipe. - * - * Input: pipe: the urb pipe - * - * Return: index - **************************************************************************/ -static inline int qu_pipeindex (__u32 pipe) -{ - DBGFUNC ("enter qu_pipeindex\n"); - return (usb_pipeendpoint (pipe) << 1) | (usb_pipecontrol (pipe) ? 0 : usb_pipeout (pipe)); -} - -/*************************************************************************** - * Function Name : qu_seturbstate - * - * This function set the state of the URB. - * - * control pipe: 3 states -- Setup, data, status - * interrupt and bulk pipe: 1 state -- data - * - * Input: urb = USB request block data structure - * state = the urb state - * - * Return: none - **************************************************************************/ -static inline void qu_seturbstate (struct urb * urb, int state) -{ - DBGFUNC ("enter qu_seturbstate\n"); - urb->pipe &= ~0x1f; - urb->pipe |= state & 0x1f; -} - -/*************************************************************************** - * Function Name : qu_urbstate - * - * This function get the current state of the URB. - * - * Input: urb = USB request block data structure - * - * Return: none - **************************************************************************/ -static inline int qu_urbstate (struct urb * urb) -{ - - DBGFUNC ("enter qu_urbstate\n"); - - return urb->pipe & 0x1f; -} - -/*************************************************************************** - * Function Name : qu_queue_active_urb - * - * This function adds the urb to the appropriate active urb list and set - * the urb state. - * - * There are four active lists: isochoronous list, interrupt list, - * control list, and bulk list. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * ed = endpoint descriptor - * - * Return: none - **************************************************************************/ -static inline void qu_queue_active_urb (hci_t * hci, struct urb * urb, epd_t * ed) -{ - int urb_state = 0; - DBGFUNC ("enter qu_queue_active_urb\n"); - switch (usb_pipetype (urb->pipe)) { - case PIPE_CONTROL: - list_add (&urb->urb_list, &hci->ctrl_list); - urb_state = US_CTRL_SETUP; - break; - - case PIPE_BULK: - list_add (&urb->urb_list, &hci->bulk_list); - if ((urb->transfer_flags & USB_ZERO_PACKET) - && urb->transfer_buffer_length > 0 - && - ((urb->transfer_buffer_length % - usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe))) == 0)) { - urb_state = US_BULK0; - } - break; - - case PIPE_INTERRUPT: - urb->start_frame = hci->frame_number; - list_add (&urb->urb_list, &hci->intr_list); - break; - - case PIPE_ISOCHRONOUS: - list_add (&urb->urb_list, &hci->iso_list); - break; - } - -#ifdef HC_URB_TIMEOUT - if (urb->timeout) { - ed->timeout.data = (unsigned long) urb; - ed->timeout.expires = urb->timeout + jiffies; - ed->timeout.function = qu_urb_timeout; - add_timer (&ed->timeout); - } -#endif - - qu_seturbstate (urb, urb_state); -} - -/*************************************************************************** - * Function Name : qu_queue_urb - * - * This function adds the urb to the endpoint descriptor list - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * - * Return: none - **************************************************************************/ -static int qu_queue_urb (hci_t * hci, struct urb * urb) -{ - struct hci_device *hci_dev = usb_to_hci (urb->dev); - epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)]; - - DBGFUNC ("Enter qu_queue_urb\n"); - - /* for ISOC transfers calculate start frame index */ - - if (usb_pipeisoc (urb->pipe) && urb->transfer_flags & USB_ISO_ASAP) { - urb->start_frame = ((ed->pipe_head) ? (ed->last_iso + 1) : hci_get_current_frame_number (urb-> dev) + 1) & 0xffff; - } - - if (ed->pipe_head) { - __list_add (&urb->urb_list, ed->urb_queue.prev, - &(ed->urb_queue)); - } else { - ed->pipe_head = urb; - qu_queue_active_urb (hci, urb, ed); - if (++hci->active_urbs == 1) - hc_start_int (hci); - } - - return 0; -} - -/*************************************************************************** - * Function Name : qu_next_urb - * - * This function removes the URB from the queue and add the next URB to - * active list. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * resub_ok = resubmit flag - * - * Return: pointer to the next urb - **************************************************************************/ -static struct urb *qu_next_urb (hci_t * hci, struct urb * urb, int resub_ok) -{ - struct hci_device *hci_dev = usb_to_hci (urb->dev); - epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)]; - - DBGFUNC ("enter qu_next_urb\n"); - list_del (&urb->urb_list); - INIT_LIST_HEAD (&urb->urb_list); - if (ed->pipe_head == urb) { - -#ifdef HC_URB_TIMEOUT - if (urb->timeout) - del_timer (&ed->timeout); -#endif - - if (!--hci->active_urbs) - hc_stop_int (hci); - - if (!list_empty (&ed->urb_queue)) { - urb = list_entry (ed->urb_queue.next, struct urb, urb_list); - list_del (&urb->urb_list); - INIT_LIST_HEAD (&urb->urb_list); - ed->pipe_head = urb; - qu_queue_active_urb (hci, urb, ed); - } else { - ed->pipe_head = NULL; - urb = NULL; - } - } - return urb; -} - -/*************************************************************************** - * Function Name : qu_return_urb - * - * This function is part of the return path. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * resub_ok = resubmit flag - * - * Return: pointer to the next urb - **************************************************************************/ -static struct urb *qu_return_urb (hci_t * hci, struct urb * urb, int resub_ok) -{ - struct urb *next_urb; - - DBGFUNC ("enter qu_return_rub\n"); - next_urb = qu_next_urb (hci, urb, resub_ok); - hcs_return_urb (hci, urb, resub_ok); - return next_urb; -} - -/*************************************************************************** - * Function Name : sh_scan_iso_urb_list - * - * This function goes throught the isochronous urb list and schedule the - * the transfer. - * - * Note: This function has not tested yet - * - * Input: hci = data structure for the host controller - * list_lh = pointer to the isochronous list - * frame_number = the frame number - * - * Return: 0 = unsuccessful; 1 = successful - **************************************************************************/ -static int sh_scan_iso_urb_list (hci_t * hci, struct list_head *list_lh, - int frame_number) -{ - struct list_head *lh = list_lh->next; - struct urb *urb; - - DBGFUNC ("enter sh_scan_iso_urb_list\n"); - hci->td_array->len = 0; - - while (lh != list_lh) { - urb = list_entry (lh, struct urb, urb_list); - lh = lh->next; - if (((frame_number - urb->start_frame) & 0x7ff) < - urb->number_of_packets) { - if (!sh_add_packet (hci, urb)) { - return 0; - } else { - if (((frame_number - - urb->start_frame) & 0x7ff) > 0x400) { - if (qu_urbstate (urb) > 0) - urb = qu_return_urb (hci, urb, 1); - else - urb = qu_next_urb (hci, urb, 1); - - if (lh == list_lh && urb) - lh = &urb->urb_list; - } - } - } - } - return 1; -} - -/*************************************************************************** - * Function Name : sh_scan_urb_list - * - * This function goes through the urb list and schedule the - * the transaction. - * - * Input: hci = data structure for the host controller - * list_lh = pointer to the isochronous list - * - * Return: 0 = unsuccessful; 1 = successful - **************************************************************************/ -static int sh_scan_urb_list (hci_t * hci, struct list_head *list_lh) -{ - struct list_head *lh = NULL; - struct urb *urb; - - if (list_lh == NULL) { - DBGERR ("sh_scan_urb_list: error, list_lh == NULL\n"); - } - - DBGFUNC ("enter sh_scan_urb_list: frame# \n"); - - list_for_each (lh, list_lh) { - urb = list_entry (lh, struct urb, urb_list); - if (urb == NULL) - return 1; - if (!usb_pipeint (urb->pipe) - || (((hci->frame_number - urb->start_frame) - & 0x7ff) >= urb->interval)) { - DBGVERBOSE ("sh_scan_urb_list !INT: %d fr_no: %d int: %d pint: %d\n", - urb->start_frame, hci->frame_number, urb->interval, - usb_pipeint (urb->pipe)); - if (!sh_add_packet (hci, urb)) { - return 0; - } else { - DBGVERBOSE ("INT: start: %d fr_no: %d int: %d pint: %d\n", - urb->start_frame, hci->frame_number, - urb->interval, usb_pipeint (urb->pipe)); - urb->start_frame = hci->frame_number; - return 0; - - } - } - } - return 1; -} - -/*************************************************************************** - * Function Name : sh_shedule_trans - * - * This function schedule the USB transaction. - * This function will process the endpoint in the following order: - * interrupt, control, and bulk. - * - * Input: hci = data structure for the host controller - * isSOF = flag indicate if Start Of Frame has occurred - * - * Return: 0 - **************************************************************************/ -static int sh_schedule_trans (hci_t * hci, int isSOF) -{ - int units_left = 1; - struct list_head *lh; - - if (hci == NULL) { - DBGERR ("sh_schedule_trans: hci == NULL\n"); - return 0; - } - if (hci->td_array == NULL) { - DBGERR ("sh_schedule_trans: hci->td_array == NULL\n"); - return 0; - } - - if (hci->td_array->len != 0) { - DBGERR ("ERROR: schedule, hci->td_array->len = 0x%x, s/b: 0\n", - hci->td_array->len); - } - - /* schedule the next available interrupt transfer or the next - * stage of the interrupt transfer */ - - if (hci->td_array->len == 0 && !list_empty (&hci->intr_list)) { - units_left = sh_scan_urb_list (hci, &hci->intr_list); - } - - /* schedule the next available control transfer or the next - * stage of the control transfer */ - - if (hci->td_array->len == 0 && !list_empty (&hci->ctrl_list) && units_left > 0) { - units_left = sh_scan_urb_list (hci, &hci->ctrl_list); - } - - /* schedule the next available bulk transfer or the next - * stage of the bulk transfer */ - - if (hci->td_array->len == 0 && !list_empty (&hci->bulk_list) && units_left > 0) { - sh_scan_urb_list (hci, &hci->bulk_list); - - /* be fair to each BULK URB (move list head around) - * only when the new SOF happens */ - - lh = hci->bulk_list.next; - list_del (&hci->bulk_list); - list_add (&hci->bulk_list, lh); - } - return 0; -} - -/*************************************************************************** - * Function Name : sh_add_packet - * - * This function forms the packet and transmit the packet. This function - * will handle all endpoint type: isochoronus, interrupt, control, and - * bulk. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * - * Return: 0 = unsucessful; 1 = successful - **************************************************************************/ -static int sh_add_packet (hci_t * hci, struct urb * urb) -{ - __u8 *data = NULL; - int len = 0; - int toggle = 0; - int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); - int endpoint = usb_pipeendpoint (urb->pipe); - int address = usb_pipedevice (urb->pipe); - int slow = (((urb->pipe) >> 26) & 1); - int out = usb_pipeout (urb->pipe); - int pid = 0; - int ret; - int i = 0; - int iso = 0; - - DBGFUNC ("enter sh_add_packet\n"); - if (maxps == 0) - maxps = 8; - - /* calculate len, toggle bit and add the transaction */ - switch (usb_pipetype (urb->pipe)) { - case PIPE_ISOCHRONOUS: - pid = out ? PID_OUT : PID_IN; - iso = 1; - i = hci->frame_number - urb->start_frame; - data = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - len = urb->iso_frame_desc[i].length; - break; - - case PIPE_BULK: /* BULK and BULK0 */ - case PIPE_INTERRUPT: - pid = out ? PID_OUT : PID_IN; - len = urb->transfer_buffer_length - urb->actual_length; - data = urb->transfer_buffer + urb->actual_length; - toggle = usb_gettoggle (urb->dev, endpoint, out); - break; - - case PIPE_CONTROL: - switch (qu_urbstate (urb)) { - case US_CTRL_SETUP: - len = 8; - pid = PID_SETUP; - data = urb->setup_packet; - toggle = 0; - break; - - case US_CTRL_DATA: - if (!hci->last_packet_nak) { - /* The last packet received is not a nak: - * reset the nak count - */ - - hci->nakCnt = 0; - } - if (urb->transfer_buffer_length != 0) { - pid = out ? PID_OUT : PID_IN; - len = urb->transfer_buffer_length - urb->actual_length; - data = urb->transfer_buffer + urb->actual_length; - toggle = (urb->actual_length & maxps) ? 0 : 1; - usb_settoggle (urb->dev, - usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe), toggle); - break; - } else { - /* correct state and fall through */ - qu_seturbstate (urb, US_CTRL_ACK); - } - - case US_CTRL_ACK: - len = 0; - - /* reply in opposite direction */ - pid = !out ? PID_OUT : PID_IN; - toggle = 1; - usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe), toggle); - break; - } - } - - ret = - hc_add_trans (hci, len, data, toggle, maxps, slow, endpoint, - address, pid, iso, qu_urbstate (urb)); - - DBGVERBOSE ("transfer_pa: addr:%d ep:%d pid:%x tog:%x iso:%x sl:%x " - "max:%d\n len:%d ret:%d data:%p left:%d\n", - address, endpoint, pid, toggle, iso, slow, - maxps, len, ret, data, hci->hp.units_left); - - if (ret >= 0) { - hci->td_array->td[hci->td_array->len].urb = urb; - hci->td_array->td[hci->td_array->len].len = ret; - hci->td_array->td[hci->td_array->len].iso_index = i; - hci->td_array->len++; - hci->active_trans = 1; - return 1; - } - return 0; -} - -/*************************************************************************** - * Function Name : cc_to_error - * - * This function maps the SL811HS hardware error code to the linux USB error - * code. - * - * Input: cc = hardware error code - * - * Return: USB error code - **************************************************************************/ -static int cc_to_error (int cc) -{ - int errCode = 0; - if (cc & SL11H_STATMASK_ERROR) { - errCode |= -EILSEQ; - } else if (cc & SL11H_STATMASK_OVF) { - errCode |= -EOVERFLOW; - } else if (cc & SL11H_STATMASK_STALL) { - errCode |= -EPIPE; - } - return errCode; -} - -/*************************************************************************** - * Function Name : sh_done_list - * - * This function process the packet when it has done finish transfer. - * - * 1) It handles hardware error - * 2) It updates the URB state - * 3) If the USB transaction is complete, it start the return stack path. - * - * Input: hci = data structure for the host controller - * isExcessNak = flag tells if there excess NAK condition occurred - * - * Return: urb_state or -1 if the transaction has complete - **************************************************************************/ -static int sh_done_list (hci_t * hci, int *isExcessNak) -{ - int actbytes = 0; - int active = 0; - void *data = NULL; - int cc; - int maxps; - int toggle; - struct urb *urb; - int urb_state = 0; - int ret = 1; /* -1 parse abbort, 1 parse ok, 0 last element */ - int trans = 0; - int len; - int iso_index = 0; - int out; - int pid = 0; - int debugLen = 0; - - *isExcessNak = 0; - - DBGFUNC ("enter sh_done_list: td_array->len = 0x%x\n", - hci->td_array->len); - - debugLen = hci->td_array->len; - if (debugLen > 1) - DBGERR ("sh_done_list: td_array->len = 0x%x > 1\n", - hci->td_array->len); - - for (trans = 0; ret && trans < hci->td_array->len && trans < MAX_TRANS; - trans++) { - urb = hci->td_array->td[trans].urb; - len = hci->td_array->td[trans].len; - out = usb_pipeout (urb->pipe); - - if (usb_pipeisoc (urb->pipe)) { - iso_index = hci->td_array->td[trans].iso_index; - data = urb->transfer_buffer + urb->iso_frame_desc[iso_index].offset; - toggle = 0; - } else { - data = urb->transfer_buffer + urb->actual_length; - toggle = usb_gettoggle (urb->dev, - usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe)); - - } - urb_state = qu_urbstate (urb); - pid = out ? PID_OUT : PID_IN; - ret = hc_parse_trans (hci, &actbytes, data, &cc, &toggle, len, - pid, urb_state); - maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); - - if (maxps == 0) - maxps = 8; - - active = (urb_state != US_CTRL_SETUP) && (actbytes && !(actbytes & (maxps - 1))); - - /* If the transfer is not bulk in, then it is necessary to get all - * data specify by the urb->transfer_len. - */ - - if (!(usb_pipebulk (urb->pipe) && usb_pipein (urb->pipe))) - active = active && (urb->transfer_buffer_length != urb->actual_length + actbytes); - - if (urb->transfer_buffer_length == urb->actual_length + actbytes) - active = 0; - - if ((cc & - (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | - SL11H_STATMASK_OVF | SL11H_STATMASK_STALL)) - && !(cc & SL11H_STATMASK_NAK)) { - if (++urb->error_count > 3) { - DBGERR ("done_list: excessive error: errcount = 0x%x, cc = 0x%x\n", - urb->error_count, cc); - urb_state = 0; - active = 0; - } else { - DBGERR ("done_list: packet err, cc = 0x%x, " - " urb->length = 0x%x, actual_len = 0x%x," - " urb_state =0x%x\n", - cc, urb->transfer_buffer_length, - urb->actual_length, urb_state); -// if (cc & SL11H_STATMASK_STALL) { - /* The USB function is STALLED on a control pipe (0), - * then it needs to send the SETUP command again to - * clear the STALL condition - */ - -// if (usb_pipeendpoint (urb->pipe) == 0) { -// urb_state = 2; -// active = 0; -// } -// } else - active = 1; - } - } else { - if (cc & SL11H_STATMASK_NAK) { - if (hci->nakCnt < 0x10000) { - hci->nakCnt++; - hci->last_packet_nak = 1; - active = 1; - *isExcessNak = 0; - } else { - DBGERR ("done_list: nak count exceed limit\n"); - active = 0; - *isExcessNak = 1; - hci->nakCnt = 0; - } - } else { - hci->nakCnt = 0; - hci->last_packet_nak = 0; - } - - if (urb_state != US_CTRL_SETUP) { - /* no error */ - urb->actual_length += actbytes; - usb_settoggle (urb->dev, - usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe), toggle); - } - if (usb_pipeisoc (urb->pipe)) { - urb->iso_frame_desc[iso_index].actual_length = actbytes; - urb->iso_frame_desc[iso_index].status = cc_to_error (cc); - active = (iso_index < urb->number_of_packets); - } - } - if (!active) { - if (!urb_state) { - urb->status = cc_to_error (cc); - if (urb->status) { - DBGERR ("error on received packet: urb->status = 0x%x\n", - urb->status); - } - hci->td_array->len = 0; - qu_return_urb (hci, urb, 1); - return -1; - } else { - /* We do not want to decrement the urb_state if exceeded nak, - * because we need to finish the data stage of the control - * packet - */ - - if (!(*isExcessNak)) - urb_state--; - qu_seturbstate (urb, urb_state); - } - } - } - - if (urb_state < 0) - DBGERR ("ERROR: done_list, urb_state = %d, suppose > 0\n", - urb_state); - if (debugLen != hci->td_array->len) { - DBGERR ("ERROR: done_list, debugLen!= td_array->len," - "debugLen = 0x%x, hci->td_array->len = 0x%x\n", - debugLen, hci->td_array->len); - } - - hci->td_array->len = 0; - - return urb_state; -} diff -urN linux-2.4.22-bk41/drivers/usb/hc_simple.h linux-2.4.22-bk42/drivers/usb/hc_simple.h --- linux-2.4.22-bk41/drivers/usb/hc_simple.h 2002-11-28 15:53:14.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/hc_simple.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,231 +0,0 @@ -/*-------------------------------------------------------------------------*/ -/* list of all controllers using this driver - * */ - -static LIST_HEAD (hci_hcd_list); - -/* URB states (urb_state) */ -/* isoc, interrupt single state */ - -/* bulk transfer main state and 0-length packet */ -#define US_BULK 0 -#define US_BULK0 1 -/* three setup states */ -#define US_CTRL_SETUP 2 -#define US_CTRL_DATA 1 -#define US_CTRL_ACK 0 - -/*-------------------------------------------------------------------------*/ -/* HC private part of a device descriptor - * */ - -#define NUM_EDS 32 - -typedef struct epd { - struct urb *pipe_head; - struct list_head urb_queue; -// int urb_state; - struct timer_list timeout; - int last_iso; /* timestamp of last queued ISOC transfer */ - -} epd_t; - -struct hci_device { - epd_t ed[NUM_EDS]; -}; - -/*-------------------------------------------------------------------------*/ -/* Virtual Root HUB - */ - -#define usb_to_hci(usb) ((struct hci_device *)(usb)->hcpriv) - -struct virt_root_hub { - int devnum; /* Address of Root Hub endpoint */ - void *urb; /* interrupt URB of root hub */ - int send; /* active flag */ - int interval; /* intervall of roothub interrupt transfers */ - struct timer_list rh_int_timer; /* intervall timer for rh interrupt EP */ -}; - -#if 1 -/* USB HUB CONSTANTS (not OHCI-specific; see hub.h and USB spec) */ - -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 - -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -#endif - -/*-------------------------------------------------------------------------*/ -/* struct for each HC - */ - -#define MAX_TRANS 32 - -typedef struct td { - struct urb *urb; - __u16 len; - __u16 iso_index; -} td_t; - -typedef struct td_array { - int len; - td_t td[MAX_TRANS]; -} td_array_t; - -typedef struct hci { - struct virt_root_hub rh; /* roothub */ - wait_queue_head_t waitq; /* deletion of URBs and devices needs a waitqueue */ - int active; /* HC is operating */ - - struct list_head ctrl_list; /* set of ctrl endpoints */ - struct list_head bulk_list; /* set of bulk endpoints */ - struct list_head iso_list; /* set of isoc endpoints */ - struct list_head intr_list; /* ordered (tree) set of int endpoints */ - struct list_head del_list; /* set of entpoints to be deleted */ - - td_array_t *td_array; - td_array_t a_td_array; - td_array_t i_td_array[2]; - - struct list_head hci_hcd_list; /* list of all hci_hcd */ - struct usb_bus *bus; /* our bus */ - -// int trans; /* number of transactions pending */ - int active_urbs; - int active_trans; - int frame_number; /* frame number */ - hcipriv_t hp; /* individual part of hc type */ - int nakCnt; - int last_packet_nak; - -} hci_t; - -/*-------------------------------------------------------------------------*/ -/* condition (error) CC codes and mapping OHCI like - */ - -#define TD_CC_NOERROR 0x00 -#define TD_CC_CRC 0x01 -#define TD_CC_BITSTUFFING 0x02 -#define TD_CC_DATATOGGLEM 0x03 -#define TD_CC_STALL 0x04 -#define TD_DEVNOTRESP 0x05 -#define TD_PIDCHECKFAIL 0x06 -#define TD_UNEXPECTEDPID 0x07 -#define TD_DATAOVERRUN 0x08 -#define TD_DATAUNDERRUN 0x09 -#define TD_BUFFEROVERRUN 0x0C -#define TD_BUFFERUNDERRUN 0x0D -#define TD_NOTACCESSED 0x0F - - -/* urb interface functions */ -static int hci_get_current_frame_number (struct usb_device *usb_dev); -static int hci_unlink_urb (struct urb * urb); - -static int qu_queue_urb (hci_t * hci, struct urb * urb); - -/* root hub */ -static int rh_init_int_timer (struct urb * urb); -static int rh_submit_urb (struct urb * urb); -static int rh_unlink_urb (struct urb * urb); - -/* schedule functions */ -static int sh_add_packet (hci_t * hci, struct urb * urb); - -/* hc specific functions */ -static inline void hc_flush_data_cache (hci_t * hci, void *data, int len); -static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data, - int *cc, int *toggle, int length, int pid, - int urb_state); -static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle, - int maxps, int slow, int endpoint, int address, - int pid, int format, int urb_state); - -static void hc_start_int (hci_t * hci); -static void hc_stop_int (hci_t * hci); -static void SL811Write (hci_t * hci, char offset, char data); - -/* debug| print the main components of an URB - * small: 0) header + data packets 1) just header */ - -static void urb_print (struct urb * urb, char *str, int small) -{ - unsigned int pipe = urb->pipe; - int i, len; - - if (!urb->dev || !urb->dev->bus) { - dbg ("%s URB: no dev", str); - return; - } - - printk ("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)\n", - str, hci_get_current_frame_number (urb->dev), - usb_pipedevice (pipe), usb_pipeendpoint (pipe), - usb_pipeout (pipe) ? 'O' : 'I', - usb_pipetype (pipe) < 2 ? (usb_pipeint (pipe) ? "INTR" : "ISOC") - : (usb_pipecontrol (pipe) ? "CTRL" : "BULK"), urb->transfer_flags, - urb->actual_length, urb->transfer_buffer_length, urb->status, - urb->status); - if (!small) { - if (usb_pipecontrol (pipe)) { - printk (__FILE__ ": cmd(8):"); - for (i = 0; i < 8; i++) - printk (" %02x", ((__u8 *) urb->setup_packet)[i]); - printk ("\n"); - } - if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { - printk (__FILE__ ": data(%d/%d):", urb->actual_length, - urb->transfer_buffer_length); - len = usb_pipeout (pipe) ? urb-> transfer_buffer_length : urb->actual_length; - for (i = 0; i < 2096 && i < len; i++) - printk (" %02x", ((__u8 *) urb->transfer_buffer)[i]); - printk ("%s stat:%d\n", i < len ? "..." : "", - urb->status); - } - } -} diff -urN linux-2.4.22-bk41/drivers/usb/hc_sl811.c linux-2.4.22-bk42/drivers/usb/hc_sl811.c --- linux-2.4.22-bk41/drivers/usb/hc_sl811.c 2003-06-13 07:51:36.000000000 -0700 +++ linux-2.4.22-bk42/drivers/usb/hc_sl811.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,1359 +0,0 @@ -/*-------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------* - * SL811HS USB HCD for Linux Version 0.1 (10/28/2001) - * - * requires (includes) hc_simple.[hc] simple generic HCD frontend - * - * COPYRIGHT(C) 2001 by CYPRESS SEMICONDUCTOR INC. - * - *-------------------------------------------------------------------------* - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - *-------------------------------------------------------------------------*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "../core/hcd.h" - -#undef HC_URB_TIMEOUT -#undef HC_SWITCH_INT -#undef HC_ENABLE_ISOC - -#define SL811_DEBUG_ERR - -#ifdef SL811_DEBUG_ERR -#define DBGERR(fmt, args...) printk(fmt,## args) -#else -#define DBGERR(fmt, args...) -#endif - -#ifdef SL811_DEBUG -#define DBG(fmt, args...) printk(fmt,## args) -#else -#define DBG(fmt, args...) -#endif - -#ifdef SL811_DEBUG_FUNC -#define DBGFUNC(fmt, args...) printk(fmt,## args) -#else -#define DBGFUNC(fmt, args...) -#endif - -#ifdef SL811_DEBUG_DATA -#define DBGDATAR(fmt, args...) printk(fmt,## args) -#define DBGDATAW(fmt, args...) printk(fmt,## args) -#else -#define DBGDATAR(fmt, args...) -#define DBGDATAW(fmt, args...) -#endif - -#ifdef SL811_DEBUG_VERBOSE -#define DBGVERBOSE(fmt, args...) printk(fmt,## args) -#else -#define DBGVERBOSE(fmt, args...) -#endif - -#define TRUE 1 -#define FALSE 0 - -#define HC_SWITCH_INT -#include "hc_sl811.h" -#include "hc_simple.h" - -static int urb_debug = 0; - -#include "hc_simple.c" -#include "hc_sl811_rh.c" - -/* The base_addr, data_reg_addr, and irq number are board specific. - * The current values are design to run on the Accelent SA1110 IDP - * NOTE: values need to modify for different development boards - */ - -static int base_addr = 0xd3800000; -static int data_reg_addr = 0xd3810000; -static int irq = 34; - -/* forware declaration */ - -int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len, - int toggle, int slow, int urb_state); - -static int sofWaitCnt = 0; - -MODULE_PARM (urb_debug, "i"); -MODULE_PARM_DESC (urb_debug, "debug urb messages, default is 0 (no)"); - -MODULE_PARM (base_addr, "i"); -MODULE_PARM_DESC (base_addr, "sl811 base address 0xd3800000"); -MODULE_PARM (data_reg_addr, "i"); -MODULE_PARM_DESC (data_reg_addr, "sl811 data register address 0xd3810000"); -MODULE_PARM (irq, "i"); -MODULE_PARM_DESC (irq, "IRQ 34 (default)"); - -static int hc_reset (hci_t * hci); - -/*************************************************************************** - * Function Name : SL811Read - * - * Read a byte of data from the SL811H/SL11H - * - * Input: hci = data structure for the host controller - * offset = address of SL811/SL11H register or memory - * - * Return: data - **************************************************************************/ -char SL811Read (hci_t * hci, char offset) -{ - hcipriv_t *hp = &hci->hp; - char data; - writeb (offset, hp->hcport); - wmb (); - data = readb (hp->hcport2); - rmb (); - return (data); -} - -/*************************************************************************** - * Function Name : SL811Write - * - * Write a byte of data to the SL811H/SL11H - * - * Input: hci = data structure for the host controller - * offset = address of SL811/SL11H register or memory - * data = the data going to write to SL811H - * - * Return: none - **************************************************************************/ -void SL811Write (hci_t * hci, char offset, char data) -{ - hcipriv_t *hp = &hci->hp; - writeb (offset, hp->hcport); - writeb (data, hp->hcport2); - wmb (); -} - -/*************************************************************************** - * Function Name : SL811BufRead - * - * Read consecutive bytes of data from the SL811H/SL11H buffer - * - * Input: hci = data structure for the host controller - * offset = SL811/SL11H register offset - * buf = the buffer where the data will store - * size = number of bytes to read - * - * Return: none - **************************************************************************/ -void SL811BufRead (hci_t * hci, short offset, char *buf, short size) -{ - hcipriv_t *hp = &hci->hp; - if (size <= 0) - return; - writeb ((char) offset, hp->hcport); - wmb (); - DBGDATAR ("SL811BufRead: offset = 0x%x, data = ", offset); - while (size--) { - *buf++ = (char) readb (hp->hcport2); - DBGDATAR ("0x%x ", *(buf - 1)); - rmb (); - } - DBGDATAR ("\n"); -} - -/*************************************************************************** - * Function Name : SL811BufWrite - * - * Write consecutive bytes of data to the SL811H/SL11H buffer - * - * Input: hci = data structure for the host controller - * offset = SL811/SL11H register offset - * buf = the data buffer - * size = number of bytes to write - * - * Return: none - **************************************************************************/ -void SL811BufWrite (hci_t * hci, short offset, char *buf, short size) -{ - hcipriv_t *hp = &hci->hp; - if (size <= 0) - return; - writeb ((char) offset, hp->hcport); - wmb (); - DBGDATAW ("SL811BufWrite: offset = 0x%x, data = ", offset); - while (size--) { - DBGDATAW ("0x%x ", *buf); - writeb (*buf, hp->hcport2); - wmb (); - buf++; - } - DBGDATAW ("\n"); -} - -/*************************************************************************** - * Function Name : regTest - * - * This routine test the Read/Write functionality of SL811HS registers - * - * 1) Store original register value into a buffer - * 2) Write to registers with a RAMP pattern. (10, 11, 12, ..., 255) - * 3) Read from register - * 4) Compare the written value with the read value and make sure they are - * equivalent - * 5) Restore the original register value - * - * Input: hci = data structure for the host controller - * - * - * Return: TRUE = passed; FALSE = failed - **************************************************************************/ -int regTest (hci_t * hci) -{ - int i, data, result = TRUE; - char buf[256]; - - DBGFUNC ("Enter regTest\n"); - for (i = 0x10; i < 256; i++) { - /* save the original buffer */ - buf[i] = (char) SL811Read (hci, i); - - /* Write the new data to the buffer */ - SL811Write (hci, i, i); - } - - /* compare the written data */ - for (i = 0x10; i < 256; i++) { - data = SL811Read (hci, i); - if (data != i) { - DBGERR ("Pattern test failed!! value = 0x%x, s/b 0x%x\n", - data, i); - result = FALSE; - } - } - - /* restore the data */ - for (i = 0x10; i < 256; i++) { - SL811Write (hci, i, buf[i]); - } - - return (result); -} - -/*************************************************************************** - * Function Name : regShow - * - * Display all SL811HS register values - * - * Input: hci = data structure for the host controller - * - * Return: none - **************************************************************************/ -void regShow (hci_t * hci) -{ - int i; - for (i = 0; i < 256; i++) { - printk ("offset %d: 0x%x\n", i, SL811Read (hci, i)); - } -} - -/************************************************************************ - * Function Name : USBReset - * - * This function resets SL811HS controller and detects the speed of - * the connecting device - * - * Input: hci = data structure for the host controller - * - * Return: 0 = no device attached; 1 = USB device attached - * - ***********************************************************************/ -static int USBReset (hci_t * hci) -{ - int status; - hcipriv_t *hp = &hci->hp; - - DBGFUNC ("enter USBReset\n"); - - SL811Write (hci, SL11H_CTLREG2, 0xae); - - // setup master and full speed - - SL811Write (hci, SL11H_CTLREG1, 0x08); // reset USB - mdelay (20); // 20ms - SL811Write (hci, SL11H_CTLREG1, 0); // remove SE0 - - for (status = 0; status < 100; status++) - SL811Write (hci, SL11H_INTSTATREG, 0xff); // clear all interrupt bits - - status = SL811Read (hci, SL11H_INTSTATREG); - - if (status & 0x40) // Check if device is removed - { - DBG ("USBReset: Device removed\n"); - SL811Write (hci, SL11H_INTENBLREG, - SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | - SL11H_INTMASK_INSRMV); - hp->RHportStatus->portStatus &= - ~(PORT_CONNECT_STAT | PORT_ENABLE_STAT); - - return 0; - } - - SL811Write (hci, SL11H_BUFLNTHREG_B, 0); //zero lenth - SL811Write (hci, SL11H_PIDEPREG_B, 0x50); //send SOF to EP0 - SL811Write (hci, SL11H_DEVADDRREG_B, 0x01); //address0 - SL811Write (hci, SL11H_SOFLOWREG, 0xe0); - - if (!(status & 0x80)) { - /* slow speed device connect directly to root-hub */ - - DBG ("USBReset: low speed Device attached\n"); - SL811Write (hci, SL11H_CTLREG1, 0x8); - mdelay (20); - SL811Write (hci, SL11H_SOFTMRREG, 0xee); - SL811Write (hci, SL11H_CTLREG1, 0x21); - - /* start the SOF or EOP */ - - SL811Write (hci, SL11H_HOSTCTLREG_B, 0x01); - hp->RHportStatus->portStatus |= - (PORT_CONNECT_STAT | PORT_LOW_SPEED_DEV_ATTACH_STAT); - - /* clear all interrupt bits */ - - for (status = 0; status < 20; status++) - SL811Write (hci, SL11H_INTSTATREG, 0xff); - } else { - /* full speed device connect directly to root hub */ - - DBG ("USBReset: full speed Device attached\n"); - SL811Write (hci, SL11H_CTLREG1, 0x8); - mdelay (20); - SL811Write (hci, SL11H_SOFTMRREG, 0xae); - SL811Write (hci, SL11H_CTLREG1, 0x01); - - /* start the SOF or EOP */ - - SL811Write (hci, SL11H_HOSTCTLREG_B, 0x01); - hp->RHportStatus->portStatus |= (PORT_CONNECT_STAT); - hp->RHportStatus->portStatus &= ~PORT_LOW_SPEED_DEV_ATTACH_STAT; - - /* clear all interrupt bits */ - - SL811Write (hci, SL11H_INTSTATREG, 0xff); - - } - - /* enable all interrupts */ - SL811Write (hci, SL11H_INTENBLREG, - SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | - SL11H_INTMASK_INSRMV); - - return 1; -} - -/*-------------------------------------------------------------------------*/ -/* tl functions */ -static inline void hc_mark_last_trans (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - __u8 *ptd = hp->tl; - - dbg ("enter hc_mark_last_trans\n"); - if (ptd == NULL) { - printk ("hc_mark_last_trans: ptd = null\n"); - return; - } - if (hp->xferPktLen > 0) - *(ptd + hp->tl_last) |= (1 << 3); -} - -static inline void hc_flush_data_cache (hci_t * hci, void *data, int len) -{ -} - -/************************************************************************ - * Function Name : hc_add_trans - * - * This function sets up the SL811HS register and transmit the USB packets. - * - * 1) Determine if enough time within the current frame to send the packet - * 2) Load the data into the SL811HS register - * 3) Set the appropriate command to the register and trigger the transmit - * - * Input: hci = data structure for the host controller - * len = data length - * data = transmitting data - * toggle = USB toggle bit, either 0 or 1 - * maxps = maximum packet size for this endpoint - * slow = speed of the device - * endpoint = endpoint number - * address = USB address of the device - * pid = packet ID - * format = - * urb_state = the current stage of USB transaction - * - * Return: 0 = no time left to schedule the transfer - * 1 = success - * - ***********************************************************************/ -static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle, - int maxps, int slow, int endpoint, int address, - int pid, int format, int urb_state) -{ - hcipriv_t *hp = &hci->hp; - __u16 speed; - int ii, jj, kk; - - DBGFUNC ("enter hc_addr_trans: len =0x%x, toggle:0x%x, endpoing:0x%x," - " addr:0x%x, pid:0x%x,format:0x%x\n", len, toggle, endpoint, - i address, pid, format); - - if (len > maxps) { - len = maxps; - } - - speed = hp->RHportStatus->portStatus; - if (speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) { -// ii = (8*7*8 + 6*3) * len + 800; - ii = 8 * 8 * len + 1024; - } else { - if (slow) { -// ii = (8*7*8 + 6*3) * len + 800; - ii = 8 * 8 * len + 2048; - } else -// ii = (8*7 + 6*3)*len + 110; - ii = 8 * len + 256; - } - - ii += 2 * 10 * len; - - jj = SL811Read (hci, SL11H_SOFTMRREG); - kk = (jj & 0xFF) * 64 - ii; - - if (kk < 0) { - DBGVERBOSE - ("hc_add_trans: no bandwidth for schedule, ii = 0x%x," - "jj = 0x%x, len =0x%x, active_trans = 0x%x\n", ii, jj, len, - hci->active_trans); - return (-1); - } - - if (pid != PID_IN) { - /* Load data into hc */ - - SL811BufWrite (hci, SL11H_DATA_START, (__u8 *) data, len); - } - - /* transmit */ - - SL11StartXaction (hci, (__u8) address, (__u8) endpoint, (__u8) pid, len, - toggle, slow, urb_state); - - return len; -} - -/************************************************************************ - * Function Name : hc_parse_trans - * - * This function checks the status of the transmitted or received packet - * and copy the data from the SL811HS register into a buffer. - * - * 1) Check the status of the packet - * 2) If successful, and IN packet then copy the data from the SL811HS register - * into a buffer - * - * Input: hci = data structure for the host controller - * actbytes = pointer to actual number of bytes - * data = data buffer - * cc = packet status - * length = the urb transmit length - * pid = packet ID - * urb_state = the current stage of USB transaction - * - * Return: 0 - ***********************************************************************/ -static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data, - int *cc, int *toggle, int length, int pid, - int urb_state) -{ - __u8 addr; - __u8 len; - - DBGFUNC ("enter hc_parse_trans\n"); - - /* get packet status; convert ack rcvd to ack-not-rcvd */ - - *cc = (int) SL811Read (hci, SL11H_PKTSTATREG); - - if (*cc & - (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | SL11H_STATMASK_OVF | - SL11H_STATMASK_NAK | SL11H_STATMASK_STALL)) { - if (*cc & SL11H_STATMASK_OVF) - DBGERR ("parse trans: error recv ack, cc = 0x%x, TX_BASE_Len = " - "0x%x, TX_count=0x%x\n", *cc, - SL811Read (hci, SL11H_BUFLNTHREG), - SL811Read (hci, SL11H_XFERCNTREG)); - - } else { - DBGVERBOSE ("parse trans: recv ack, cc = 0x%x, len = 0x%x, \n", - *cc, length); - - /* Successful data */ - if ((pid == PID_IN) && (urb_state != US_CTRL_SETUP)) { - - /* Find the base address */ - addr = SL811Read (hci, SL11H_BUFADDRREG); - - /* Find the Transmit Length */ - len = SL811Read (hci, SL11H_BUFLNTHREG); - - /* The actual data length = xmit length reg - xfer count reg */ - *actbytes = len - SL811Read (hci, SL11H_XFERCNTREG); - - if ((data != NULL) && (*actbytes > 0)) { - SL811BufRead (hci, addr, data, *actbytes); - - } else if ((data == NULL) && (*actbytes <= 0)) { - DBGERR ("hc_parse_trans: data = NULL or actbyte = 0x%x\n", - *actbytes); - return 0; - } - } else if (pid == PID_OUT) { - *actbytes = length; - } else { - // printk ("ERR:parse_trans, pid != IN or OUT, pid = 0x%x\n", pid); - } - *toggle = !*toggle; - } - - return 0; -} - -/************************************************************************ - * Function Name : hc_start_int - * - * This function enables SL811HS interrupts - * - * Input: hci = data structure for the host controller - * - * Return: none - ***********************************************************************/ -static void hc_start_int (hci_t * hci) -{ -#ifdef HC_SWITCH_INT - int mask = - SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | - SL11H_INTMASK_INSRMV | SL11H_INTMASK_USBRESET; - SL811Write (hci, IntEna, mask); -#endif -} - -/************************************************************************ - * Function Name : hc_stop_int - * - * This function disables SL811HS interrupts - * - * Input: hci = data structure for the host controller - * - * Return: none - ***********************************************************************/ -static void hc_stop_int (hci_t * hci) -{ -#ifdef HC_SWITCH_INT - SL811Write (hci, SL11H_INTSTATREG, 0xff); -// SL811Write(hci, SL11H_INTENBLREG, SL11H_INTMASK_INSRMV); - -#endif -} - -/************************************************************************ - * Function Name : handleInsRmvIntr - * - * This function handles the insertion or removal of device on SL811HS. - * It resets the controller and updates the port status - * - * Input: hci = data structure for the host controller - * - * Return: none - ***********************************************************************/ -void handleInsRmvIntr (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - - USBReset (hci); - - /* Changes in connection status */ - - hp->RHportStatus->portChange |= PORT_CONNECT_CHANGE; - - /* Port Enable or Disable */ - - if (hp->RHportStatus->portStatus & PORT_CONNECT_STAT) { - /* device is connected to the port: - * 1) Enable port - * 2) Resume ?? - */ -// hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE; - - /* Over Current is not supported by the SL811 HW ?? */ - - /* How about the Port Power ?? */ - - } else { - /* Device has disconnect: - * 1) Disable port - */ - - hp->RHportStatus->portStatus &= ~(PORT_ENABLE_STAT); - hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE; - - } -} - -/***************************************************************** - * - * Function Name: SL11StartXaction - * - * This functions load the registers with appropriate value and - * transmit the packet. - * - * Input: hci = data structure for the host controller - * addr = USB address of the device - * epaddr = endpoint number - * pid = packet ID - * len = data length - * toggle = USB toggle bit, either 0 or 1 - * slow = speed of the device - * urb_state = the current stage of USB transaction - * - * Return: 0 = error; 1 = successful - * - *****************************************************************/ -int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len, - int toggle, int slow, int urb_state) -{ - - hcipriv_t *hp = &hci->hp; - __u8 cmd = 0; - __u8 setup_data[4]; - __u16 speed; - - speed = hp->RHportStatus->portStatus; - if (!(speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) && slow) { - cmd |= SL11H_HCTLMASK_PREAMBLE; - } - switch (pid) { - case PID_SETUP: - cmd &= SL11H_HCTLMASK_PREAMBLE; - cmd |= - (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP | - SL11H_HCTLMASK_WRITE); - break; - - case PID_OUT: - cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE); - cmd |= - (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP | - SL11H_HCTLMASK_WRITE); - if (toggle) { - cmd |= SL11H_HCTLMASK_SEQ; - } - break; - - case PID_IN: - cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE); - cmd |= (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP); - break; - - default: - DBGERR ("ERR: SL11StartXaction: unknow pid = 0x%x\n", pid); - return 0; - } - setup_data[0] = SL11H_DATA_START; - setup_data[1] = len; - setup_data[2] = (((pid & 0x0F) << 4) | (epaddr & 0xF)); - setup_data[3] = addr & 0x7F; - - SL811BufWrite (hci, SL11H_BUFADDRREG, (__u8 *) & setup_data[0], 4); - - SL811Write (hci, SL11H_HOSTCTLREG, cmd); - -#if 0 - /* The SL811 has a hardware flaw when hub devices sends out - * SE0 between packets. It has been found in a TI chipset and - * cypress hub chipset. It causes the SL811 to hang - * The workaround is to re-issue the preample again. - */ - - if ((cmd & SL11H_HCTLMASK_PREAMBLE)) { - SL811Write (hci, SL11H_PIDEPREG_B, 0xc0); - SL811Write (hci, SL11H_HOSTCTLREG_B, 0x1); // send the premable - } -#endif - return 1; -} - -/***************************************************************** - * - * Function Name: hc_interrupt - * - * Interrupt service routine. - * - * 1) determine the causes of interrupt - * 2) clears all interrupts - * 3) calls appropriate function to service the interrupt - * - * Input: irq = interrupt line associated with the controller - * hci = data structure for the host controller - * r = holds the snapshot of the processor's context before - * the processor entered interrupt code. (not used here) - * - * Return value : None. - * - *****************************************************************/ -static void hc_interrupt (int irq, void *__hci, struct pt_regs *r) -{ - char ii; - hci_t *hci = __hci; - int isExcessNak = 0; - int urb_state = 0; - char tmpIrq = 0; - - /* Get value from interrupt status register */ - - ii = SL811Read (hci, SL11H_INTSTATREG); - - if (ii & SL11H_INTMASK_INSRMV) { - /* Device insertion or removal detected for the USB port */ - - SL811Write (hci, SL11H_INTENBLREG, 0); - SL811Write (hci, SL11H_CTLREG1, 0); - mdelay (100); // wait for device stable - handleInsRmvIntr (hci); - return; - } - - /* Clear all interrupts */ - - SL811Write (hci, SL11H_INTSTATREG, 0xff); - - if (ii & SL11H_INTMASK_XFERDONE) { - /* USB Done interrupt occurred */ - - urb_state = sh_done_list (hci, &isExcessNak); -#ifdef WARNING - if (hci->td_array->len > 0) - printk ("WARNING: IRQ, td_array->len = 0x%x, s/b:0\n", - hci->td_array->len); -#endif - if (hci->td_array->len == 0 && !isExcessNak - && !(ii & SL11H_INTMASK_SOFINTR) && (urb_state == 0)) { - if (urb_state == 0) { - /* All urb_state has not been finished yet! - * continue with the current urb transaction - */ - - if (hci->last_packet_nak == 0) { - if (!usb_pipecontrol - (hci->td_array->td[0].urb->pipe)) - sh_add_packet (hci, hci->td_array-> td[0].urb); - } - } else { - /* The last transaction has completed: - * schedule the next transaction - */ - - sh_schedule_trans (hci, 0); - } - } - SL811Write (hci, SL11H_INTSTATREG, 0xff); - return; - } - - if (ii & SL11H_INTMASK_SOFINTR) { - hci->frame_number = (hci->frame_number + 1) % 2048; - if (hci->td_array->len == 0) - sh_schedule_trans (hci, 1); - else { - if (sofWaitCnt++ > 100) { - /* The last transaction has not completed. - * Need to retire the current td, and let - * it transmit again later on. - * (THIS NEEDS TO BE WORK ON MORE, IT SHOULD NEVER - * GET TO THIS POINT) - */ - - DBGERR ("SOF interrupt: td_array->len = 0x%x, s/b: 0\n", - hci->td_array->len); - urb_print (hci->td_array->td[hci->td_array->len - 1].urb, - "INTERRUPT", 0); - sh_done_list (hci, &isExcessNak); - SL811Write (hci, SL11H_INTSTATREG, 0xff); - hci->td_array->len = 0; - sofWaitCnt = 0; - } - } - tmpIrq = SL811Read (hci, SL11H_INTSTATREG) & SL811Read (hci, SL11H_INTENBLREG); - if (tmpIrq) { - DBG ("IRQ occurred while service SOF: irq = 0x%x\n", - tmpIrq); - - /* If we receive a DONE IRQ after schedule, need to - * handle DONE IRQ again - */ - - if (tmpIrq & SL11H_INTMASK_XFERDONE) { - DBGERR ("IRQ occurred while service SOF: irq = 0x%x\n", - tmpIrq); - urb_state = sh_done_list (hci, &isExcessNak); - } - SL811Write (hci, SL11H_INTSTATREG, 0xff); - } - } else { - DBG ("SL811 ISR: unknown, int = 0x%x \n", ii); - } - - SL811Write (hci, SL11H_INTSTATREG, 0xff); - return; -} - -/***************************************************************** - * - * Function Name: hc_reset - * - * This function does register test and resets the SL811HS - * controller. - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static int hc_reset (hci_t * hci) -{ - int attachFlag = 0; - - DBGFUNC ("Enter hc_reset\n"); - regTest (hci); - attachFlag = USBReset (hci); - if (attachFlag) { - setPortChange (hci, PORT_CONNECT_CHANGE); - } - return (0); -} - -/***************************************************************** - * - * Function Name: hc_alloc_trans_buffer - * - * This function allocates all transfer buffer - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static int hc_alloc_trans_buffer (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - int maxlen; - - hp->itl0_len = 0; - hp->itl1_len = 0; - hp->atl_len = 0; - - hp->itl_buffer_len = 1024; - hp->atl_buffer_len = 4096 - 2 * hp->itl_buffer_len; /* 2048 */ - - maxlen = (hp->itl_buffer_len > hp->atl_buffer_len) ? hp->itl_buffer_len : hp->atl_buffer_len; - - hp->tl = kmalloc (maxlen, GFP_KERNEL); - - if (!hp->tl) - return -ENOMEM; - - memset (hp->tl, 0, maxlen); - return 0; -} - -/***************************************************************** - * - * Function Name: getPortStatusAndChange - * - * This function gets the ports status from SL811 and format it - * to a USB request format - * - * Input: hci = data structure for the host controller - * - * Return value : port status and change - * - *****************************************************************/ -static __u32 getPortStatusAndChange (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - __u32 portstatus; - - DBGFUNC ("enter getPorStatusAndChange\n"); - - portstatus = hp->RHportStatus->portChange << 16 | hp->RHportStatus->portStatus; - - return (portstatus); -} - -/***************************************************************** - * - * Function Name: setPortChange - * - * This function set the bit position of portChange. - * - * Input: hci = data structure for the host controller - * bitPos = the bit position - * - * Return value : none - * - *****************************************************************/ -static void setPortChange (hci_t * hci, __u16 bitPos) -{ - hcipriv_t *hp = &hci->hp; - - switch (bitPos) { - case PORT_CONNECT_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_ENABLE_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_RESET_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_POWER_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_SUSPEND_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_OVER_CURRENT_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - } -} - -/***************************************************************** - * - * Function Name: clrPortChange - * - * This function clear the bit position of portChange. - * - * Input: hci = data structure for the host controller - * bitPos = the bit position - * - * Return value : none - * - *****************************************************************/ -static void clrPortChange (hci_t * hci, __u16 bitPos) -{ - hcipriv_t *hp = &hci->hp; - switch (bitPos) { - case PORT_CONNECT_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - - case PORT_ENABLE_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - - case PORT_RESET_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - - case PORT_SUSPEND_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - - case PORT_OVER_CURRENT_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - } -} - -/***************************************************************** - * - * Function Name: clrPortStatus - * - * This function clear the bit position of portStatus. - * - * Input: hci = data structure for the host controller - * bitPos = the bit position - * - * Return value : none - * - *****************************************************************/ -static void clrPortStatus (hci_t * hci, __u16 bitPos) -{ - hcipriv_t *hp = &hci->hp; - switch (bitPos) { - case PORT_ENABLE_STAT: - hp->RHportStatus->portStatus &= ~bitPos; - break; - - case PORT_RESET_STAT: - hp->RHportStatus->portStatus &= ~bitPos; - break; - - case PORT_POWER_STAT: - hp->RHportStatus->portStatus &= ~bitPos; - break; - - case PORT_SUSPEND_STAT: - hp->RHportStatus->portStatus &= ~bitPos; - break; - } -} - -/***************************************************************** - * - * Function Name: setPortStatus - * - * This function set the bit position of portStatus. - * - * Input: hci = data structure for the host controller - * bitPos = the bit position - * - * Return value : none - * - *****************************************************************/ -static void setPortStatus (hci_t * hci, __u16 bitPos) -{ - hcipriv_t *hp = &hci->hp; - switch (bitPos) { - case PORT_ENABLE_STAT: - hp->RHportStatus->portStatus |= bitPos; - break; - - case PORT_RESET_STAT: - hp->RHportStatus->portStatus |= bitPos; - break; - - case PORT_POWER_STAT: - hp->RHportStatus->portStatus |= bitPos; - break; - - case PORT_SUSPEND_STAT: - hp->RHportStatus->portStatus |= bitPos; - break; - } -} - -/***************************************************************** - * - * Function Name: hc_start - * - * This function starts the root hub functionality. - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static int hc_start (hci_t * hci) -{ - DBGFUNC ("Enter hc_start\n"); - - rh_connect_rh (hci); - - return 0; -} - -/***************************************************************** - * - * Function Name: hc_alloc_hci - * - * This function allocates all data structure and store in the - * private data structure. - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static hci_t *__devinit hc_alloc_hci (void) -{ - hci_t *hci; - hcipriv_t *hp; - portstat_t *ps; - struct usb_bus *bus; - - DBGFUNC ("Enter hc_alloc_hci\n"); - hci = (hci_t *) kmalloc (sizeof (hci_t), GFP_KERNEL); - if (!hci) - return NULL; - - memset (hci, 0, sizeof (hci_t)); - - hp = &hci->hp; - - hp->irq = -1; - hp->hcport = -1; - - /* setup root hub port status */ - - ps = (portstat_t *) kmalloc (sizeof (portstat_t), GFP_KERNEL); - - if (!ps) - return NULL; - ps->portStatus = PORT_STAT_DEFAULT; - ps->portChange = PORT_CHANGE_DEFAULT; - hp->RHportStatus = ps; - - hci->nakCnt = 0; - hci->last_packet_nak = 0; - - hci->a_td_array.len = 0; - hci->i_td_array[0].len = 0; - hci->i_td_array[1].len = 0; - hci->td_array = &hci->a_td_array; - hci->active_urbs = 0; - hci->active_trans = 0; - INIT_LIST_HEAD (&hci->hci_hcd_list); - list_add (&hci->hci_hcd_list, &hci_hcd_list); - init_waitqueue_head (&hci->waitq); - - INIT_LIST_HEAD (&hci->ctrl_list); - INIT_LIST_HEAD (&hci->bulk_list); - INIT_LIST_HEAD (&hci->iso_list); - INIT_LIST_HEAD (&hci->intr_list); - INIT_LIST_HEAD (&hci->del_list); - - bus = usb_alloc_bus (&hci_device_operations); - if (!bus) { - kfree (hci); - return NULL; - } - - hci->bus = bus; - bus->bus_name = "sl811"; - bus->hcpriv = (void *) hci; - - return hci; -} - -/***************************************************************** - * - * Function Name: hc_release_hci - * - * This function De-allocate all resources - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static void hc_release_hci (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - - DBGFUNC ("Enter hc_release_hci\n"); - - /* disconnect all devices */ - if (hci->bus->root_hub) - usb_disconnect (&hci->bus->root_hub); - - hc_reset (hci); - - if (hp->tl) - kfree (hp->tl); - - if (hp->hcport > 0) { - release_region (hp->hcport, 2); - hp->hcport = 0; - } - - if (hp->irq >= 0) { - free_irq (hp->irq, hci); - hp->irq = -1; - } - - usb_deregister_bus (hci->bus); - usb_free_bus (hci->bus); - - list_del (&hci->hci_hcd_list); - INIT_LIST_HEAD (&hci->hci_hcd_list); - - kfree (hci); -} - -/***************************************************************** - * - * Function Name: init_irq - * - * This function is board specific. It sets up the interrupt to - * be an edge trigger and trigger on the rising edge - * - * Input: none - * - * Return value : none - * - *****************************************************************/ -void init_irq (void) -{ - GPDR &= ~(1 << 13); - set_GPIO_IRQ_edge (1 << 13, GPIO_RISING_EDGE); -} - -/***************************************************************** - * - * Function Name: hc_found_hci - * - * This function request IO memory regions, request IRQ, and - * allocate all other resources. - * - * Input: addr = first IO address - * addr2 = second IO address - * irq = interrupt number - * - * Return: 0 = success or error condition - * - *****************************************************************/ -static int __devinit hc_found_hci (int addr, int addr2, int irq) -{ - hci_t *hci; - hcipriv_t *hp; - - DBGFUNC ("Enter hc_found_hci\n"); - hci = hc_alloc_hci (); - if (!hci) { - return -ENOMEM; - } - - init_irq (); - hp = &hci->hp; - - if (!request_region (addr, 256, "SL811 USB HOST")) { - DBGERR ("request address %d failed", addr); - hc_release_hci (hci); - return -EBUSY; - } - hp->hcport = addr; - if (!hp->hcport) { - DBGERR ("Error mapping SL811 Memory 0x%x", hp->hcport); - } - - if (!request_region (addr2, 256, "SL811 USB HOST")) { - DBGERR ("request address %d failed", addr2); - hc_release_hci (hci); - return -EBUSY; - } - hp->hcport2 = addr2; - if (!hp->hcport2) { - DBGERR ("Error mapping SL811 Memory 0x%x", hp->hcport2); - } - - if (hc_alloc_trans_buffer (hci)) { - hc_release_hci (hci); - return -ENOMEM; - } - - usb_register_bus (hci->bus); - - if (request_irq (irq, hc_interrupt, 0, "SL811", hci) != 0) { - DBGERR ("request interrupt %d failed", irq); - hc_release_hci (hci); - return -EBUSY; - } - hp->irq = irq; - - printk (KERN_INFO __FILE__ ": USB SL811 at %x, addr2 = %x, IRQ %d\n", - addr, addr2, irq); - hc_reset (hci); - - if (hc_start (hci) < 0) { - DBGERR ("can't start usb-%x", addr); - hc_release_hci (hci); - return -EBUSY; - } - - return 0; -} - -/***************************************************************** - * - * Function Name: hci_hcd_init - * - * This is an init function, and it is the first function being called - * - * Input: none - * - * Return: 0 = success or error condition - * - *****************************************************************/ -static int __init hci_hcd_init (void) -{ - int ret; - - DBGFUNC ("Enter hci_hcd_init\n"); - ret = hc_found_hci (base_addr, data_reg_addr, irq); - - return ret; -} - -/***************************************************************** - * - * Function Name: hci_hcd_cleanup - * - * This is a cleanup function, and it is called when module is - * unloaded. - * - * Input: none - * - * Return: none - * - *****************************************************************/ -static void __exit hci_hcd_cleanup (void) -{ - struct list_head *hci_l; - hci_t *hci; - - DBGFUNC ("Enter hci_hcd_cleanup\n"); - for (hci_l = hci_hcd_list.next; hci_l != &hci_hcd_list;) { - hci = list_entry (hci_l, hci_t, hci_hcd_list); - hci_l = hci_l->next; - hc_release_hci (hci); - } -} - -module_init (hci_hcd_init); -module_exit (hci_hcd_cleanup); - -MODULE_AUTHOR ("Pei Liu "); -MODULE_DESCRIPTION ("USB SL811HS Host Controller Driver"); diff -urN linux-2.4.22-bk41/drivers/usb/hc_sl811.h linux-2.4.22-bk42/drivers/usb/hc_sl811.h --- linux-2.4.22-bk41/drivers/usb/hc_sl811.h 2002-11-28 15:53:14.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/hc_sl811.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,385 +0,0 @@ -/* - * SL811HS HCD (Host Controller Driver) for USB. - * - * COPYRIGHT (C) by CYPRESS SEMICONDUCTOR INC - * - * - */ - -#define GET_FRAME_NUMBER(hci) READ_REG32 (hci, HcFmNumber) - -/* - * Maximum number of root hub ports - */ -#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ - -/* control and status registers */ -#define HcRevision 0x00 -#define HcControl 0x01 -#define HcCommandStatus 0x02 -#define HcInterruptStatus 0x03 -#define HcInterruptEnable 0x04 -#define HcInterruptDisable 0x05 -#define HcFmInterval 0x0D -#define HcFmRemaining 0x0E -#define HcFmNumber 0x0F -#define HcLSThreshold 0x11 -#define HcRhDescriptorA 0x12 -#define HcRhDescriptorB 0x13 -#define HcRhStatus 0x14 -#define HcRhPortStatus 0x15 - -#define HcHardwareConfiguration 0x20 -#define HcDMAConfiguration 0x21 -#define HcTransferCounter 0x22 -#define HcuPInterrupt 0x24 -#define HcuPInterruptEnable 0x25 -#define HcChipID 0x27 -#define HcScratch 0x28 -#define HcSoftwareReset 0x29 -#define HcITLBufferLength 0x2A -#define HcATLBufferLength 0x2B -#define HcBufferStatus 0x2C -#define HcReadBackITL0Length 0x2D -#define HcReadBackITL1Length 0x2E -#define HcITLBufferPort 0x40 -#define HcATLBufferPort 0x41 - -/* OHCI CONTROL AND STATUS REGISTER MASKS */ - -/* - * HcControl (control) register masks - */ -#define OHCI_CTRL_HCFS (3 << 6) /* BUS state mask */ -#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ -#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ - -/* pre-shifted values for HCFS */ -#define OHCI_USB_RESET (0 << 6) -#define OHCI_USB_RESUME (1 << 6) -#define OHCI_USB_OPER (2 << 6) -#define OHCI_USB_SUSPEND (3 << 6) - -/* - * HcCommandStatus (cmdstatus) register masks - */ -#define OHCI_HCR (1 << 0) /* host controller reset */ -#define OHCI_SO (3 << 16) /* scheduling overrun count */ - -/* - * masks used with interrupt registers: - * HcInterruptStatus (intrstatus) - * HcInterruptEnable (intrenable) - * HcInterruptDisable (intrdisable) - */ -#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ - -#define OHCI_INTR_SF (1 << 2) /* start frame */ -#define OHCI_INTR_RD (1 << 3) /* resume detect */ -#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ -#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ -#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ -#define OHCI_INTR_ATD (1 << 7) /* scheduling overrun */ - -#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ - -/* - * HcHardwareConfiguration - */ -#define InterruptPinEnable (1 << 0) -#define InterruptPinTrigger (1 << 1) -#define InterruptOutputPolarity (1 << 2) -#define DataBusWidth16 (1 << 3) -#define DREQOutputPolarity (1 << 5) -#define DACKInputPolarity (1 << 6) -#define EOTInputPolarity (1 << 7) -#define DACKMode (1 << 8) -#define AnalogOCEnable (1 << 10) -#define SuspendClkNotStop (1 << 11) -#define DownstreamPort15KRSel (1 << 12) - -/* - * HcDMAConfiguration - */ -#define DMAReadWriteSelect (1 << 0) -#define ITL_ATL_DataSelect (1 << 1) -#define DMACounterSelect (1 << 2) -#define DMAEnable (1 << 4) -#define BurstLen_1 0 -#define BurstLen_4 (1 << 5) -#define BurstLen_8 (2 << 5) - -/* - * HcuPInterrupt - */ -#define SOFITLInt (1 << 0) -#define ATLInt (1 << 1) -#define AllEOTInterrupt (1 << 2) -#define OPR_Reg (1 << 4) -#define HCSuspended (1 << 5) -#define ClkReady (1 << 6) - -/* - * HcBufferStatus - */ -#define ITL0BufferFull (1 << 0) -#define ITL1BufferFull (1 << 1) -#define ATLBufferFull (1 << 2) -#define ITL0BufferDone (1 << 3) -#define ITL1BufferDone (1 << 4) -#define ATLBufferDone (1 << 5) - -/* OHCI ROOT HUB REGISTER MASKS */ - -/* roothub.portstatus [i] bits */ -#define RH_PS_CCS 0x00000001 /* current connect status */ -#define RH_PS_PES 0x00000002 /* port enable status */ -#define RH_PS_PSS 0x00000004 /* port suspend status */ -#define RH_PS_POCI 0x00000008 /* port over current indicator */ -#define RH_PS_PRS 0x00000010 /* port reset status */ -#define RH_PS_PPS 0x00000100 /* port power status */ -#define RH_PS_LSDA 0x00000200 /* low speed device attached */ -#define RH_PS_CSC 0x00010000 /* connect status change */ -#define RH_PS_PESC 0x00020000 /* port enable status change */ -#define RH_PS_PSSC 0x00040000 /* port suspend status change */ -#define RH_PS_OCIC 0x00080000 /* over current indicator change */ -#define RH_PS_PRSC 0x00100000 /* port reset status change */ - -/* roothub.status bits */ -#define RH_HS_LPS 0x00000001 /* local power status */ -#define RH_HS_OCI 0x00000002 /* over current indicator */ -#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ -#define RH_HS_LPSC 0x00010000 /* local power status change */ -#define RH_HS_OCIC 0x00020000 /* over current indicator change */ -#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ - -/* roothub.b masks */ -#define RH_B_DR 0x0000ffff /* device removable flags */ -#define RH_B_PPCM 0xffff0000 /* port power control mask */ - -/* roothub.a masks */ -#define RH_A_NDP (0xff << 0) /* number of downstream ports */ -#define RH_A_PSM (1 << 8) /* power switching mode */ -#define RH_A_NPS (1 << 9) /* no power switching */ -#define RH_A_DT (1 << 10) /* device type (mbz) */ -#define RH_A_OCPM (1 << 11) /* over current protection mode */ -#define RH_A_NOCP (1 << 12) /* no over current protection */ -#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ - -#define URB_DEL 1 - -#define PORT_STAT_DEFAULT 0x0100 -#define PORT_CONNECT_STAT 0x1 -#define PORT_ENABLE_STAT 0x2 -#define PORT_SUSPEND_STAT 0x4 -#define PORT_OVER_CURRENT_STAT 0x8 -#define PORT_RESET_STAT 0x10 -#define PORT_POWER_STAT 0x100 -#define PORT_LOW_SPEED_DEV_ATTACH_STAT 0x200 - -#define PORT_CHANGE_DEFAULT 0x0 -#define PORT_CONNECT_CHANGE 0x1 -#define PORT_ENABLE_CHANGE 0x2 -#define PORT_SUSPEND_CHANGE 0x4 -#define PORT_OVER_CURRENT_CHANGE 0x8 -#define PORT_RESET_CHANGE 0x10 - -/* Port Status Request info */ - -typedef struct portstat { - __u16 portChange; - __u16 portStatus; -} portstat_t; - -typedef struct hcipriv { - int irq; - int disabled; /* e.g. got a UE, we're hung */ - atomic_t resume_count; /* defending against multiple resumes */ - struct ohci_regs *regs; /* OHCI controller's memory */ - int hcport; /* I/O base address */ - int hcport2; /* I/O data reg addr */ - - struct portstat *RHportStatus; /* root hub port status */ - - int intrstatus; - __u32 hc_control; /* copy of the hc control reg */ - - int frame; - - __u8 *tl; - int xferPktLen; - int atl_len; - int atl_buffer_len; - int itl0_len; - int itl1_len; - int itl_buffer_len; - int itl_index; - int tl_last; - int units_left; - -} hcipriv_t; -struct hci; - -#define cClt 0 // Control -#define cISO 1 // ISO -#define cBULK 2 // BULK -#define cInt 3 // Interrupt -#define ISO_BIT 0x10 - -/*------------------------------------------------------------------------- - * EP0 use for configuration and Vendor Specific command interface - *------------------------------------------------------------------------*/ -#define cMemStart 0x10 -#define EP0Buf 0x40 /* SL11H/SL811H memory start at 0x40 */ -#define EP0Len 0x40 /* Length of config buffer EP0Buf */ -#define EP1Buf 0x60 -#define EP1Len 0x40 - -/*------------------------------------------------------------------------- - * SL11H/SL811H memory from 80h-ffh use as ping-pong buffer. - *------------------------------------------------------------------------*/ -#define uBufA 0x80 /* buffer A address for DATA0 */ -#define uBufB 0xc0 /* buffer B address for DATA1 */ -#define uXferLen 0x40 /* xfer length */ -#define sMemSize 0xc0 /* Total SL11 memory size */ -#define cMemEnd 256 - -/*------------------------------------------------------------------------- - * SL811H Register Control memory map - * --Note: - * --SL11H only has one control register set from 0x00-0x04 - * --SL811H has two control register set from 0x00-0x04 and 0x08-0x0c - *------------------------------------------------------------------------*/ - -#define EP0Control 0x00 -#define EP0Address 0x01 -#define EP0XferLen 0x02 -#define EP0Status 0x03 -#define EP0Counter 0x04 - -#define EP1Control 0x08 -#define EP1Address 0x09 -#define EP1XferLen 0x0a -#define EP1Status 0x0b -#define EP1Counter 0x0c - -#define CtrlReg 0x05 -#define IntEna 0x06 - // 0x07 is reserved -#define IntStatus 0x0d -#define cDATASet 0x0e -#define cSOFcnt 0x0f -#define IntMask 0x57 /* Reset|DMA|EP0|EP2|EP1 for IntEna */ -#define HostMask 0x47 /* Host request command for IntStatus */ -#define ReadMask 0xd7 /* Read mask interrupt for IntStatus */ - -/*------------------------------------------------------------------------- - * Standard Chapter 9 definition - *------------------------------------------------------------------------- - */ -#define GET_STATUS 0x00 -#define CLEAR_FEATURE 0x01 -#define SET_FEATURE 0x03 -#define SET_ADDRESS 0x05 -#define GET_DESCRIPTOR 0x06 -#define SET_DESCRIPTOR 0x07 -#define GET_CONFIG 0x08 -#define SET_CONFIG 0x09 -#define GET_INTERFACE 0x0a -#define SET_INTERFACE 0x0b -#define SYNCH_FRAME 0x0c - -#define DEVICE 0x01 -#define CONFIGURATION 0x02 -#define STRING 0x03 -#define INTERFACE 0x04 -#define ENDPOINT 0x05 - -/*------------------------------------------------------------------------- - * SL11H/SL811H definition - *------------------------------------------------------------------------- - */ -#define DATA0_WR 0x07 // (Arm+Enable+tranmist to Host+DATA0) -#define DATA1_WR 0x47 // (Arm+Enable+tranmist to Host on DATA1) -#define ZDATA0_WR 0x05 // (Arm+Transaction Ignored+tranmist to Host+DATA0) -#define ZDATA1_WR 0x45 // (Arm+Transaction Ignored+tranmist to Host+DATA1) -#define DATA0_RD 0x03 // (Arm+Enable+received from Host+DATA0) -#define DATA1_RD 0x43 // (Arm+Enable+received from Host+DATA1) - -#define PID_SETUP 0x2d // USB Specification 1.1 Standard Definition -#define PID_SOF 0xA5 -#define PID_IN 0x69 -#define PID_OUT 0xe1 - -#define MAX_RETRY 0xffff -#define TIMEOUT 5 /* 2 mseconds */ - -#define SL11H_HOSTCTLREG 0 -#define SL11H_BUFADDRREG 1 -#define SL11H_BUFLNTHREG 2 -#define SL11H_PKTSTATREG 3 /* read */ -#define SL11H_PIDEPREG 3 /* write */ -#define SL11H_XFERCNTREG 4 /* read */ -#define SL11H_DEVADDRREG 4 /* write */ -#define SL11H_CTLREG1 5 -#define SL11H_INTENBLREG 6 - -#define SL11H_HOSTCTLREG_B 8 -#define SL11H_BUFADDRREG_B 9 -#define SL11H_BUFLNTHREG_B 0x0A -#define SL11H_PKTSTATREG_B 0x0B /* read */ -#define SL11H_PIDEPREG_B 0x0B /* write */ -#define SL11H_XFERCNTREG_B 0x0C /* read */ -#define SL11H_DEVADDRREG_B 0x0C /* write */ - -#define SL11H_INTSTATREG 0x0D /* write clears bitwise */ -#define SL11H_HWREVREG 0x0E /* read */ -#define SL11H_SOFLOWREG 0x0E /* write */ -#define SL11H_SOFTMRREG 0x0F /* read */ -#define SL11H_CTLREG2 0x0F /* write */ -#define SL11H_DATA_START 0x10 - -/* Host control register bits (addr 0) */ -#define SL11H_HCTLMASK_ARM 1 -#define SL11H_HCTLMASK_ENBLEP 2 -#define SL11H_HCTLMASK_WRITE 4 -#define SL11H_HCTLMASK_ISOCH 0x10 -#define SL11H_HCTLMASK_AFTERSOF 0x20 -#define SL11H_HCTLMASK_SEQ 0x40 -#define SL11H_HCTLMASK_PREAMBLE 0x80 - -/* Packet status register bits (addr 3) */ -#define SL11H_STATMASK_ACK 1 -#define SL11H_STATMASK_ERROR 2 -#define SL11H_STATMASK_TMOUT 4 -#define SL11H_STATMASK_SEQ 8 -#define SL11H_STATMASK_SETUP 0x10 -#define SL11H_STATMASK_OVF 0x20 -#define SL11H_STATMASK_NAK 0x40 -#define SL11H_STATMASK_STALL 0x80 - -/* Control register 1 bits (addr 5) */ -#define SL11H_CTL1MASK_DSBLSOF 1 -#define SL11H_CTL1MASK_NOTXEOF2 4 -#define SL11H_CTL1MASK_DSTATE 0x18 -#define SL11H_CTL1MASK_NSPD 0x20 -#define SL11H_CTL1MASK_SUSPEND 0x40 -#define SL11H_CTL1MASK_CLK12 0x80 - -#define SL11H_CTL1VAL_RESET 8 - -/* Interrut enable (addr 6) and interrupt status register bits (addr 0xD) */ -#define SL11H_INTMASK_XFERDONE 1 -#define SL11H_INTMASK_SOFINTR 0x10 -#define SL11H_INTMASK_INSRMV 0x20 -#define SL11H_INTMASK_USBRESET 0x40 -#define SL11H_INTMASK_DSTATE 0x80 /* only in status reg */ - -/* HW rev and SOF lo register bits (addr 0xE) */ -#define SL11H_HWRMASK_HWREV 0xF0 - -/* SOF counter and control reg 2 (addr 0xF) */ -#define SL11H_CTL2MASK_SOFHI 0x3F -#define SL11H_CTL2MASK_DSWAP 0x40 -#define SL11H_CTL2MASK_HOSTMODE 0xae - diff -urN linux-2.4.22-bk41/drivers/usb/hc_sl811_rh.c linux-2.4.22-bk42/drivers/usb/hc_sl811_rh.c --- linux-2.4.22-bk41/drivers/usb/hc_sl811_rh.c 2002-11-28 15:53:14.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/hc_sl811_rh.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,526 +0,0 @@ - -/*-------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------* - * SL811HS virtual root hub - * - * based on usb-ohci.c by R. Weissgaerber et al. - *-------------------------------------------------------------------------* - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - *-------------------------------------------------------------------------*/ - -#ifdef DEBUG -#undef DEBUG -#endif -static __u32 getPortStatusAndChange (hci_t * hci); -static void setPortStatus (hci_t * hci, __u16 bitPos); -static void setPortChange (hci_t * hci, __u16 bitPos); -static void clrPortStatus (hci_t * hci, __u16 bitPos); -static void clrPortChange (hci_t * hci, __u16 bitPos); -static int USBReset (hci_t * hci); -static int cc_to_error (int cc); - -/*-------------------------------------------------------------------------* - * Virtual Root Hub - *-------------------------------------------------------------------------*/ - -/* Device descriptor */ -static __u8 root_hub_dev_des[] = { - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x10, /* __u16 bcdUSB; v1.1 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x02, /* __u8 iProduct; */ - 0x01, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = { - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, - 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -/* Hub class-specific descriptor is constructed dynamically */ - -/*************************************************************************** - * Function Name : rh_send_irq - * - * This function examine the port change in the virtual root hub. - * - * Note: This function assumes only one port exist in the root hub. - * - * Input: hci = data structure for the host controller - * rh_data = The pointer to port change data - * rh_len = length of the data in bytes - * - * Return: length of data - **************************************************************************/ -static int rh_send_irq (hci_t * hci, void *rh_data, int rh_len) -{ - int num_ports; - int i; - int ret; - int len; - __u8 data[8]; - - DBGFUNC ("enter rh_send_irq: \n"); - - /* Assuming the root hub has one port. This value need to change if - * there are more than one port for the root hub - */ - - num_ports = 1; - - /* The root hub status is not implemented, it basically has two fields: - * -- Local Power Status - * -- Over Current Indicator - * -- Local Power Change - * -- Over Current Indicator - * - * Right now, It is assume the power is good and no changes - */ - - *(__u8 *) data = 0; - - ret = *(__u8 *) data; - - /* Has the port status change within the root hub: It checks for - * -- Port Connect Status change - * -- Port Enable Change - */ - - for (i = 0; i < num_ports; i++) { - *(__u8 *) (data + (i + 1) / 8) |= - (((getPortStatusAndChange (hci) >> 16) & (PORT_CONNECT_STAT | PORT_ENABLE_STAT)) ? 1 : 0) << ((i + 1) % 8); - ret += *(__u8 *) (data + (i + 1) / 8); - - /* After the port change is read, it should be reset so the next time - * is it doesn't trigger a change again */ - - } - len = i / 8 + 1; - - if (ret > 0) { - memcpy (rh_data, data, min (len, min (rh_len, (int)sizeof (data)))); - return len; - } - return 0; -} - -/*************************************************************************** - * Function Name : rh_int_timer_do - * - * This function is called when the timer expires. It gets the the port - * change data and pass along to the upper protocol. - * - * Note: The virtual root hub interrupt pipe are polled by the timer - * every "interval" ms - * - * Input: ptr = ptr to the urb - * - * Return: none - **************************************************************************/ -static void rh_int_timer_do (unsigned long ptr) -{ - int len; - struct urb *urb = (struct urb *) ptr; - hci_t *hci = urb->dev->bus->hcpriv; - - DBGFUNC ("enter rh_int_timer_do\n"); - - if (hci->rh.send) { - len = rh_send_irq (hci, urb->transfer_buffer, - urb->transfer_buffer_length); - if (len > 0) { - urb->actual_length = len; - if (urb_debug == 2) - urb_print (urb, "RET-t(rh)", - usb_pipeout (urb->pipe)); - - if (urb->complete) { - urb->complete (urb); - } - } - } - - /* re-activate the timer */ - rh_init_int_timer (urb); -} - -/*************************************************************************** - * Function Name : rh_init_int_timer - * - * This function creates a timer that act as interrupt pipe in the - * virtual hub. - * - * Note: The virtual root hub's interrupt pipe are polled by the timer - * every "interval" ms - * - * Input: urb = USB request block - * - * Return: 0 - **************************************************************************/ -static int rh_init_int_timer (struct urb * urb) -{ - hci_t *hci = urb->dev->bus->hcpriv; - hci->rh.interval = urb->interval; - - init_timer (&hci->rh.rh_int_timer); - hci->rh.rh_int_timer.function = rh_int_timer_do; - hci->rh.rh_int_timer.data = (unsigned long) urb; - hci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000; - add_timer (&hci->rh.rh_int_timer); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* helper macro */ -#define OK(x) len = (x); break - -/*************************************************************************** - * Function Name : rh_submit_urb - * - * This function handles all USB request to the the virtual root hub - * - * Input: urb = USB request block - * - * Return: 0 - **************************************************************************/ -static int rh_submit_urb (struct urb * urb) -{ - struct usb_device *usb_dev = urb->dev; - hci_t *hci = usb_dev->bus->hcpriv; - unsigned int pipe = urb->pipe; - struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet; - void *data = urb->transfer_buffer; - int leni = urb->transfer_buffer_length; - int len = 0; - int status = TD_CC_NOERROR; - __u32 datab[4]; - __u8 *data_buf = (__u8 *) datab; - - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - - DBGFUNC ("enter rh_submit_urb\n"); - if (usb_pipeint (pipe)) { - hci->rh.urb = urb; - hci->rh.send = 1; - hci->rh.interval = urb->interval; - rh_init_int_timer (urb); - urb->status = cc_to_error (TD_CC_NOERROR); - - return 0; - } - - bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8); - wValue = le16_to_cpu (cmd->wValue); - wIndex = le16_to_cpu (cmd->wIndex); - wLength = le16_to_cpu (cmd->wLength); - - DBG ("rh_submit_urb, req = %d(%x) len=%d", - bmRType_bReq, bmRType_bReq, wLength); - - switch (bmRType_bReq) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - *(__u16 *) data_buf = cpu_to_le16 (1); - OK (2); - - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = cpu_to_le16 (0); - OK (2); - - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = cpu_to_le16 (0); - OK (2); - - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = cpu_to_le32 (0); - OK (4); - - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = - cpu_to_le32 (getPortStatusAndChange (hci)); - OK (4); - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): - OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case RH_C_HUB_LOCAL_POWER: - OK (0); - - case (RH_C_HUB_OVER_CURRENT): - /* Over Current Not Implemented */ - OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): - clrPortStatus (hci, PORT_ENABLE_STAT); - OK (0); - - case (RH_PORT_SUSPEND): - clrPortStatus (hci, PORT_SUSPEND_STAT); - OK (0); - - case (RH_PORT_POWER): - clrPortStatus (hci, PORT_POWER_STAT); - OK (0); - - case (RH_C_PORT_CONNECTION): - clrPortChange (hci, PORT_CONNECT_STAT); - OK (0); - - case (RH_C_PORT_ENABLE): - clrPortChange (hci, PORT_ENABLE_STAT); - OK (0); - - case (RH_C_PORT_SUSPEND): - clrPortChange (hci, PORT_SUSPEND_STAT); - OK (0); - - case (RH_C_PORT_OVER_CURRENT): - clrPortChange (hci, PORT_OVER_CURRENT_STAT); - OK (0); - - case (RH_C_PORT_RESET): - clrPortChange (hci, PORT_RESET_STAT); - OK (0); - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - setPortStatus (hci, PORT_SUSPEND_STAT); - OK (0); - - case (RH_PORT_RESET): - setPortStatus (hci, PORT_RESET_STAT); - // USBReset(hci); - clrPortChange (hci, - PORT_CONNECT_CHANGE | PORT_ENABLE_CHANGE - | PORT_SUSPEND_CHANGE | - PORT_OVER_CURRENT_CHANGE); - setPortChange (hci, PORT_RESET_CHANGE); - clrPortStatus (hci, PORT_RESET_STAT); - setPortStatus (hci, PORT_ENABLE_STAT); - - OK (0); - - case (RH_PORT_POWER): - setPortStatus (hci, PORT_POWER_STAT); - OK (0); - - case (RH_PORT_ENABLE): - setPortStatus (hci, PORT_ENABLE_STAT); - OK (0); - } - break; - - case RH_SET_ADDRESS: - hci->rh.devnum = wValue; - OK (0); - - case RH_GET_DESCRIPTOR: - DBGVERBOSE ("rh_submit_urb: RH_GET_DESCRIPTOR, wValue = 0x%x\n", wValue); - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min (leni, min ((__u16)sizeof (root_hub_dev_des), wLength)); - data_buf = root_hub_dev_des; - OK (len); - - case (0x02): /* configuration descriptor */ - len = min (leni, min ((__u16)sizeof (root_hub_config_des), wLength)); - data_buf = root_hub_config_des; - OK (len); - - case (0x03): /* string descriptors */ - len = usb_root_hub_string (wValue & 0xff, (int) (long) 0, - "SL811HS", data, wLength); - if (len > 0) { - data_buf = data; - OK (min (leni, len)); - } - - default: - status = SL11H_STATMASK_STALL; - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - data_buf[0] = 9; // min length; - data_buf[1] = 0x29; - data_buf[2] = 1; // # of downstream port - data_buf[3] = 0; - datab[1] = 0; - data_buf[5] = 50; // 100 ms for port reset - data_buf[7] = 0xfc; // which port is attachable - if (data_buf[2] < 7) { - data_buf[8] = 0xff; - } else { - } - - len = min (leni, min ((__u16)data_buf[0], wLength)); - OK (len); - - case RH_GET_CONFIGURATION: - *(__u8 *) data_buf = 0x01; - OK (1); - - case RH_SET_CONFIGURATION: - OK (0); - - default: - DBGERR ("unsupported root hub command"); - status = SL11H_STATMASK_STALL; - } - - len = min (len, leni); - if (data != data_buf) - memcpy (data, data_buf, len); - urb->actual_length = len; - urb->status = cc_to_error (status); - - urb->hcpriv = NULL; - urb->dev = NULL; - if (urb->complete) { - urb->complete (urb); - } - - return 0; -} - -/*************************************************************************** - * Function Name : rh_unlink_urb - * - * This function unlinks the URB - * - * Input: urb = USB request block - * - * Return: 0 - **************************************************************************/ -static int rh_unlink_urb (struct urb * urb) -{ - hci_t *hci = urb->dev->bus->hcpriv; - - DBGFUNC ("enter rh_unlink_urb\n"); - if (hci->rh.urb == urb) { - hci->rh.send = 0; - del_timer (&hci->rh.rh_int_timer); - hci->rh.urb = NULL; - - urb->hcpriv = NULL; - usb_put_dev (urb->dev); - urb->dev = NULL; - if (urb->transfer_flags & USB_ASYNC_UNLINK) { - urb->status = -ECONNRESET; - if (urb->complete) { - urb->complete (urb); - } - } else - urb->status = -ENOENT; - } - return 0; -} - -/*************************************************************************** - * Function Name : rh_connect_rh - * - * This function connect the virtual root hub to the USB stack - * - * Input: urb = USB request block - * - * Return: 0 - **************************************************************************/ -static int rh_connect_rh (hci_t * hci) -{ - struct usb_device *usb_dev; - - hci->rh.devnum = 0; - usb_dev = usb_alloc_dev (NULL, hci->bus); - if (!usb_dev) - return -ENOMEM; - - hci->bus->root_hub = usb_dev; - usb_connect (usb_dev); - if (usb_new_device (usb_dev) != 0) { - usb_free_dev (usb_dev); - return -ENODEV; - } - - return 0; -} diff -urN linux-2.4.22-bk41/drivers/usb/host/Config.in linux-2.4.22-bk42/drivers/usb/host/Config.in --- linux-2.4.22-bk41/drivers/usb/host/Config.in 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.22-bk42/drivers/usb/host/Config.in 2003-10-26 02:49:55.000000000 -0800 @@ -12,6 +12,8 @@ define_bool CONFIG_USB_UHCI_ALT n fi dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB -if [ "$CONFIG_ARM" = "y" ]; then - dep_tristate ' SL811HS Alternate (support isosynchronous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB +if [ "$CONFIG_ARM" = "y" -o "$CONFIG_X86" = "y" ]; then + # Cypress embedded USB controller on StrongARM or on x86 in PC/104 + dep_tristate ' SL811HS Alternate (x86, StrongARM, isosynchronous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB $CONFIG_EXPERIMENTAL + dep_tristate ' SL811HS (x86, StrongARM) support, old driver' CONFIG_USB_SL811HS $CONFIG_USB $CONFIG_EXPERIMENTAL fi diff -urN linux-2.4.22-bk41/drivers/usb/host/Makefile linux-2.4.22-bk42/drivers/usb/host/Makefile --- linux-2.4.22-bk41/drivers/usb/host/Makefile 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.22-bk42/drivers/usb/host/Makefile 2003-10-26 02:49:55.000000000 -0800 @@ -10,6 +10,7 @@ obj-$(CONFIG_USB_UHCI) += usb-uhci.o obj-$(CONFIG_USB_OHCI) += usb-ohci.o obj-$(CONFIG_USB_SL811HS_ALT) += sl811.o +obj-$(CONFIG_USB_SL811HS) += hc_sl811.o # Extract lists of the multi-part drivers. # The 'int-*' lists are the intermediate files used to build the multi's. diff -urN linux-2.4.22-bk41/drivers/usb/host/ehci-dbg.c linux-2.4.22-bk42/drivers/usb/host/ehci-dbg.c --- linux-2.4.22-bk41/drivers/usb/host/ehci-dbg.c 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.22-bk42/drivers/usb/host/ehci-dbg.c 2003-10-26 02:49:55.000000000 -0800 @@ -471,7 +471,7 @@ spin_lock_irqsave (&ehci->lock, flags); for (i = 0; i < ehci->periodic_size; i++) { p = ehci->pshadow [i]; - if (!p.ptr) + if (likely (!p.ptr)) continue; tag = Q_NEXT_TYPE (ehci->periodic [i]); @@ -491,7 +491,7 @@ break; } /* show more info the first time around */ - if (temp == seen_count) { + if (temp == seen_count && p.ptr) { u32 scratch = cpu_to_le32p ( &p.qh->hw_info1); @@ -509,8 +509,10 @@ seen [seen_count++].qh = p.qh; } else temp = 0; - tag = Q_NEXT_TYPE (p.qh->hw_next); - p = p.qh->qh_next; + if (p.qh) { + tag = Q_NEXT_TYPE (p.qh->hw_next); + p = p.qh->qh_next; + } break; case Q_TYPE_FSTN: temp = snprintf (next, size, diff -urN linux-2.4.22-bk41/drivers/usb/host/ehci-q.c linux-2.4.22-bk42/drivers/usb/host/ehci-q.c --- linux-2.4.22-bk41/drivers/usb/host/ehci-q.c 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/host/ehci-q.c 2003-10-26 02:49:55.000000000 -0800 @@ -189,7 +189,7 @@ struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; /* S-mask in a QH means it's an interrupt urb */ - if ((qh->hw_info2 & cpu_to_le32 (0x00ff)) != 0) { + if ((qh->hw_info2 & __constant_cpu_to_le32 (0x00ff)) != 0) { /* ... update hc-wide periodic stats (for usbfs) */ hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--; @@ -257,7 +257,7 @@ * Chases up to qh->hw_current. Returns number of completions called, * indicating how much "real" work we did. */ -#define HALT_BIT cpu_to_le32(QTD_STS_HALT) +#define HALT_BIT __constant_cpu_to_le32(QTD_STS_HALT) static unsigned qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs) { @@ -410,10 +410,14 @@ return count; } -#undef HALT_BIT /*-------------------------------------------------------------------------*/ +// high bandwidth multiplier, as encoded in highspeed endpoint descriptors +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +// ... and packet size, for any kind of endpoint descriptor +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) + /* * reverse of qh_urb_transaction: free a list of TDs. * used for cleanup after errors, before HC sees an URB's TDs. @@ -494,7 +498,7 @@ token |= (1 /* "in" */ << 8); /* else it's already initted to "out" pid (0 << 8) */ - maxpacket = usb_maxpacket (urb->dev, urb->pipe, !is_input) & 0x03ff; + maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input)); /* * buffer gets wrapped in one or more qtds; @@ -597,11 +601,6 @@ // That'd mean updating how usbcore talks to HCDs. (2.5?) -// high bandwidth multiplier, as encoded in highspeed endpoint descriptors -#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) -// ... and packet size, for any kind of endpoint descriptor -#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x03ff) - /* * Each QH holds a qtd list; a QH is used for everything except iso. * @@ -766,7 +765,7 @@ } } - qh->hw_token &= ~__constant_cpu_to_le32 (QTD_STS_HALT); + qh->hw_token &= ~HALT_BIT; /* splice right after start */ qh->qh_next = head->qh_next; @@ -782,6 +781,8 @@ /*-------------------------------------------------------------------------*/ +#define QH_ADDR_MASK __constant_le32_to_cpu(0x7f) + /* * For control/bulk/interrupt, return QH with these TDs appended. * Allocates and initializes the QH if necessary. @@ -816,12 +817,13 @@ /* control qh may need patching after enumeration */ if (unlikely (epnum == 0)) { /* set_address changes the address */ - if (le32_to_cpu (qh->hw_info1 & 0x7f) == 0) + if ((qh->hw_info1 & QH_ADDR_MASK) == 0) qh->hw_info1 |= cpu_to_le32 ( usb_pipedevice (urb->pipe)); /* for full speed, ep0 maxpacket can grow */ - else if (!(qh->hw_info1 & cpu_to_le32 (0x3 << 12))) { + else if (!(qh->hw_info1 + & __constant_cpu_to_le32 (0x3 << 12))) { u32 info, max; info = le32_to_cpu (qh->hw_info1); @@ -835,7 +837,7 @@ /* usb_reset_device() briefly reverts to address 0 */ if (usb_pipedevice (urb->pipe) == 0) - qh->hw_info1 &= cpu_to_le32(~0x7f); + qh->hw_info1 &= ~QH_ADDR_MASK; } /* NOTE: changing config or interface setting is not @@ -875,7 +877,7 @@ * HC is allowed to fetch the old dummy (4.10.2). */ token = qtd->hw_token; - qtd->hw_token = cpu_to_le32 (QTD_STS_HALT); + qtd->hw_token = HALT_BIT; wmb (); dummy = qh->dummy; diff -urN linux-2.4.22-bk41/drivers/usb/host/ehci.h linux-2.4.22-bk42/drivers/usb/host/ehci.h --- linux-2.4.22-bk41/drivers/usb/host/ehci.h 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.22-bk42/drivers/usb/host/ehci.h 2003-10-26 02:49:55.000000000 -0800 @@ -325,6 +325,7 @@ struct ehci_itd *itd; /* Q_TYPE_ITD */ struct ehci_sitd *sitd; /* Q_TYPE_SITD */ struct ehci_fstn *fstn; /* Q_TYPE_FSTN */ + u32 *hw_next; /* (all types) */ void *ptr; }; diff -urN linux-2.4.22-bk41/drivers/usb/host/hc_simple.c linux-2.4.22-bk42/drivers/usb/host/hc_simple.c --- linux-2.4.22-bk41/drivers/usb/host/hc_simple.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/host/hc_simple.c 2003-10-26 02:49:55.000000000 -0800 @@ -0,0 +1,1089 @@ +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------* + * simple generic USB HCD frontend Version 0.9.5 (10/28/2001) + * for embedded HCs (SL811HS) + * + * USB URB handling, hci_ hcs_ + * URB queueing, qu_ + * Transfer scheduling, sh_ + * + * + *-------------------------------------------------------------------------* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *-------------------------------------------------------------------------*/ + +/* main lock for urb access */ +static spinlock_t usb_urb_lock = SPIN_LOCK_UNLOCKED; + +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ +/* URB HCD API function layer + * * * */ + +/*************************************************************************** + * Function Name : hcs_urb_queue + * + * This function initializes the urb status and length before queueing the + * urb. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * + * Return: 0 + **************************************************************************/ +static inline int hcs_urb_queue (hci_t * hci, struct urb * urb) +{ + int i; + + DBGFUNC ("enter hcs_urb_queue\n"); + if (usb_pipeisoc (urb->pipe)) { + DBGVERBOSE ("hcs_urb_queue: isoc pipe\n"); + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = -EXDEV; + } + + /* urb->next hack : 1 .. resub, 0 .. single shot */ + /* urb->interval = urb->next ? 1 : 0; */ + } + + urb->status = -EINPROGRESS; + urb->actual_length = 0; + urb->error_count = 0; + + if (usb_pipecontrol (urb->pipe)) + hc_flush_data_cache (hci, urb->setup_packet, 8); + if (usb_pipeout (urb->pipe)) + hc_flush_data_cache (hci, urb->transfer_buffer, + urb->transfer_buffer_length); + + qu_queue_urb (hci, urb); + + return 0; +} + +/*************************************************************************** + * Function Name : hcs_return_urb + * + * This function the return path of URB back to the USB core. It calls the + * the urb complete function if exist, and also handles the resubmition of + * interrupt URBs. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * resub_ok = resubmit flag: 1 = submit urb again, 0 = not submit + * + * Return: 0 + **************************************************************************/ +static int hcs_return_urb (hci_t * hci, struct urb * urb, int resub_ok) +{ + struct usb_device *dev = urb->dev; + int resubmit = 0; + + DBGFUNC ("enter hcs_return_urb, urb pointer = %p, " + "transferbuffer point = %p, " + " setup packet pointer = %p, context pointer = %p \n", + (__u32 *) urb, (__u32 *) urb->transfer_buffer, + (__u32 *) urb->setup_packet, (__u32 *) urb->context); + if (urb_debug) + urb_print (urb, "RET", usb_pipeout (urb->pipe)); + + resubmit = urb->interval && resub_ok; + + urb->dev = urb->hcpriv = NULL; + + if (urb->complete) { + urb->complete (urb); /* call complete */ + } + + if (resubmit) { + /* requeue the URB */ + urb->dev = dev; + hcs_urb_queue (hci, urb); + } + + return 0; +} + +/*************************************************************************** + * Function Name : hci_submit_urb + * + * This function is called by the USB core API when an URB is available to + * process. This function does the following + * + * 1) Check the validity of the URB + * 2) Parse the device number from the URB + * 3) Pass the URB to the root hub routine if its intended for the hub, else + * queue the urb for the attached device. + * + * Input: urb = USB request block data structure + * + * Return: 0 if success or error code + **************************************************************************/ +static int hci_submit_urb (struct urb * urb) +{ + hci_t *hci; + unsigned int pipe = urb->pipe; + unsigned long flags; + int ret; + + DBGFUNC ("enter hci_submit_urb, pipe = 0x%x\n", urb->pipe); + 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 */ + + if (usb_pipedevice (pipe) == hci->rh.devnum) { + if (urb_debug > 1) + urb_print (urb, "SUB-RH", usb_pipein (pipe)); + + return rh_submit_urb (urb); + } + + /* queue the URB to its endpoint-queue */ + + spin_lock_irqsave (&usb_urb_lock, flags); + ret = hcs_urb_queue (hci, urb); + if (ret != 0) { + /* error on return */ + DBGERR + ("hci_submit_urb: return err, ret = 0x%x, urb->status = 0x%x\n", + ret, urb->status); + } + + spin_unlock_irqrestore (&usb_urb_lock, flags); + + return ret; + +} + +/*************************************************************************** + * Function Name : hci_unlink_urb + * + * This function mark the URB to unlink + * + * Input: urb = USB request block data structure + * + * Return: 0 if success or error code + **************************************************************************/ +static int hci_unlink_urb (struct urb * urb) +{ + unsigned long flags; + hci_t *hci; + DECLARE_WAITQUEUE (wait, current); + void *comp = NULL; + + DBGFUNC ("enter hci_unlink_urb\n"); + + if (!urb) /* just to be sure */ + return -EINVAL; + + if (!urb->dev || !urb->dev->bus) + return -ENODEV; + + hci = (hci_t *) urb->dev->bus->hcpriv; + + /* a request to the virtual root hub */ + if (usb_pipedevice (urb->pipe) == hci->rh.devnum) { + return rh_unlink_urb (urb); + } + + if (urb_debug) + urb_print (urb, "UNLINK", 1); + + spin_lock_irqsave (&usb_urb_lock, flags); + + if (!list_empty (&urb->urb_list) && urb->status == -EINPROGRESS) { + /* URB active? */ + + if (urb-> + transfer_flags & (USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED)) { + /* asynchron with callback */ + + list_del (&urb->urb_list); /* relink the urb to the del list */ + list_add (&urb->urb_list, &hci->del_list); + spin_unlock_irqrestore (&usb_urb_lock, flags); + + } else { + /* synchron without callback */ + + add_wait_queue (&hci->waitq, &wait); + + set_current_state (TASK_UNINTERRUPTIBLE); + comp = urb->complete; + urb->complete = NULL; + +/* --> crash --> */ list_del (&urb->urb_list); /* relink the urb to the del list */ + list_add (&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); + } + } 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 & USB_ASYNC_UNLINK)) { + urb->status = -ENOENT; + urb->actual_length = 0; + urb->complete (urb); + urb->status = 0; + } else { + urb->status = -ENOENT; + } + } + + return 0; +} + +/*************************************************************************** + * Function Name : hci_alloc_dev + * + * This function allocates private data space for the usb device and + * initialize the endpoint descriptor heads. + * + * Input: usb_dev = pointer to the usb device + * + * Return: 0 if success or error code + **************************************************************************/ +static int hci_alloc_dev (struct usb_device *usb_dev) +{ + struct hci_device *dev; + int i; + + DBGFUNC ("enter hci_alloc_dev\n"); + dev = kmalloc (sizeof (*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + memset (dev, 0, sizeof (*dev)); + + for (i = 0; i < 32; i++) { + INIT_LIST_HEAD (&(dev->ed[i].urb_queue)); + dev->ed[i].pipe_head = NULL; + } + + usb_dev->hcpriv = dev; + + DBGVERBOSE ("USB HC dev alloc %d bytes\n", sizeof (*dev)); + + return 0; + +} + +/*************************************************************************** + * Function Name : hci_free_dev + * + * This function de-allocates private data space for the usb devic + * + * Input: usb_dev = pointer to the usb device + * + * Return: 0 + **************************************************************************/ +static int hci_free_dev (struct usb_device *usb_dev) +{ + DBGFUNC ("enter hci_free_dev\n"); + + if (usb_dev->hcpriv) + kfree (usb_dev->hcpriv); + + usb_dev->hcpriv = NULL; + + return 0; +} + +/*************************************************************************** + * Function Name : hci_get_current_frame_number + * + * This function get the current USB frame number + * + * Input: usb_dev = pointer to the usb device + * + * Return: frame number + **************************************************************************/ +static int hci_get_current_frame_number (struct usb_device *usb_dev) +{ + hci_t *hci = usb_dev->bus->hcpriv; + DBGFUNC ("enter hci_get_current_frame_number, frame = 0x%x \r\n", + hci->frame_number); + + return (hci->frame_number); +} + +/*************************************************************************** + * List of all io-functions + **************************************************************************/ + +static struct usb_operations hci_device_operations = { + allocate: hci_alloc_dev, + deallocate: hci_free_dev, + get_frame_number: hci_get_current_frame_number, + submit_urb: hci_submit_urb, + unlink_urb: hci_unlink_urb, +}; + +/*************************************************************************** + * URB queueing: + * + * For each type of transfer (INTR, BULK, ISO, CTRL) there is a list of + * active URBs. + * (hci->intr_list, hci->bulk_list, hci->iso_list, hci->ctrl_list) + * For every endpoint the head URB of the queued URBs is linked to one of + * those lists. + * + * The rest of the queued URBs of an endpoint are linked into a + * private URB list for each endpoint. (hci_dev->ed [endpoint_io].urb_queue) + * hci_dev->ed [endpoint_io].pipe_head .. points to the head URB which is + * in one of the active URB lists. + * + * The index of an endpoint consists of its number and its direction. + * + * The state of an intr and iso URB is 0. + * For ctrl URBs the states are US_CTRL_SETUP, US_CTRL_DATA, US_CTRL_ACK + * Bulk URBs states are US_BULK and US_BULK0 (with 0-len packet) + * + **************************************************************************/ + +/*************************************************************************** + * Function Name : qu_urb_timeout + * + * This function is called when the URB timeout. The function unlinks the + * URB. + * + * Input: lurb: URB + * + * Return: none + **************************************************************************/ +#ifdef HC_URB_TIMEOUT +static void qu_urb_timeout (unsigned long lurb) +{ + struct urb *urb = (struct urb *) lurb; + + DBGFUNC ("enter qu_urb_timeout\n"); + urb->transfer_flags |= USB_TIMEOUT_KILLED; + hci_unlink_urb (urb); +} +#endif + +/*************************************************************************** + * Function Name : qu_pipeindex + * + * This function gets the index of the pipe. + * + * Input: pipe: the urb pipe + * + * Return: index + **************************************************************************/ +static inline int qu_pipeindex (__u32 pipe) +{ + DBGFUNC ("enter qu_pipeindex\n"); + return (usb_pipeendpoint (pipe) << 1) | (usb_pipecontrol (pipe) ? 0 : usb_pipeout (pipe)); +} + +/*************************************************************************** + * Function Name : qu_seturbstate + * + * This function set the state of the URB. + * + * control pipe: 3 states -- Setup, data, status + * interrupt and bulk pipe: 1 state -- data + * + * Input: urb = USB request block data structure + * state = the urb state + * + * Return: none + **************************************************************************/ +static inline void qu_seturbstate (struct urb * urb, int state) +{ + DBGFUNC ("enter qu_seturbstate\n"); + urb->pipe &= ~0x1f; + urb->pipe |= state & 0x1f; +} + +/*************************************************************************** + * Function Name : qu_urbstate + * + * This function get the current state of the URB. + * + * Input: urb = USB request block data structure + * + * Return: none + **************************************************************************/ +static inline int qu_urbstate (struct urb * urb) +{ + + DBGFUNC ("enter qu_urbstate\n"); + + return urb->pipe & 0x1f; +} + +/*************************************************************************** + * Function Name : qu_queue_active_urb + * + * This function adds the urb to the appropriate active urb list and set + * the urb state. + * + * There are four active lists: isochoronous list, interrupt list, + * control list, and bulk list. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * ed = endpoint descriptor + * + * Return: none + **************************************************************************/ +static inline void qu_queue_active_urb (hci_t * hci, struct urb * urb, epd_t * ed) +{ + int urb_state = 0; + DBGFUNC ("enter qu_queue_active_urb\n"); + switch (usb_pipetype (urb->pipe)) { + case PIPE_CONTROL: + list_add (&urb->urb_list, &hci->ctrl_list); + urb_state = US_CTRL_SETUP; + break; + + case PIPE_BULK: + list_add (&urb->urb_list, &hci->bulk_list); + if ((urb->transfer_flags & USB_ZERO_PACKET) + && urb->transfer_buffer_length > 0 + && + ((urb->transfer_buffer_length % + usb_maxpacket (urb->dev, urb->pipe, + usb_pipeout (urb->pipe))) == 0)) { + urb_state = US_BULK0; + } + break; + + case PIPE_INTERRUPT: + urb->start_frame = hci->frame_number; + list_add (&urb->urb_list, &hci->intr_list); + break; + + case PIPE_ISOCHRONOUS: + list_add (&urb->urb_list, &hci->iso_list); + break; + } + +#ifdef HC_URB_TIMEOUT + if (urb->timeout) { + ed->timeout.data = (unsigned long) urb; + ed->timeout.expires = urb->timeout + jiffies; + ed->timeout.function = qu_urb_timeout; + add_timer (&ed->timeout); + } +#endif + + qu_seturbstate (urb, urb_state); +} + +/*************************************************************************** + * Function Name : qu_queue_urb + * + * This function adds the urb to the endpoint descriptor list + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * + * Return: none + **************************************************************************/ +static int qu_queue_urb (hci_t * hci, struct urb * urb) +{ + struct hci_device *hci_dev = usb_to_hci (urb->dev); + epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)]; + + DBGFUNC ("Enter qu_queue_urb\n"); + + /* for ISOC transfers calculate start frame index */ + + if (usb_pipeisoc (urb->pipe) && urb->transfer_flags & USB_ISO_ASAP) { + urb->start_frame = ((ed->pipe_head) ? (ed->last_iso + 1) : hci_get_current_frame_number (urb-> dev) + 1) & 0xffff; + } + + if (ed->pipe_head) { + __list_add (&urb->urb_list, ed->urb_queue.prev, + &(ed->urb_queue)); + } else { + ed->pipe_head = urb; + qu_queue_active_urb (hci, urb, ed); + if (++hci->active_urbs == 1) + hc_start_int (hci); + } + + return 0; +} + +/*************************************************************************** + * Function Name : qu_next_urb + * + * This function removes the URB from the queue and add the next URB to + * active list. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * resub_ok = resubmit flag + * + * Return: pointer to the next urb + **************************************************************************/ +static struct urb *qu_next_urb (hci_t * hci, struct urb * urb, int resub_ok) +{ + struct hci_device *hci_dev = usb_to_hci (urb->dev); + epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)]; + + DBGFUNC ("enter qu_next_urb\n"); + list_del (&urb->urb_list); + INIT_LIST_HEAD (&urb->urb_list); + if (ed->pipe_head == urb) { + +#ifdef HC_URB_TIMEOUT + if (urb->timeout) + del_timer (&ed->timeout); +#endif + + if (!--hci->active_urbs) + hc_stop_int (hci); + + if (!list_empty (&ed->urb_queue)) { + urb = list_entry (ed->urb_queue.next, struct urb, urb_list); + list_del (&urb->urb_list); + INIT_LIST_HEAD (&urb->urb_list); + ed->pipe_head = urb; + qu_queue_active_urb (hci, urb, ed); + } else { + ed->pipe_head = NULL; + urb = NULL; + } + } + return urb; +} + +/*************************************************************************** + * Function Name : qu_return_urb + * + * This function is part of the return path. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * resub_ok = resubmit flag + * + * Return: pointer to the next urb + **************************************************************************/ +static struct urb *qu_return_urb (hci_t * hci, struct urb * urb, int resub_ok) +{ + struct urb *next_urb; + + DBGFUNC ("enter qu_return_rub\n"); + next_urb = qu_next_urb (hci, urb, resub_ok); + hcs_return_urb (hci, urb, resub_ok); + return next_urb; +} + +#if 0 /* unused now (hne) */ +/*************************************************************************** + * Function Name : sh_scan_iso_urb_list + * + * This function goes throught the isochronous urb list and schedule the + * the transfer. + * + * Note: This function has not tested yet + * + * Input: hci = data structure for the host controller + * list_lh = pointer to the isochronous list + * frame_number = the frame number + * + * Return: 0 = unsuccessful; 1 = successful + **************************************************************************/ +static int sh_scan_iso_urb_list (hci_t * hci, struct list_head *list_lh, + int frame_number) +{ + struct list_head *lh = list_lh->next; + struct urb *urb; + + DBGFUNC ("enter sh_scan_iso_urb_list\n"); + hci->td_array->len = 0; + + while (lh != list_lh) { + urb = list_entry (lh, struct urb, urb_list); + lh = lh->next; + if (((frame_number - urb->start_frame) & 0x7ff) < + urb->number_of_packets) { + if (!sh_add_packet (hci, urb)) { + return 0; + } else { + if (((frame_number - + urb->start_frame) & 0x7ff) > 0x400) { + if (qu_urbstate (urb) > 0) + urb = qu_return_urb (hci, urb, 1); + else + urb = qu_next_urb (hci, urb, 1); + + if (lh == list_lh && urb) + lh = &urb->urb_list; + } + } + } + } + return 1; +} +#endif // if0 + +/*************************************************************************** + * Function Name : sh_scan_urb_list + * + * This function goes through the urb list and schedule the + * the transaction. + * + * Input: hci = data structure for the host controller + * list_lh = pointer to the isochronous list + * + * Return: 0 = unsuccessful; 1 = successful + **************************************************************************/ +static int sh_scan_urb_list (hci_t * hci, struct list_head *list_lh) +{ + struct list_head *lh = NULL; + struct urb *urb; + + if (list_lh == NULL) { + DBGERR ("sh_scan_urb_list: error, list_lh == NULL\n"); + } + + DBGFUNC ("enter sh_scan_urb_list: frame# \n"); + + list_for_each (lh, list_lh) { + urb = list_entry (lh, struct urb, urb_list); + if (urb == NULL) + return 1; + if (!usb_pipeint (urb->pipe) + || (((hci->frame_number - urb->start_frame) + & 0x7ff) >= urb->interval)) { + DBGVERBOSE ("sh_scan_urb_list !INT: %d fr_no: %d int: %d pint: %d\n", + urb->start_frame, hci->frame_number, urb->interval, + usb_pipeint (urb->pipe)); + if (!sh_add_packet (hci, urb)) { + return 0; + } else { + DBGVERBOSE ("INT: start: %d fr_no: %d int: %d pint: %d\n", + urb->start_frame, hci->frame_number, + urb->interval, usb_pipeint (urb->pipe)); + urb->start_frame = hci->frame_number; + return 0; + + } + } + } + return 1; +} + +/*************************************************************************** + * Function Name : sh_shedule_trans + * + * This function schedule the USB transaction. + * This function will process the endpoint in the following order: + * interrupt, control, and bulk. + * + * Input: hci = data structure for the host controller + * isSOF = flag indicate if Start Of Frame has occurred + * + * Return: 0 + **************************************************************************/ +static int sh_schedule_trans (hci_t * hci, int isSOF) +{ + int units_left = 1; + struct list_head *lh; + + if (hci == NULL) { + DBGERR ("sh_schedule_trans: hci == NULL\n"); + return 0; + } + if (hci->td_array == NULL) { + DBGERR ("sh_schedule_trans: hci->td_array == NULL\n"); + return 0; + } + + if (hci->td_array->len != 0) { + DBGERR ("ERROR: schedule, hci->td_array->len = 0x%x, s/b: 0\n", + hci->td_array->len); + } + + /* schedule the next available interrupt transfer or the next + * stage of the interrupt transfer */ + + if (hci->td_array->len == 0 && !list_empty (&hci->intr_list)) { + units_left = sh_scan_urb_list (hci, &hci->intr_list); + } + + /* schedule the next available control transfer or the next + * stage of the control transfer */ + + if (hci->td_array->len == 0 && !list_empty (&hci->ctrl_list) && units_left > 0) { + units_left = sh_scan_urb_list (hci, &hci->ctrl_list); + } + + /* schedule the next available bulk transfer or the next + * stage of the bulk transfer */ + + if (hci->td_array->len == 0 && !list_empty (&hci->bulk_list) && units_left > 0) { + sh_scan_urb_list (hci, &hci->bulk_list); + + /* be fair to each BULK URB (move list head around) + * only when the new SOF happens */ + + lh = hci->bulk_list.next; + list_del (&hci->bulk_list); + list_add (&hci->bulk_list, lh); + } + return 0; +} + +/*************************************************************************** + * Function Name : sh_add_packet + * + * This function forms the packet and transmit the packet. This function + * will handle all endpoint type: isochoronus, interrupt, control, and + * bulk. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * + * Return: 0 = unsucessful; 1 = successful + **************************************************************************/ +static int sh_add_packet (hci_t * hci, struct urb * urb) +{ + __u8 *data = NULL; + int len = 0; + int toggle = 0; + int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); + int endpoint = usb_pipeendpoint (urb->pipe); + int address = usb_pipedevice (urb->pipe); + int slow = (((urb->pipe) >> 26) & 1); + int out = usb_pipeout (urb->pipe); + int pid = 0; + int ret; + int i = 0; + int iso = 0; + + DBGFUNC ("enter sh_add_packet\n"); + if (maxps == 0) + maxps = 8; + + /* calculate len, toggle bit and add the transaction */ + switch (usb_pipetype (urb->pipe)) { + case PIPE_ISOCHRONOUS: + pid = out ? PID_OUT : PID_IN; + iso = 1; + i = hci->frame_number - urb->start_frame; + data = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + len = urb->iso_frame_desc[i].length; + break; + + case PIPE_BULK: /* BULK and BULK0 */ + case PIPE_INTERRUPT: + pid = out ? PID_OUT : PID_IN; + len = urb->transfer_buffer_length - urb->actual_length; + data = urb->transfer_buffer + urb->actual_length; + toggle = usb_gettoggle (urb->dev, endpoint, out); + break; + + case PIPE_CONTROL: + switch (qu_urbstate (urb)) { + case US_CTRL_SETUP: + len = 8; + pid = PID_SETUP; + data = urb->setup_packet; + toggle = 0; + break; + + case US_CTRL_DATA: + if (!hci->last_packet_nak) { + /* The last packet received is not a nak: + * reset the nak count + */ + + hci->nakCnt = 0; + } + if (urb->transfer_buffer_length != 0) { + pid = out ? PID_OUT : PID_IN; + len = urb->transfer_buffer_length - urb->actual_length; + data = urb->transfer_buffer + urb->actual_length; + toggle = (urb->actual_length & maxps) ? 0 : 1; + usb_settoggle (urb->dev, + usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe), toggle); + break; + } else { + /* correct state and fall through */ + qu_seturbstate (urb, US_CTRL_ACK); + } + + case US_CTRL_ACK: + len = 0; + + /* reply in opposite direction */ + pid = !out ? PID_OUT : PID_IN; + toggle = 1; + usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe), toggle); + break; + } + } + + ret = + hc_add_trans (hci, len, data, toggle, maxps, slow, endpoint, + address, pid, iso, qu_urbstate (urb)); + + DBGVERBOSE ("transfer_pa: addr:%d ep:%d pid:%x tog:%x iso:%x sl:%x " + "max:%d\n len:%d ret:%d data:%p left:%d\n", + address, endpoint, pid, toggle, iso, slow, + maxps, len, ret, data, hci->hp.units_left); + + if (ret >= 0) { + hci->td_array->td[hci->td_array->len].urb = urb; + hci->td_array->td[hci->td_array->len].len = ret; + hci->td_array->td[hci->td_array->len].iso_index = i; + hci->td_array->len++; + hci->active_trans = 1; + return 1; + } + return 0; +} + +/*************************************************************************** + * Function Name : cc_to_error + * + * This function maps the SL811HS hardware error code to the linux USB error + * code. + * + * Input: cc = hardware error code + * + * Return: USB error code + **************************************************************************/ +static int cc_to_error (int cc) +{ + int errCode = 0; + if (cc & SL11H_STATMASK_ERROR) { + errCode |= -EILSEQ; + } else if (cc & SL11H_STATMASK_OVF) { + errCode |= -EOVERFLOW; + } else if (cc & SL11H_STATMASK_STALL) { + errCode |= -EPIPE; + } + return errCode; +} + +/*************************************************************************** + * Function Name : sh_done_list + * + * This function process the packet when it has done finish transfer. + * + * 1) It handles hardware error + * 2) It updates the URB state + * 3) If the USB transaction is complete, it start the return stack path. + * + * Input: hci = data structure for the host controller + * isExcessNak = flag tells if there excess NAK condition occurred + * + * Return: urb_state or -1 if the transaction has complete + **************************************************************************/ +static int sh_done_list (hci_t * hci, int *isExcessNak) +{ + int actbytes = 0; + int active = 0; + void *data = NULL; + int cc; + int maxps; + int toggle; + struct urb *urb; + int urb_state = 0; + int ret = 1; /* -1 parse abbort, 1 parse ok, 0 last element */ + int trans = 0; + int len; + int iso_index = 0; + int out; + int pid = 0; + int debugLen = 0; + + *isExcessNak = 0; + + DBGFUNC ("enter sh_done_list: td_array->len = 0x%x\n", + hci->td_array->len); + + debugLen = hci->td_array->len; + if (debugLen > 1) + DBGERR ("sh_done_list: td_array->len = 0x%x > 1\n", + hci->td_array->len); + + for (trans = 0; ret && trans < hci->td_array->len && trans < MAX_TRANS; + trans++) { + urb = hci->td_array->td[trans].urb; + /* FIXME: */ + /* +++ I'm sorry, can't handle NULL-Pointers 21.11.2002 (hne) */ + if (!urb) { + DBGERR ("sh_done_list: urb = NULL\n"); + continue; + } + if (!urb->dev || !urb->pipe) { + if (!urb->dev) DBGERR ("sh_done_list: urb->dev = NULL\n"); + if (!urb->pipe) DBGERR ("sh_done_list: urb->pipe = NULL\n"); + continue; + } + /* --- 21.11.2002 (hne) */ + + len = hci->td_array->td[trans].len; + out = usb_pipeout (urb->pipe); + + if (usb_pipeisoc (urb->pipe)) { + iso_index = hci->td_array->td[trans].iso_index; + data = urb->transfer_buffer + urb->iso_frame_desc[iso_index].offset; + toggle = 0; + } else { + data = urb->transfer_buffer + urb->actual_length; + /* +++ Crash (hne) urb->dev == NULL !!! */ + toggle = usb_gettoggle (urb->dev, + usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe)); + /* --- Crash (hne) urb->dev == NULL !!! */ + + } + urb_state = qu_urbstate (urb); + pid = out ? PID_OUT : PID_IN; + ret = hc_parse_trans (hci, &actbytes, data, &cc, &toggle, len, + pid, urb_state); + maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); + + if (maxps == 0) + maxps = 8; + + active = (urb_state != US_CTRL_SETUP) && (actbytes && !(actbytes & (maxps - 1))); + + /* If the transfer is not bulk in, then it is necessary to get all + * data specify by the urb->transfer_len. + */ + + if (!(usb_pipebulk (urb->pipe) && usb_pipein (urb->pipe))) + active = active && (urb->transfer_buffer_length != urb->actual_length + actbytes); + + if (urb->transfer_buffer_length == urb->actual_length + actbytes) + active = 0; + + if ((cc & + (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | + SL11H_STATMASK_OVF | SL11H_STATMASK_STALL)) + && !(cc & SL11H_STATMASK_NAK)) { + if (++urb->error_count > 3) { + DBGERR ("done_list: excessive error: errcount = 0x%x, cc = 0x%x\n", + urb->error_count, cc); + urb_state = 0; + active = 0; + } else { + DBGERR ("done_list: packet err, cc=0x%x, " + " urb->length=0x%x, actual_len=0x%x," + " urb_state=0x%x\n", + cc, urb->transfer_buffer_length, + urb->actual_length, urb_state); +// if (cc & SL11H_STATMASK_STALL) { + /* The USB function is STALLED on a control pipe (0), + * then it needs to send the SETUP command again to + * clear the STALL condition + */ + +// if (usb_pipeendpoint (urb->pipe) == 0) { +// urb_state = 2; +// active = 0; +// } +// } else + active = 1; + } + } else { + if (cc & SL11H_STATMASK_NAK) { + if (hci->nakCnt < 0x10000) { + hci->nakCnt++; + hci->last_packet_nak = 1; + active = 1; + *isExcessNak = 0; + } else { + DBGERR ("done_list: nak count exceed limit\n"); + active = 0; + *isExcessNak = 1; + hci->nakCnt = 0; + } + } else { + hci->nakCnt = 0; + hci->last_packet_nak = 0; + } + + if (urb_state != US_CTRL_SETUP) { + /* no error */ + urb->actual_length += actbytes; + usb_settoggle (urb->dev, + usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe), toggle); + } + if (usb_pipeisoc (urb->pipe)) { + urb->iso_frame_desc[iso_index].actual_length = actbytes; + urb->iso_frame_desc[iso_index].status = cc_to_error (cc); + active = (iso_index < urb->number_of_packets); + } + } + if (!active) { + if (!urb_state) { + urb->status = cc_to_error (cc); + if (urb->status) { + DBGERR ("error on received packet: urb->status = 0x%x\n", + urb->status); + } + hci->td_array->len = 0; + qu_return_urb (hci, urb, 1); + return -1; + } else { + /* We do not want to decrement the urb_state if exceeded nak, + * because we need to finish the data stage of the control + * packet + */ + + if (!(*isExcessNak)) + urb_state--; + qu_seturbstate (urb, urb_state); + } + } + } + + if (urb_state < 0) + DBGERR ("ERROR: done_list, urb_state = %d, suppose > 0\n", + urb_state); + if (debugLen != hci->td_array->len) { + DBGERR ("ERROR: done_list, debugLen!= td_array->len," + "debugLen = 0x%x, hci->td_array->len = 0x%x\n", + debugLen, hci->td_array->len); + } + + hci->td_array->len = 0; + + return urb_state; +} diff -urN linux-2.4.22-bk41/drivers/usb/host/hc_simple.h linux-2.4.22-bk42/drivers/usb/host/hc_simple.h --- linux-2.4.22-bk41/drivers/usb/host/hc_simple.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/host/hc_simple.h 2003-10-26 02:49:55.000000000 -0800 @@ -0,0 +1,230 @@ +/*-------------------------------------------------------------------------*/ +/* list of all controllers using this driver + * */ + +static LIST_HEAD (hci_hcd_list); + +/* URB states (urb_state) */ +/* isoc, interrupt single state */ + +/* bulk transfer main state and 0-length packet */ +#define US_BULK 0 +#define US_BULK0 1 +/* three setup states */ +#define US_CTRL_SETUP 2 +#define US_CTRL_DATA 1 +#define US_CTRL_ACK 0 + +/*-------------------------------------------------------------------------*/ +/* HC private part of a device descriptor + * */ + +#define NUM_EDS 32 + +typedef struct epd { + struct urb *pipe_head; + struct list_head urb_queue; +// int urb_state; + struct timer_list timeout; + int last_iso; /* timestamp of last queued ISOC transfer */ + +} epd_t; + +struct hci_device { + epd_t ed[NUM_EDS]; +}; + +/*-------------------------------------------------------------------------*/ +/* Virtual Root HUB + */ + +#define usb_to_hci(usb) ((struct hci_device *)(usb)->hcpriv) + +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *urb; /* interrupt URB of root hub */ + int send; /* active flag */ + int interval; /* intervall of roothub interrupt transfers */ + struct timer_list rh_int_timer; /* intervall timer for rh interrupt EP */ +}; + +#if 1 +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h and USB spec) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#endif + +/*-------------------------------------------------------------------------*/ +/* struct for each HC + */ + +#define MAX_TRANS 32 + +typedef struct td { + struct urb *urb; + __u16 len; + __u16 iso_index; +} td_t; + +typedef struct td_array { + int len; + td_t td[MAX_TRANS]; +} td_array_t; + +typedef struct hci { + struct virt_root_hub rh; /* roothub */ + wait_queue_head_t waitq; /* deletion of URBs and devices needs a waitqueue */ + int active; /* HC is operating */ + + struct list_head ctrl_list; /* set of ctrl endpoints */ + struct list_head bulk_list; /* set of bulk endpoints */ + struct list_head iso_list; /* set of isoc endpoints */ + struct list_head intr_list; /* ordered (tree) set of int endpoints */ + struct list_head del_list; /* set of entpoints to be deleted */ + + td_array_t *td_array; + td_array_t a_td_array; + td_array_t i_td_array[2]; + + struct list_head hci_hcd_list; /* list of all hci_hcd */ + struct usb_bus *bus; /* our bus */ + +// int trans; /* number of transactions pending */ + int active_urbs; + int active_trans; + int frame_number; /* frame number */ + hcipriv_t hp; /* individual part of hc type */ + int nakCnt; + int last_packet_nak; + +} hci_t; + +/*-------------------------------------------------------------------------*/ +/* condition (error) CC codes and mapping OHCI like + */ + +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +/* urb interface functions */ +static int hci_get_current_frame_number (struct usb_device *usb_dev); +static int hci_unlink_urb (struct urb * urb); + +static int qu_queue_urb (hci_t * hci, struct urb * urb); + +/* root hub */ +static int rh_init_int_timer (struct urb * urb); +static int rh_submit_urb (struct urb * urb); +static int rh_unlink_urb (struct urb * urb); + +/* schedule functions */ +static int sh_add_packet (hci_t * hci, struct urb * urb); + +/* hc specific functions */ +static inline void hc_flush_data_cache (hci_t * hci, void *data, int len); +static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data, + int *cc, int *toggle, int length, int pid, + int urb_state); +static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle, + int maxps, int slow, int endpoint, int address, + int pid, int format, int urb_state); + +static void hc_start_int (hci_t * hci); +static void hc_stop_int (hci_t * hci); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void urb_print (struct urb * urb, char *str, int small) +{ + unsigned int pipe = urb->pipe; + int i, len; + + if (!urb->dev || !urb->dev->bus) { + dbg ("%s URB: no dev", str); + return; + } + + printk ("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)\n", + str, hci_get_current_frame_number (urb->dev), + usb_pipedevice (pipe), usb_pipeendpoint (pipe), + usb_pipeout (pipe) ? 'O' : 'I', + usb_pipetype (pipe) < 2 ? (usb_pipeint (pipe) ? "INTR" : "ISOC") + : (usb_pipecontrol (pipe) ? "CTRL" : "BULK"), urb->transfer_flags, + urb->actual_length, urb->transfer_buffer_length, urb->status, + urb->status); + if (!small) { + if (usb_pipecontrol (pipe)) { + printk (__FILE__ ": cmd(8):"); + for (i = 0; i < 8; i++) + printk (" %02x", ((__u8 *) urb->setup_packet)[i]); + printk ("\n"); + } + if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { + printk (__FILE__ ": data(%d/%d):", urb->actual_length, + urb->transfer_buffer_length); + len = usb_pipeout (pipe) ? urb-> transfer_buffer_length : urb->actual_length; + for (i = 0; i < 2096 && i < len; i++) + printk (" %02x", ((__u8 *) urb->transfer_buffer)[i]); + printk ("%s stat:%d\n", i < len ? "..." : "", + urb->status); + } + } +} diff -urN linux-2.4.22-bk41/drivers/usb/host/hc_sl811.c linux-2.4.22-bk42/drivers/usb/host/hc_sl811.c --- linux-2.4.22-bk41/drivers/usb/host/hc_sl811.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/host/hc_sl811.c 2003-10-26 02:49:55.000000000 -0800 @@ -0,0 +1,1484 @@ +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------* + * SL811HS USB HCD for Linux Version 0.1 (10/28/2001) 0.3 (11/23/2003) + * + * requires (includes) hc_simple.[hc] simple generic HCD frontend + * + * COPYRIGHT(C) 2001 by CYPRESS SEMICONDUCTOR INC. + * + * ! This driver have end of live! Please use hcd/sl811.c instand ! + * + * 05.06.2003 HNE + * Support x86 architecture now. + * Set "bus->bus_name" at init. + * hc_reset,regTest: Don't load driver, if pattern-Test failed (better error returns) + * + * 06.06.2003 HNE + * Moved regTest from hc_reset to hc_found_hci. So we check registers + * only at start, and not at unload. Only all Register show, if full Debug. + * + * 22.09.2003 HNE + * Do not write SL11H_INTSTATREG in loop, use delay instand. + * If device disconnected, wait only for next insert interrupt (no Idle-Interrpts). + * + * 29.09.2003 HNE + * Moving hc_sl811-arm.h and hc_sl811-x86.h to include/asm-.../hc_sl811-hw.h + * + * 03.10.2003 HNE + * Low level only for port io into hardware-include. + * GPRD as parameter (ARM only). + + * ToDo: + * Separate IO-Base for second controller (see sl811.c) + * Only as module tested! Compiled in Version not tested! + * + *-------------------------------------------------------------------------* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *-------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MODNAME "HC_SL811" + +#undef HC_URB_TIMEOUT +#undef HC_SWITCH_INT +#undef HC_ENABLE_ISOC + +// #define SL811_DEBUG +#define SL811_DEBUG_ERR +// #define SL811_DEBUG_IRQ +// #define SL811_DEBUG_VERBOSE + +#ifdef SL811_DEBUG_ERR +#define DBGERR(fmt, args...) printk(fmt,## args) +#else +#define DBGERR(fmt, args...) +#endif + +#ifdef SL811_DEBUG +#define DBG(fmt, args...) printk(fmt,## args) +#else +#define DBG(fmt, args...) +#endif + +#ifdef SL811_DEBUG_FUNC +#define DBGFUNC(fmt, args...) printk(fmt,## args) +#else +#define DBGFUNC(fmt, args...) +#endif + +#ifdef SL811_DEBUG_DATA +#define DBGDATAR(fmt, args...) printk(fmt,## args) +#define DBGDATAW(fmt, args...) printk(fmt,## args) +#else +#define DBGDATAR(fmt, args...) +#define DBGDATAW(fmt, args...) +#endif + +#ifdef SL811_DEBUG_VERBOSE +#define DBGVERBOSE(fmt, args...) printk(fmt,## args) +#else +#define DBGVERBOSE(fmt, args...) +#endif + +#define TRUE 1 +#define FALSE 0 + +#define HC_SWITCH_INT +#include "hc_sl811.h" +#include "hc_simple.h" + +static int urb_debug = 0; + +#include "hc_simple.c" +#include "hc_sl811_rh.c" + +/* Include hardware and board depens */ +#include + +/* forware declaration */ +int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len, + int toggle, int slow, int urb_state); + +static int sofWaitCnt = 0; + +MODULE_PARM (urb_debug, "i"); +MODULE_PARM_DESC (urb_debug, "debug urb messages, default is 0 (no)"); + +static int hc_reset (hci_t * hci); + +/*************************************************************************** + * Function Name : SL811Read + * + * Read a byte of data from the SL811H/SL11H + * + * Input: hci = data structure for the host controller + * offset = address of SL811/SL11H register or memory + * + * Return: data + **************************************************************************/ + +static __u8 inline SL811Read (hci_t *hci, __u8 offset) +{ + hcipriv_t *hp = &hci->hp; +#ifdef SL811_DEBUG_ERR + if (!hp->hcport) + DBGERR ("SL811Read: Error port not set!\n"); +#endif + sl811_write_index (hp, offset); + return (sl811_read_data (hp)); +} + +/*************************************************************************** + * Function Name : SL811Write + * + * Write a byte of data to the SL811H/SL11H + * + * Input: hci = data structure for the host controller + * offset = address of SL811/SL11H register or memory + * data = the data going to write to SL811H + * + * Return: none + **************************************************************************/ + +static void inline SL811Write (hci_t *hci, __u8 offset, __u8 data) +{ + hcipriv_t *hp = &hci->hp; +#ifdef SL811_DEBUG_ERR + if (!hp->hcport) + DBGERR ("SL811Write: Error port not set!\n"); +#endif + sl811_write_index_data (hp, offset, data); +} + + +/*************************************************************************** + * Function Name : SL811BufRead + * + * Read consecutive bytes of data from the SL811H/SL11H buffer + * + * Input: hci = data structure for the host controller + * offset = SL811/SL11H register offset + * buf = the buffer where the data will store + * size = number of bytes to read + * + * Return: none + **************************************************************************/ + +static void SL811BufRead (hci_t *hci, __u8 offset, __u8 *buf, __u8 size) +{ + hcipriv_t *hp = &hci->hp; + if( size <= 0) + return; +#ifdef SL811_DEBUG_ERR + if (!hp->hcport) + DBGERR ("SL811BufRead: Error port not set!\n"); +#endif + sl811_write_index (hp, offset); + DBGDATAR ("SL811BufRead: io=%X offset = %02x, data = ", hp->hcport, offset); + while (size--) { + *buf++ = sl811_read_data(hp); + DBGDATAR ("%02x ", *(buf-1)); + } + DBGDATAR ("\n"); +} + +/*************************************************************************** + * Function Name : SL811BufWrite + * + * Write consecutive bytes of data to the SL811H/SL11H buffer + * + * Input: hci = data structure for the host controller + * offset = SL811/SL11H register offset + * buf = the data buffer + * size = number of bytes to write + * + * Return: none + **************************************************************************/ + +static void SL811BufWrite(hci_t *hci, __u8 offset, __u8 *buf, __u8 size) +{ + hcipriv_t *hp = &hci->hp; + if(size<=0) + return; +#ifdef SL811_DEBUG_ERR + if (!hp->hcport) + DBGERR ("SL811BufWrite: Error port not set!\n"); +#endif + sl811_write_index (hp, offset); + DBGDATAW ("SL811BufWrite: io=%X offset = %02x, data = ", hp->hcport, offset); + while (size--) { + DBGDATAW ("%02x ", *buf); + sl811_write_data (hp, *buf); + buf++; + } + DBGDATAW ("\n"); +} + +/*************************************************************************** + * Function Name : regTest + * + * This routine test the Read/Write functionality of SL811HS registers + * + * 1) Store original register value into a buffer + * 2) Write to registers with a RAMP pattern. (10, 11, 12, ..., 255) + * 3) Read from register + * 4) Compare the written value with the read value and make sure they are + * equivalent + * 5) Restore the original register value + * + * Input: hci = data structure for the host controller + * + * + * Return: TRUE = passed; FALSE = failed + **************************************************************************/ +int regTest (hci_t * hci) +{ + int i, result = TRUE; + __u8 buf[256], data; + + DBGFUNC ("Enter regTest\n"); + for (i = 0x10; i < 256; i++) { + /* save the original buffer */ + buf[i] = SL811Read (hci, i); + + /* Write the new data to the buffer */ + SL811Write (hci, i, i); + } + + /* compare the written data */ + for (i = 0x10; i < 256; i++) { + data = SL811Read (hci, i); + if (data != i) { + DBGERR ("Pattern test failed!! value = 0x%x, s/b 0x%x\n", + data, i); + result = FALSE; + + /* If no Debug, show only first failed Address */ + if (!urb_debug) + break; + } + } + + /* restore the data */ + for (i = 0x10; i < 256; i++) { + SL811Write (hci, i, buf[i]); + } + + return (result); +} + +#if 0 /* unused (hne) */ +/*************************************************************************** + * Function Name : regShow + * + * Display all SL811HS register values + * + * Input: hci = data structure for the host controller + * + * Return: none + **************************************************************************/ +static void regShow (hci_t * hci) +{ + int i; + for (i = 0; i < 256; i++) { + printk ("offset %d: 0x%x\n", i, SL811Read (hci, i)); + } +} +#endif // if0 + +/************************************************************************ + * Function Name : USBReset + * + * This function resets SL811HS controller and detects the speed of + * the connecting device + * + * Input: hci = data structure for the host controller + * + * Return: 0 = no device attached; 1 = USB device attached + * + ***********************************************************************/ +/* [2.4.22] sl811_hc_reset */ +static int USBReset (hci_t * hci) +{ + int status; + hcipriv_t *hp = &hci->hp; + + DBGFUNC ("enter USBReset\n"); + + SL811Write (hci, SL11H_CTLREG2, 0xae); + + // setup master and full speed + + SL811Write (hci, SL11H_CTLREG1, 0x08); // reset USB + mdelay (20); // 20ms + SL811Write (hci, SL11H_CTLREG1, 0); // remove SE0 + + /* disable all interrupts (18.09.2003) */ + SL811Write (hci, SL11H_INTENBLREG, 0); + + /* 19.09.2003 [2.4.22] */ + mdelay(2); + SL811Write (hci, SL11H_INTSTATREG, 0xff); // clear all interrupt bits + status = SL811Read (hci, SL11H_INTSTATREG); + + if (status & SL11H_INTMASK_USBRESET) // Check if device is removed (0x40) + { + DBG ("USBReset: Device removed %03X\n", hci->hp.hcport); + /* 19.09.2003 [2.4.22] only IRQ for insert... + SL811Write (hci, SL11H_INTENBLREG, + SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | + SL11H_INTMASK_INSRMV); + */ + SL811Write (hci, SL11H_INTENBLREG, + SL11H_INTMASK_INSRMV); + hp->RHportStatus->portStatus &= + ~(PORT_CONNECT_STAT | PORT_ENABLE_STAT); + + return 0; + } + + // Send SOF to address 0, endpoint 0. + SL811Write (hci, SL11H_BUFLNTHREG_B, 0); //zero lenth + SL811Write (hci, SL11H_PIDEPREG_B, 0x50); //send SOF to EP0 + SL811Write (hci, SL11H_DEVADDRREG_B, 0x01); //address0 + SL811Write (hci, SL11H_SOFLOWREG, 0xe0); + + if (!(status & SL11H_INTMASK_DSTATE)) { /* 0x80 */ + /* slow speed device connect directly to root-hub */ + + DBG ("USBReset: low speed Device attached %03X\n", hci->hp.hcport); + SL811Write (hci, SL11H_CTLREG1, 0x8); + mdelay (20); + // SL811Write (hci, SL11H_SOFTMRREG, 0xee); /* the same, but better reading (hne) */ + SL811Write (hci, SL11H_CTLREG2, 0xee); + SL811Write (hci, SL11H_CTLREG1, 0x21); + + hp->RHportStatus->portStatus |= + (PORT_CONNECT_STAT | PORT_LOW_SPEED_DEV_ATTACH_STAT); + + } else { + /* full speed device connect directly to root hub */ + + DBG ("USBReset: full speed Device attached %03X \n", hci->hp.hcport); + SL811Write (hci, SL11H_CTLREG1, 0x8); + mdelay (20); + // SL811Write (hci, SL11H_SOFTMRREG, 0xae); /* the same, but better reading (hne) */ + SL811Write (hci, SL11H_CTLREG2, 0xae); + SL811Write (hci, SL11H_CTLREG1, 0x01); + + hp->RHportStatus->portStatus |= (PORT_CONNECT_STAT); + hp->RHportStatus->portStatus &= ~PORT_LOW_SPEED_DEV_ATTACH_STAT; + + } + + /* start the SOF or EOP */ + SL811Write (hci, SL11H_HOSTCTLREG_B, 0x01); + + /* clear all interrupt bits */ + /* 19.09.2003 [2.4.22] */ + mdelay(2); + SL811Write (hci, SL11H_INTSTATREG, 0xff); + + /* enable all interrupts */ + SL811Write (hci, SL11H_INTENBLREG, + SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | + SL11H_INTMASK_INSRMV); + + return 1; +} + +/*-------------------------------------------------------------------------*/ +/* tl functions */ +static inline void hc_mark_last_trans (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + __u8 *ptd = hp->tl; + + dbg ("enter hc_mark_last_trans\n"); + if (ptd == NULL) { + printk ("hc_mark_last_trans: ptd = null\n"); + return; + } + if (hp->xferPktLen > 0) + *(ptd + hp->tl_last) |= (1 << 3); +} + +static inline void hc_flush_data_cache (hci_t * hci, void *data, int len) +{ +} + +/************************************************************************ + * Function Name : hc_add_trans + * + * This function sets up the SL811HS register and transmit the USB packets. + * + * 1) Determine if enough time within the current frame to send the packet + * 2) Load the data into the SL811HS register + * 3) Set the appropriate command to the register and trigger the transmit + * + * Input: hci = data structure for the host controller + * len = data length + * data = transmitting data + * toggle = USB toggle bit, either 0 or 1 + * maxps = maximum packet size for this endpoint + * slow = speed of the device + * endpoint = endpoint number + * address = USB address of the device + * pid = packet ID + * format = + * urb_state = the current stage of USB transaction + * + * Return: 0 = no time left to schedule the transfer + * 1 = success + * + ***********************************************************************/ +static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle, + int maxps, int slow, int endpoint, int address, + int pid, int format, int urb_state) +{ + hcipriv_t *hp = &hci->hp; + __u16 speed; + int ii, jj, kk; + + DBGFUNC ("enter hc_add_trans: len=0x%x, toggle:0x%x, endpoing:0x%x," + " addr:0x%x, pid:0x%x,format:0x%x\n", len, toggle, endpoint, + address, pid, format); + + if (len > maxps) { + len = maxps; + } + + speed = hp->RHportStatus->portStatus; + if (speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) { +// ii = (8*7*8 + 6*3) * len + 800; + ii = 8 * 8 * len + 1024; + } else { + if (slow) { +// ii = (8*7*8 + 6*3) * len + 800; + ii = 8 * 8 * len + 2048; + } else +// ii = (8*7 + 6*3)*len + 110; + ii = 8 * len + 256; + } + + ii += 2 * 10 * len; + + jj = SL811Read (hci, SL11H_SOFTMRREG); + + /* Read back SOF counter HIGH (bit0-bit5 only) 26.11.2002 (hne) */ + // kk = (jj & 0xFF) * 64 - ii; + kk = (jj & (64-1)) * 64 - ii; + + if (kk < 0) { + DBGVERBOSE + ("hc_add_trans: no bandwidth for schedule, ii = 0x%x," + "jj = 0x%x, len =0x%x, active_trans = 0x%x\n", ii, jj, len, + hci->active_trans); + return (-1); + } + + if (pid != PID_IN) { + /* Load data into hc */ + + SL811BufWrite (hci, SL11H_DATA_START, (__u8 *) data, len); + } + + /* transmit */ + + SL11StartXaction (hci, (__u8) address, (__u8) endpoint, (__u8) pid, len, + toggle, slow, urb_state); + + return len; +} + +/************************************************************************ + * Function Name : hc_parse_trans + * + * This function checks the status of the transmitted or received packet + * and copy the data from the SL811HS register into a buffer. + * + * 1) Check the status of the packet + * 2) If successful, and IN packet then copy the data from the SL811HS register + * into a buffer + * + * Input: hci = data structure for the host controller + * actbytes = pointer to actual number of bytes + * data = data buffer + * cc = packet status + * length = the urb transmit length + * pid = packet ID + * urb_state = the current stage of USB transaction + * + * Return: 0 + ***********************************************************************/ +static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data, + int *cc, int *toggle, int length, int pid, + int urb_state) +{ + __u8 addr; + __u8 len; + __u8 pkt_stat; + + DBGFUNC ("enter hc_parse_trans\n"); + + /* get packet status; convert ack rcvd to ack-not-rcvd */ + + *cc = pkt_stat \ + = SL811Read (hci, SL11H_PKTSTATREG); + + if (pkt_stat & + (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | SL11H_STATMASK_OVF | + SL11H_STATMASK_NAK | SL11H_STATMASK_STALL)) { + if (*cc & SL11H_STATMASK_OVF) + DBGERR ("parse trans: error recv ack, cc = 0x%x/0x%x, TX_BASE_Len = " + "0x%x, TX_count=0x%x\n", pkt_stat, + SL811Read (hci, SL11H_PKTSTATREG), + SL811Read (hci, SL11H_BUFLNTHREG), + SL811Read (hci, SL11H_XFERCNTREG)); + else + DBGVERBOSE ("parse trans: error recv ack, cc = 0x%x/0x%x\n", + pkt_stat, SL811Read (hci, SL11H_PKTSTATREG)); + } else { + DBGVERBOSE ("parse trans: recv ack, cc=0x%x, len=0x%x, pid=0x%x, urb=%d\n", + pkt_stat, length, pid, urb_state); + + /* Successful data */ + if ((pid == PID_IN) && (urb_state != US_CTRL_SETUP)) { + + /* Find the base address */ + addr = SL811Read (hci, SL11H_BUFADDRREG); + + /* Find the Transmit Length */ + len = SL811Read (hci, SL11H_BUFLNTHREG); + + /* The actual data length = xmit length reg - xfer count reg */ + *actbytes = len - SL811Read (hci, SL11H_XFERCNTREG); + + if ((data != NULL) && (*actbytes > 0)) { + SL811BufRead (hci, addr, data, *actbytes); + + } else if ((data == NULL) && (*actbytes <= 0)) { + DBGERR ("hc_parse_trans: data = NULL or actbyte = 0x%x\n", + *actbytes); + return 0; + } + } else if (pid == PID_OUT) { + *actbytes = length; + } else { + // printk ("ERR:parse_trans, pid != IN or OUT, pid = 0x%x\n", pid); + } + *toggle = !*toggle; + } + + return 0; +} + +/************************************************************************ + * Function Name : hc_start_int + * + * This function enables SL811HS interrupts + * + * Input: hci = data structure for the host controller + * + * Return: none + ***********************************************************************/ +static void hc_start_int (hci_t * hci) +{ +#ifdef HC_SWITCH_INT + int mask = + SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | + SL11H_INTMASK_INSRMV | SL11H_INTMASK_USBRESET; + SL811Write (hci, IntEna, mask); +#endif +} + +/************************************************************************ + * Function Name : hc_stop_int + * + * This function disables SL811HS interrupts + * + * Input: hci = data structure for the host controller + * + * Return: none + ***********************************************************************/ +static void hc_stop_int (hci_t * hci) +{ +#ifdef HC_SWITCH_INT + SL811Write (hci, SL11H_INTSTATREG, 0xff); +// SL811Write(hci, SL11H_INTENBLREG, SL11H_INTMASK_INSRMV); + +#endif +} + +/************************************************************************ + * Function Name : handleInsRmvIntr + * + * This function handles the insertion or removal of device on SL811HS. + * It resets the controller and updates the port status + * + * Input: hci = data structure for the host controller + * + * Return: none + ***********************************************************************/ +void handleInsRmvIntr (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + + USBReset (hci); + + /* Changes in connection status */ + + hp->RHportStatus->portChange |= PORT_CONNECT_CHANGE; + + /* Port Enable or Disable */ + + if (hp->RHportStatus->portStatus & PORT_CONNECT_STAT) { + /* device is connected to the port: + * 1) Enable port + * 2) Resume ?? + */ +// hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE; + + /* Over Current is not supported by the SL811 HW ?? */ + + /* How about the Port Power ?? */ + + } else { + /* Device has disconnect: + * 1) Disable port + */ + + hp->RHportStatus->portStatus &= ~(PORT_ENABLE_STAT); + hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE; + + } +} + +/***************************************************************** + * + * Function Name: SL11StartXaction + * + * This functions load the registers with appropriate value and + * transmit the packet. + * + * Input: hci = data structure for the host controller + * addr = USB address of the device + * epaddr = endpoint number + * pid = packet ID + * len = data length + * toggle = USB toggle bit, either 0 or 1 + * slow = speed of the device + * urb_state = the current stage of USB transaction + * + * Return: 0 = error; 1 = successful + * + *****************************************************************/ +int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len, + int toggle, int slow, int urb_state) +{ + + hcipriv_t *hp = &hci->hp; + __u8 cmd = 0; + __u8 setup_data[4]; + __u16 speed; + + speed = hp->RHportStatus->portStatus; + if (!(speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) && slow) { + cmd |= SL11H_HCTLMASK_PREAMBLE; + } + switch (pid) { + case PID_SETUP: + // cmd &= SL11H_HCTLMASK_PREAMBLE; /* 26.11.2002 (hne) */ + cmd |= SL11H_HCTLMASK_ARM + | SL11H_HCTLMASK_ENBLEP + | SL11H_HCTLMASK_WRITE; + DBGVERBOSE ("SL811 Xaction: SETUP cmd=%02X\n", cmd); + break; + + case PID_OUT: + // cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE); /* (hne) 26.11.2002 */ + cmd |= SL11H_HCTLMASK_ARM + | SL11H_HCTLMASK_ENBLEP + | SL11H_HCTLMASK_WRITE; + if (toggle) { + cmd |= SL11H_HCTLMASK_SEQ; + } + DBGVERBOSE ("SL811 Xaction: OUT cmd=%02X\n", cmd); + break; + + case PID_IN: + // cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE); /* (hne) 26.11.2002 */ + cmd |= SL11H_HCTLMASK_ARM + | SL11H_HCTLMASK_ENBLEP; + DBGVERBOSE ("SL811 Xaction: IN cmd=%02x\n", cmd); + break; + + default: + DBGERR ("ERR: SL11StartXaction: unknow pid = 0x%x\n", pid); + return 0; + } + setup_data[0] = SL11H_DATA_START; /* 01:SL11H_BUFADDRREG */ + setup_data[1] = len; /* 02:SL11H_BUFLNTHREG */ + setup_data[2] = (((pid & 0x0F) << 4) | (epaddr & 0xF)); /* 03:SL11H_PIDEPREG */ + setup_data[3] = addr & 0x7F; /* 04:SL11H_DEVADDRREG */ + + SL811BufWrite (hci, SL11H_BUFADDRREG, (__u8 *) & setup_data[0], 4); + + // SL811Write (hci, SL11H_PIDEPREG, cmd); /* 03: grrr (hne) */ + SL811Write (hci, SL11H_HOSTCTLREG, cmd); /* 00: 26.11.2002 (hne) */ + +#if 0 + /* The SL811 has a hardware flaw when hub devices sends out + * SE0 between packets. It has been found in a TI chipset and + * cypress hub chipset. It causes the SL811 to hang + * The workaround is to re-issue the preample again. + */ + + if ((cmd & SL11H_HCTLMASK_PREAMBLE)) { + SL811Write (hci, SL11H_PIDEPREG_B, 0xc0); + SL811Write (hci, SL11H_HOSTCTLREG_B, 0x1); // send the premable + } +#endif + return 1; +} + +/***************************************************************** + * + * Function Name: hc_interrupt + * + * Interrupt service routine. + * + * 1) determine the causes of interrupt + * 2) clears all interrupts + * 3) calls appropriate function to service the interrupt + * + * Input: irq = interrupt line associated with the controller + * hci = data structure for the host controller + * r = holds the snapshot of the processor's context before + * the processor entered interrupt code. (not used here) + * + * Return value : None. + * + *****************************************************************/ +static void hc_interrupt (int irq, void *__hci, struct pt_regs *r) +{ + __u8 ii; + hci_t *hci = __hci; + int isExcessNak = 0; + int urb_state = 0; + // __u8 tmpIrq = 0; + int irq_loop = 16; /* total irq handled at one hardware irq */ + +#ifdef SL811_DEBUG_IRQ + hcipriv_t *hp = &hci->hp; + unsigned char sta1, sta2; + + outb (SL11H_INTSTATREG, 0x220); // Interrupt-Status register, controller1 + sta1 = (__u8) inb (0x220+1); + + outb (SL11H_INTSTATREG, 0x222); // Interrupt-Status register, controller2 + sta2 = (__u8) inb (0x222+1); +#endif + + do { + /* Get value from interrupt status register */ + + ii = SL811Read (hci, SL11H_INTSTATREG); + + /* All interrupts handled? (hne) */ + if ( !(ii & (SL11H_INTMASK_INSRMV /* | SL11H_INTMASK_USBRESET */ | + SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR)) ) + { +#ifdef SL811_DEBUG_IRQ + // printk ("%Xh IRQ ista=%02X not me\n", hp->hcport, ii); + + if ( sta1 != 0x80 && sta1 != 0x90 && + sta2 != 0x80 && sta2 != 0x90 ) + printk ("%Xh IRQ sta=%02X,%02X\n", hp->hcport, sta1, sta2); +#endif + return; + } + + /* Interrupt will be handle now (18.09.2003 2.4.22) */ + SL811Write (hci, SL11H_INTSTATREG, 0xff); + + /* SOF-outputs are to slow. No debug any SOF */ + if ( !(ii & SL11H_INTMASK_SOFINTR) ) + DBGVERBOSE ("SL811 ISR: %s%s%s%s io=%03X\n", + (ii & SL11H_INTMASK_XFERDONE) ? " DONE": "", + (ii & SL11H_INTMASK_SOFINTR) ? " SOFINTR": "", + (ii & SL11H_INTMASK_INSRMV) ? " INSRMV": "", + (ii & SL11H_INTMASK_USBRESET) ? " USBRESET": "", + hci->hp.hcport ); + + // if (ii & (SL11H_INTMASK_INSRMV | SL11H_INTMASK_USBRESET)) { + // Found in 2.5.75 (19.09.2003) + // "SL11H_INTMASK_USBRESET" is always on, if no device connected! + if (ii & SL11H_INTMASK_INSRMV) { + /* Device insertion or removal detected for the USB port */ + /* Disable all interrupts */ + SL811Write (hci, SL11H_INTENBLREG, 0); + /* No SOF, Full speed */ + SL811Write (hci, SL11H_CTLREG1, 0); + + mdelay (100); // wait for device stable + handleInsRmvIntr (hci); + return; + } + + /* Clear all interrupts */ + // SL811Write (hci, SL11H_INTSTATREG, 0xff); + + if (ii & SL11H_INTMASK_XFERDONE) { + + /* USB Done interrupt occurred */ + // DBGVERBOSE ("xsta=%02X\n", SL811Read (hci, SL11H_PKTSTATREG)); + + urb_state = sh_done_list (hci, &isExcessNak); +#ifdef WARNING + if (hci->td_array->len > 0) + printk ("WARNING: IRQ, td_array->len = 0x%x, s/b:0\n", + hci->td_array->len); +#endif + if (hci->td_array->len == 0 && !isExcessNak + && !(ii & SL11H_INTMASK_SOFINTR) && (urb_state == 0)) { + if (urb_state == 0) { + /* All urb_state has not been finished yet! + * continue with the current urb transaction + */ + + if (hci->last_packet_nak == 0) { + if (!usb_pipecontrol + (hci->td_array->td[0].urb->pipe)) + sh_add_packet (hci, hci->td_array-> td[0].urb); + } + } else { + /* The last transaction has completed: + * schedule the next transaction + */ + + sh_schedule_trans (hci, 0); + } + } + /* +++ (hne) + SL811Write (hci, SL11H_INTSTATREG, 0xff); + --- */ + return; + } + + if (ii & SL11H_INTMASK_SOFINTR) { + hci->frame_number = (hci->frame_number + 1) % 2048; + if (hci->td_array->len == 0) + sh_schedule_trans (hci, 1); + else { + if (sofWaitCnt++ > 100) { + /* The last transaction has not completed. + * Need to retire the current td, and let + * it transmit again later on. + * (THIS NEEDS TO BE WORK ON MORE, IT SHOULD NEVER + * GET TO THIS POINT) + */ + + DBGERR ("SOF interrupt: td_array->len = 0x%x, s/b:0 io=%03X\n", + hci->td_array->len, + hci->hp.hcport ); + urb_print (hci->td_array->td[hci->td_array->len - 1].urb, + "INTERRUPT", 0); + /* FIXME: Here sh_done_list was call with urb->dev=NULL 21.11.2002 (hne) */ + sh_done_list (hci, &isExcessNak); + /* +++ (hne) + SL811Write (hci, SL11H_INTSTATREG, 0xff); + --- */ + hci->td_array->len = 0; + sofWaitCnt = 0; + } + } + +#if 0 /* grrr! This READ clears my XFERDONE interrupt! Its better handle this in a loop. (hne) */ + tmpIrq = SL811Read (hci, SL11H_INTSTATREG) & SL811Read (hci, SL11H_INTENBLREG); + if (tmpIrq) { + DBG ("IRQ occurred while service SOF: irq = 0x%x\n", + tmpIrq); + + /* If we receive a DONE IRQ after schedule, need to + * handle DONE IRQ again + */ + + if (tmpIrq & SL11H_INTMASK_XFERDONE) { + DBGERR ("XFERDONE occurred while service SOF: irq = 0x%x\n", + tmpIrq); + urb_state = sh_done_list (hci, &isExcessNak); + } + SL811Write (hci, SL11H_INTSTATREG, 0xff); + } +#endif + } else { + DBG ("SL811 ISR: unknown, int=0x%x io=%03X\n", ii, hci->hp.hcport); + return; + } + + /* +++ (hne) + SL811Write (hci, SL11H_INTSTATREG, 0xff); + --- */ + + /* loop, if any interrupts can read (hne) */ + } while (--irq_loop); + + return; +} + +/***************************************************************** + * + * Function Name: hc_reset + * + * This function does register test and resets the SL811HS + * controller. + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static int hc_reset (hci_t * hci) +{ + int attachFlag = 0; + + DBGFUNC ("Enter hc_reset\n"); + attachFlag = USBReset (hci); + if (attachFlag) { + setPortChange (hci, PORT_CONNECT_CHANGE); + } + return (0); +} + +/***************************************************************** + * + * Function Name: hc_alloc_trans_buffer + * + * This function allocates all transfer buffer + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static int hc_alloc_trans_buffer (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + int maxlen; + + hp->itl0_len = 0; + hp->itl1_len = 0; + hp->atl_len = 0; + + hp->itl_buffer_len = 1024; + hp->atl_buffer_len = 4096 - 2 * hp->itl_buffer_len; /* 2048 */ + + maxlen = (hp->itl_buffer_len > hp->atl_buffer_len) ? hp->itl_buffer_len : hp->atl_buffer_len; + + hp->tl = kmalloc (maxlen, GFP_KERNEL); + + if (!hp->tl) + return -ENOMEM; + + memset (hp->tl, 0, maxlen); + return 0; +} + +/***************************************************************** + * + * Function Name: getPortStatusAndChange + * + * This function gets the ports status from SL811 and format it + * to a USB request format + * + * Input: hci = data structure for the host controller + * + * Return value : port status and change + * + *****************************************************************/ +static __u32 getPortStatusAndChange (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + __u32 portstatus; + + DBGFUNC ("enter getPorStatusAndChange\n"); + + portstatus = hp->RHportStatus->portChange << 16 | hp->RHportStatus->portStatus; + + return (portstatus); +} + +/***************************************************************** + * + * Function Name: setPortChange + * + * This function set the bit position of portChange. + * + * Input: hci = data structure for the host controller + * bitPos = the bit position + * + * Return value : none + * + *****************************************************************/ +static void setPortChange (hci_t * hci, __u16 bitPos) +{ + hcipriv_t *hp = &hci->hp; + + switch (bitPos) { + case PORT_CONNECT_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_ENABLE_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_RESET_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_POWER_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_SUSPEND_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_OVER_CURRENT_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + } +} + +/***************************************************************** + * + * Function Name: clrPortChange + * + * This function clear the bit position of portChange. + * + * Input: hci = data structure for the host controller + * bitPos = the bit position + * + * Return value : none + * + *****************************************************************/ +static void clrPortChange (hci_t * hci, __u16 bitPos) +{ + hcipriv_t *hp = &hci->hp; + switch (bitPos) { + case PORT_CONNECT_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + + case PORT_ENABLE_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + + case PORT_RESET_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + + case PORT_SUSPEND_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + + case PORT_OVER_CURRENT_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + } +} + +/***************************************************************** + * + * Function Name: clrPortStatus + * + * This function clear the bit position of portStatus. + * + * Input: hci = data structure for the host controller + * bitPos = the bit position + * + * Return value : none + * + *****************************************************************/ +static void clrPortStatus (hci_t * hci, __u16 bitPos) +{ + hcipriv_t *hp = &hci->hp; + switch (bitPos) { + case PORT_ENABLE_STAT: + hp->RHportStatus->portStatus &= ~bitPos; + break; + + case PORT_RESET_STAT: + hp->RHportStatus->portStatus &= ~bitPos; + break; + + case PORT_POWER_STAT: + hp->RHportStatus->portStatus &= ~bitPos; + break; + + case PORT_SUSPEND_STAT: + hp->RHportStatus->portStatus &= ~bitPos; + break; + } +} + +/***************************************************************** + * + * Function Name: setPortStatus + * + * This function set the bit position of portStatus. + * + * Input: hci = data structure for the host controller + * bitPos = the bit position + * + * Return value : none + * + *****************************************************************/ +static void setPortStatus (hci_t * hci, __u16 bitPos) +{ + hcipriv_t *hp = &hci->hp; + switch (bitPos) { + case PORT_ENABLE_STAT: + hp->RHportStatus->portStatus |= bitPos; + break; + + case PORT_RESET_STAT: + hp->RHportStatus->portStatus |= bitPos; + break; + + case PORT_POWER_STAT: + hp->RHportStatus->portStatus |= bitPos; + break; + + case PORT_SUSPEND_STAT: + hp->RHportStatus->portStatus |= bitPos; + break; + } +} + +/***************************************************************** + * + * Function Name: hc_start + * + * This function starts the root hub functionality. + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static int hc_start (hci_t * hci) +{ + DBGFUNC ("Enter hc_start\n"); + + rh_connect_rh (hci); + + return 0; +} + +/***************************************************************** + * + * Function Name: hc_alloc_hci + * + * This function allocates all data structure and store in the + * private data structure. + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static hci_t *__devinit hc_alloc_hci (void) +{ + hci_t *hci; + hcipriv_t *hp; + portstat_t *ps; + struct usb_bus *bus; + + DBGFUNC ("Enter hc_alloc_hci\n"); + hci = (hci_t *) kmalloc (sizeof (hci_t), GFP_KERNEL); + if (!hci) + return NULL; + + memset (hci, 0, sizeof (hci_t)); + + hp = &hci->hp; + + hp->irq = -1; + hp->hcport = -1; + + /* setup root hub port status */ + + ps = (portstat_t *) kmalloc (sizeof (portstat_t), GFP_KERNEL); + + if (!ps) + return NULL; + ps->portStatus = PORT_STAT_DEFAULT; + ps->portChange = PORT_CHANGE_DEFAULT; + hp->RHportStatus = ps; + + hci->nakCnt = 0; + hci->last_packet_nak = 0; + + hci->a_td_array.len = 0; + hci->i_td_array[0].len = 0; + hci->i_td_array[1].len = 0; + hci->td_array = &hci->a_td_array; + hci->active_urbs = 0; + hci->active_trans = 0; + INIT_LIST_HEAD (&hci->hci_hcd_list); + list_add (&hci->hci_hcd_list, &hci_hcd_list); + init_waitqueue_head (&hci->waitq); + + INIT_LIST_HEAD (&hci->ctrl_list); + INIT_LIST_HEAD (&hci->bulk_list); + INIT_LIST_HEAD (&hci->iso_list); + INIT_LIST_HEAD (&hci->intr_list); + INIT_LIST_HEAD (&hci->del_list); + + bus = usb_alloc_bus (&hci_device_operations); + if (!bus) { + kfree (hci); + return NULL; + } + + hci->bus = bus; + bus->bus_name = "sl811"; + bus->hcpriv = (void *) hci; + + return hci; +} + +/***************************************************************** + * + * Function Name: hc_release_hci + * + * This function De-allocate all resources + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static void hc_release_hci (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + + DBGFUNC ("Enter hc_release_hci\n"); + + /* disconnect all devices */ + if (hci->bus->root_hub) + usb_disconnect (&hci->bus->root_hub); + + hc_reset (hci); + + if (hp->hcport) { + // Disable all Interrupts + SL811Write (hci, SL11H_INTENBLREG, 0x00); + + // Remove all Interrups + mdelay(2); + SL811Write (hci, SL11H_INTSTATREG, 0xff); + } + + if (hp->tl) + kfree (hp->tl); + + sl811_release_regions(hp); + + if (hp->irq >= 0) { + free_irq (hp->irq, hci); + hp->irq = -1; + } + + usb_deregister_bus (hci->bus); + usb_free_bus (hci->bus); + + list_del (&hci->hci_hcd_list); + INIT_LIST_HEAD (&hci->hci_hcd_list); + + kfree (hci); +} + +/***************************************************************** + * + * Function Name: hc_found_hci + * + * This function request IO memory regions, request IRQ, and + * allocate all other resources. + * + * Input: addr = first IO address + * addr2 = second IO address + * irq = interrupt number + * + * Return: 0 = success or error condition + * + *****************************************************************/ +static int __devinit hc_found_hci (int irq, int iobase1, int iobase2) +{ + hci_t *hci; + hcipriv_t *hp; + + DBGFUNC ("Enter hc_found_hci\n"); + hci = hc_alloc_hci (); + if (!hci) { + return -ENOMEM; + } + + init_irq (); + hp = &hci->hp; + + if (sl811_request_regions (hp, iobase1, iobase2)) { + hc_release_hci (hci); + return -EBUSY; + } + + if (!regTest (hci)) { + DBGERR (KERN_ERR "regTest: Controller fault!\n"); + hc_release_hci (hci); + return -ENXIO; /* No such device or address */ + } + + if (hc_alloc_trans_buffer (hci)) { + hc_release_hci (hci); + return -ENOMEM; + } + + usb_register_bus (hci->bus); + + if (request_irq (irq, hc_interrupt, SA_SHIRQ, MODNAME, hci) != 0) { + DBGERR ("request interrupt %d failed", irq); + hc_release_hci (hci); + return -EBUSY; + } + hp->irq = irq; + + printk (KERN_INFO __FILE__ ": USB SL811 at %x,%x, IRQ %d\n", + hp->hcport, hp->hcport2, irq); + + #ifdef SL811_DEBUG_VERBOSE + { + __u8 u = SL811Read (hci, SL11H_HWREVREG); + + DBGVERBOSE ("SL811 HW: %02Xh ", u); + switch (u & 0xF0) { + case 0x00: DBGVERBOSE ("SL11H\n"); break; + case 0x10: DBGVERBOSE ("SL811HS rev1.2\n"); break; + case 0x20: DBGVERBOSE ("SL811HS rev1.5\n"); break; + default: DBGVERBOSE ("unknown!\n"); + } + } + #endif // SL811_DEBUG_VERBOSE + + if (hc_reset (hci)) { + hc_release_hci (hci); + return -EBUSY; + } + + if (hc_start (hci) < 0) { + DBGERR ("can't start usb-%x", hp->hcport); + hc_release_hci (hci); + return -EBUSY; + } + + return 0; +} + +/***************************************************************** + * + * Function Name: hci_hcd_init + * + * This is an init function, and it is the first function being called + * + * Input: none + * + * Return: 0 = success or error condition + * + *****************************************************************/ +static int __init hci_hcd_init (void) +{ + int ret; +#ifndef __arm__ + int io_offset = 0; +#endif // !__arm__ + + DBGFUNC ("Enter hci_hcd_init\n"); + DBGVERBOSE ("SL811 VERBOSE enabled\n"); + +#ifdef __arm__ + ret = hc_found_hci (irq, base_addr, data_reg_addr); +#else // __arm__ + + // registering "another instance" + for (io_offset = 0; io_offset < MAX_CONTROLERS * 2; io_offset += 2) { + + ret = hc_found_hci (irq, io_base + io_offset, 0); + if (ret) + return (ret); + + } /* endfor */ +#endif // __arm__ + + return ret; +} + +/***************************************************************** + * + * Function Name: hci_hcd_cleanup + * + * This is a cleanup function, and it is called when module is + * unloaded. + * + * Input: none + * + * Return: none + * + *****************************************************************/ +static void __exit hci_hcd_cleanup (void) +{ + struct list_head *hci_l; + hci_t *hci; + + DBGFUNC ("Enter hci_hcd_cleanup\n"); + for (hci_l = hci_hcd_list.next; hci_l != &hci_hcd_list;) { + hci = list_entry (hci_l, hci_t, hci_hcd_list); + hci_l = hci_l->next; + hc_release_hci (hci); + } +} + +module_init (hci_hcd_init); +module_exit (hci_hcd_cleanup); + +MODULE_AUTHOR ("Pei Liu , Henry Nestler "); +MODULE_DESCRIPTION ("USB SL811HS Host Controller Driver"); diff -urN linux-2.4.22-bk41/drivers/usb/host/hc_sl811.h linux-2.4.22-bk42/drivers/usb/host/hc_sl811.h --- linux-2.4.22-bk41/drivers/usb/host/hc_sl811.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/host/hc_sl811.h 2003-10-26 02:49:55.000000000 -0800 @@ -0,0 +1,386 @@ +/* + * SL811HS HCD (Host Controller Driver) for USB. + * + * COPYRIGHT (C) by CYPRESS SEMICONDUCTOR INC + * + * + */ + +#define GET_FRAME_NUMBER(hci) READ_REG32 (hci, HcFmNumber) + +/* + * Maximum number of root hub ports + */ +#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ + +/* control and status registers */ +#define HcRevision 0x00 +#define HcControl 0x01 +#define HcCommandStatus 0x02 +#define HcInterruptStatus 0x03 +#define HcInterruptEnable 0x04 +#define HcInterruptDisable 0x05 +#define HcFmInterval 0x0D +#define HcFmRemaining 0x0E +#define HcFmNumber 0x0F +#define HcLSThreshold 0x11 +#define HcRhDescriptorA 0x12 +#define HcRhDescriptorB 0x13 +#define HcRhStatus 0x14 +#define HcRhPortStatus 0x15 + +#define HcHardwareConfiguration 0x20 +#define HcDMAConfiguration 0x21 +#define HcTransferCounter 0x22 +#define HcuPInterrupt 0x24 +#define HcuPInterruptEnable 0x25 +#define HcChipID 0x27 +#define HcScratch 0x28 +#define HcSoftwareReset 0x29 +#define HcITLBufferLength 0x2A +#define HcATLBufferLength 0x2B +#define HcBufferStatus 0x2C +#define HcReadBackITL0Length 0x2D +#define HcReadBackITL1Length 0x2E +#define HcITLBufferPort 0x40 +#define HcATLBufferPort 0x41 + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_HCFS (3 << 6) /* BUS state mask */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +#define OHCI_USB_RESET (0 << 6) +#define OHCI_USB_RESUME (1 << 6) +#define OHCI_USB_OPER (2 << 6) +#define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_SO (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ + +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_ATD (1 << 7) /* scheduling overrun */ + +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + +/* + * HcHardwareConfiguration + */ +#define InterruptPinEnable (1 << 0) +#define InterruptPinTrigger (1 << 1) +#define InterruptOutputPolarity (1 << 2) +#define DataBusWidth16 (1 << 3) +#define DREQOutputPolarity (1 << 5) +#define DACKInputPolarity (1 << 6) +#define EOTInputPolarity (1 << 7) +#define DACKMode (1 << 8) +#define AnalogOCEnable (1 << 10) +#define SuspendClkNotStop (1 << 11) +#define DownstreamPort15KRSel (1 << 12) + +/* + * HcDMAConfiguration + */ +#define DMAReadWriteSelect (1 << 0) +#define ITL_ATL_DataSelect (1 << 1) +#define DMACounterSelect (1 << 2) +#define DMAEnable (1 << 4) +#define BurstLen_1 0 +#define BurstLen_4 (1 << 5) +#define BurstLen_8 (2 << 5) + +/* + * HcuPInterrupt + */ +#define SOFITLInt (1 << 0) +#define ATLInt (1 << 1) +#define AllEOTInterrupt (1 << 2) +#define OPR_Reg (1 << 4) +#define HCSuspended (1 << 5) +#define ClkReady (1 << 6) + +/* + * HcBufferStatus + */ +#define ITL0BufferFull (1 << 0) +#define ITL1BufferFull (1 << 1) +#define ATLBufferFull (1 << 2) +#define ITL0BufferDone (1 << 3) +#define ITL1BufferDone (1 << 4) +#define ATLBufferDone (1 << 5) + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status */ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +#define URB_DEL 1 + +#define PORT_STAT_DEFAULT 0x0100 +#define PORT_CONNECT_STAT 0x1 +#define PORT_ENABLE_STAT 0x2 +#define PORT_SUSPEND_STAT 0x4 +#define PORT_OVER_CURRENT_STAT 0x8 +#define PORT_RESET_STAT 0x10 +#define PORT_POWER_STAT 0x100 +#define PORT_LOW_SPEED_DEV_ATTACH_STAT 0x200 + +#define PORT_CHANGE_DEFAULT 0x0 +#define PORT_CONNECT_CHANGE 0x1 +#define PORT_ENABLE_CHANGE 0x2 +#define PORT_SUSPEND_CHANGE 0x4 +#define PORT_OVER_CURRENT_CHANGE 0x8 +#define PORT_RESET_CHANGE 0x10 + +/* Port Status Request info */ + +typedef struct portstat { + __u16 portChange; + __u16 portStatus; +} portstat_t; + +typedef struct hcipriv { + int irq; + int disabled; /* e.g. got a UE, we're hung */ + atomic_t resume_count; /* defending against multiple resumes */ + struct ohci_regs *regs; /* OHCI controller's memory */ + int hcport; /* I/O base address */ + int hcport2; /* I/O data reg addr */ + + struct portstat *RHportStatus; /* root hub port status */ + + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + + int frame; + + __u8 *tl; + int xferPktLen; + int atl_len; + int atl_buffer_len; + int itl0_len; + int itl1_len; + int itl_buffer_len; + int itl_index; + int tl_last; + int units_left; + +} hcipriv_t; +struct hci; + +#define cClt 0 // Control +#define cISO 1 // ISO +#define cBULK 2 // BULK +#define cInt 3 // Interrupt +#define ISO_BIT 0x10 + +/*------------------------------------------------------------------------- + * EP0 use for configuration and Vendor Specific command interface + *------------------------------------------------------------------------*/ +#define cMemStart 0x10 +#define EP0Buf 0x40 /* SL11H/SL811H memory start at 0x40 */ +#define EP0Len 0x40 /* Length of config buffer EP0Buf */ +#define EP1Buf 0x60 +#define EP1Len 0x40 + +/*------------------------------------------------------------------------- + * SL11H/SL811H memory from 80h-ffh use as ping-pong buffer. + *------------------------------------------------------------------------*/ +#define uBufA 0x80 /* buffer A address for DATA0 */ +#define uBufB 0xc0 /* buffer B address for DATA1 */ +#define uXferLen 0x40 /* xfer length */ +#define sMemSize 0xc0 /* Total SL11 memory size */ +#define cMemEnd 256 + +/*------------------------------------------------------------------------- + * SL811H Register Control memory map + * --Note: + * --SL11H only has one control register set from 0x00-0x04 + * --SL811H has two control register set from 0x00-0x04 and 0x08-0x0c + *------------------------------------------------------------------------*/ + +#define EP0Control 0x00 +#define EP0Address 0x01 +#define EP0XferLen 0x02 +#define EP0Status 0x03 +#define EP0Counter 0x04 + +#define EP1Control 0x08 +#define EP1Address 0x09 +#define EP1XferLen 0x0a +#define EP1Status 0x0b +#define EP1Counter 0x0c + +#define CtrlReg 0x05 +#define IntEna 0x06 + // 0x07 is reserved +#define IntStatus 0x0d +#define cDATASet 0x0e +#define cSOFcnt 0x0f +#define IntMask 0x57 /* Reset|DMA|EP0|EP2|EP1 for IntEna */ +#define HostMask 0x47 /* Host request command for IntStatus */ +#define ReadMask 0xd7 /* Read mask interrupt for IntStatus */ + +/*------------------------------------------------------------------------- + * Standard Chapter 9 definition + *------------------------------------------------------------------------- + */ +#define GET_STATUS 0x00 +#define CLEAR_FEATURE 0x01 +#define SET_FEATURE 0x03 +#define SET_ADDRESS 0x05 +#define GET_DESCRIPTOR 0x06 +#define SET_DESCRIPTOR 0x07 +#define GET_CONFIG 0x08 +#define SET_CONFIG 0x09 +#define GET_INTERFACE 0x0a +#define SET_INTERFACE 0x0b +#define SYNCH_FRAME 0x0c + +#define DEVICE 0x01 +#define CONFIGURATION 0x02 +#define STRING 0x03 +#define INTERFACE 0x04 +#define ENDPOINT 0x05 + +/*------------------------------------------------------------------------- + * SL11H/SL811H definition + *------------------------------------------------------------------------- + */ +#define DATA0_WR 0x07 // (Arm+Enable+tranmist to Host+DATA0) +#define DATA1_WR 0x47 // (Arm+Enable+tranmist to Host on DATA1) +#define ZDATA0_WR 0x05 // (Arm+Transaction Ignored+tranmist to Host+DATA0) +#define ZDATA1_WR 0x45 // (Arm+Transaction Ignored+tranmist to Host+DATA1) +#define DATA0_RD 0x03 // (Arm+Enable+received from Host+DATA0) +#define DATA1_RD 0x43 // (Arm+Enable+received from Host+DATA1) + +#define PID_SETUP 0x2d // USB Specification 1.1 Standard Definition +#define PID_SOF 0xA5 +#define PID_IN 0x69 +#define PID_OUT 0xe1 + +#define MAX_RETRY 0xffff +#define TIMEOUT 5 /* 2 mseconds */ + +#define SL11H_HOSTCTLREG 0 +#define SL11H_BUFADDRREG 1 +#define SL11H_BUFLNTHREG 2 +#define SL11H_PKTSTATREG 3 /* read */ +#define SL11H_PIDEPREG 3 /* write */ +#define SL11H_XFERCNTREG 4 /* read */ +#define SL11H_DEVADDRREG 4 /* write */ +#define SL11H_CTLREG1 5 +#define SL11H_INTENBLREG 6 + +// You should not use these registers + #define SL11H_HOSTCTLREG_B 8 +// #define SL11H_BUFADDRREG_B 9 + #define SL11H_BUFLNTHREG_B 0x0A +// #define SL11H_PKTSTATREG_B 0x0B /* read */ + #define SL11H_PIDEPREG_B 0x0B /* write */ +// #define SL11H_XFERCNTREG_B 0x0C /* read */ + #define SL11H_DEVADDRREG_B 0x0C /* write */ + +#define SL11H_INTSTATREG 0x0D /* write clears bitwise */ +#define SL11H_HWREVREG 0x0E /* read */ +#define SL11H_SOFLOWREG 0x0E /* write */ +#define SL11H_SOFTMRREG 0x0F /* read */ +#define SL11H_CTLREG2 0x0F /* write */ +#define SL11H_DATA_START 0x10 + +/* Host control register bits (addr 0) */ +#define SL11H_HCTLMASK_ARM 1 +#define SL11H_HCTLMASK_ENBLEP 2 +#define SL11H_HCTLMASK_WRITE 4 +#define SL11H_HCTLMASK_ISOCH 0x10 +#define SL11H_HCTLMASK_AFTERSOF 0x20 +#define SL11H_HCTLMASK_SEQ 0x40 +#define SL11H_HCTLMASK_PREAMBLE 0x80 + +/* Packet status register bits (addr 3) */ +#define SL11H_STATMASK_ACK 1 +#define SL11H_STATMASK_ERROR 2 +#define SL11H_STATMASK_TMOUT 4 +#define SL11H_STATMASK_SEQ 8 +#define SL11H_STATMASK_SETUP 0x10 +#define SL11H_STATMASK_OVF 0x20 +#define SL11H_STATMASK_NAK 0x40 +#define SL11H_STATMASK_STALL 0x80 + +/* Control register 1 bits (addr 5) */ +#define SL11H_CTL1MASK_DSBLSOF 1 +#define SL11H_CTL1MASK_NOTXEOF2 4 +#define SL11H_CTL1MASK_DSTATE 0x18 +#define SL11H_CTL1MASK_NSPD 0x20 +#define SL11H_CTL1MASK_SUSPEND 0x40 +#define SL11H_CTL1MASK_CLK12 0x80 + +#define SL11H_CTL1VAL_RESET 8 + +/* Interrut enable (addr 6) and interrupt status register bits (addr 0xD) */ +#define SL11H_INTMASK_XFERDONE 1 +#define SL11H_INTMASK_SOFINTR 0x10 +#define SL11H_INTMASK_INSRMV 0x20 +#define SL11H_INTMASK_USBRESET 0x40 +#define SL11H_INTMASK_DSTATE 0x80 /* only in status reg */ + +/* HW rev and SOF lo register bits (addr 0xE) */ +#define SL11H_HWRMASK_HWREV 0xF0 + +/* SOF counter and control reg 2 (addr 0xF) */ +#define SL11H_CTL2MASK_SOFHI 0x3F +#define SL11H_CTL2MASK_DSWAP 0x40 +#define SL11H_CTL2MASK_HOSTMODE 0xae + diff -urN linux-2.4.22-bk41/drivers/usb/host/hc_sl811_rh.c linux-2.4.22-bk42/drivers/usb/host/hc_sl811_rh.c --- linux-2.4.22-bk41/drivers/usb/host/hc_sl811_rh.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/host/hc_sl811_rh.c 2003-10-26 02:49:55.000000000 -0800 @@ -0,0 +1,536 @@ + +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------* + * SL811HS virtual root hub + * + * based on usb-ohci.c by R. Weissgaerber et al. + *-------------------------------------------------------------------------* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *-------------------------------------------------------------------------*/ + +#ifdef DEBUG +#undef DEBUG +#endif +static __u32 getPortStatusAndChange (hci_t * hci); +static void setPortStatus (hci_t * hci, __u16 bitPos); +static void setPortChange (hci_t * hci, __u16 bitPos); +static void clrPortStatus (hci_t * hci, __u16 bitPos); +static void clrPortChange (hci_t * hci, __u16 bitPos); +static int USBReset (hci_t * hci); +static int cc_to_error (int cc); + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = { + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = { + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, + 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +/* [Hub class-specific descriptor is constructed dynamically] */ +/* copied from "dynamic source". (hne) */ +static __u8 root_hub_hub_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x01, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x50, /* __u8 bPwrOn2pwrGood; 100ms for port reset */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0xfc, /* ??? HNE */ /* __u8 DeviceRemovable; *** 7 Ports max *** */ /* which port is attachable (HNE) */ + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; + + +/*************************************************************************** + * Function Name : rh_send_irq + * + * This function examine the port change in the virtual root hub. + * + * Note: This function assumes only one port exist in the root hub. + * + * Input: hci = data structure for the host controller + * rh_data = The pointer to port change data + * rh_len = length of the data in bytes + * + * Return: length of data + **************************************************************************/ +static int rh_send_irq (hci_t * hci, void *rh_data, int rh_len) +{ + int num_ports; + int i; + int ret; + int len; + __u8 data[8]; + + DBGFUNC ("enter rh_send_irq: \n"); + + /* Assuming the root hub has one port. This value need to change if + * there are more than one port for the root hub + */ + + num_ports = 1; + + /* The root hub status is not implemented, it basically has two fields: + * -- Local Power Status + * -- Over Current Indicator + * -- Local Power Change + * -- Over Current Indicator + * + * Right now, It is assume the power is good and no changes + */ + + *(__u8 *) data = 0; + + ret = *(__u8 *) data; + + /* Has the port status change within the root hub: It checks for + * -- Port Connect Status change + * -- Port Enable Change + */ + + for (i = 0; i < num_ports; i++) { + *(__u8 *) (data + (i + 1) / 8) |= + (((getPortStatusAndChange (hci) >> 16) & (PORT_CONNECT_STAT | PORT_ENABLE_STAT)) ? 1 : 0) << ((i + 1) % 8); + ret += *(__u8 *) (data + (i + 1) / 8); + + /* After the port change is read, it should be reset so the next time + * is it doesn't trigger a change again */ + + } + len = i / 8 + 1; + + if (ret > 0) { + memcpy (rh_data, data, min (len, (int)min (rh_len, (int)sizeof (data)))); + return len; + } + return 0; +} + +/*************************************************************************** + * Function Name : rh_int_timer_do + * + * This function is called when the timer expires. It gets the the port + * change data and pass along to the upper protocol. + * + * Note: The virtual root hub interrupt pipe are polled by the timer + * every "interval" ms + * + * Input: ptr = ptr to the urb + * + * Return: none + **************************************************************************/ +static void rh_int_timer_do (unsigned long ptr) +{ + int len; + struct urb *urb = (struct urb *) ptr; + hci_t *hci = urb->dev->bus->hcpriv; + + DBGFUNC ("enter rh_int_timer_do\n"); + + if (hci->rh.send) { + len = rh_send_irq (hci, urb->transfer_buffer, + urb->transfer_buffer_length); + if (len > 0) { + urb->actual_length = len; + if (urb_debug == 2) + urb_print (urb, "RET-t(rh)", + usb_pipeout (urb->pipe)); + + if (urb->complete) { + urb->complete (urb); + } + } + } + + /* re-activate the timer */ + rh_init_int_timer (urb); +} + +/*************************************************************************** + * Function Name : rh_init_int_timer + * + * This function creates a timer that act as interrupt pipe in the + * virtual hub. + * + * Note: The virtual root hub's interrupt pipe are polled by the timer + * every "interval" ms + * + * Input: urb = USB request block + * + * Return: 0 + **************************************************************************/ +static int rh_init_int_timer (struct urb * urb) +{ + hci_t *hci = urb->dev->bus->hcpriv; + hci->rh.interval = urb->interval; + + init_timer (&hci->rh.rh_int_timer); + hci->rh.rh_int_timer.function = rh_int_timer_do; + hci->rh.rh_int_timer.data = (unsigned long) urb; + hci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000; + add_timer (&hci->rh.rh_int_timer); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* helper macro */ +#define OK(x) len = (x); break + +/*************************************************************************** + * Function Name : rh_submit_urb + * + * This function handles all USB request to the the virtual root hub + * + * Input: urb = USB request block + * + * Return: 0 + **************************************************************************/ +static int rh_submit_urb (struct urb * urb) +{ + struct usb_device *usb_dev = urb->dev; + hci_t *hci = usb_dev->bus->hcpriv; + unsigned int pipe = urb->pipe; + struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet; + void *data = urb->transfer_buffer; + int leni = urb->transfer_buffer_length; + int len = 0; + int status = TD_CC_NOERROR; + __u32 datab[4]; + __u8 *data_buf = (__u8 *) datab; + + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + DBGFUNC ("enter rh_submit_urb\n"); + if (usb_pipeint (pipe)) { + hci->rh.urb = urb; + hci->rh.send = 1; + hci->rh.interval = urb->interval; + rh_init_int_timer (urb); + urb->status = cc_to_error (TD_CC_NOERROR); + + return 0; + } + + bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8); + wValue = le16_to_cpu (cmd->wValue); + wIndex = le16_to_cpu (cmd->wIndex); + wLength = le16_to_cpu (cmd->wLength); + + DBG ("rh_submit_urb, req = %d(%x) len=%d", + bmRType_bReq, bmRType_bReq, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *) data_buf = cpu_to_le16 (1); + OK (2); + + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data_buf = cpu_to_le16 (0); + OK (2); + + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data_buf = cpu_to_le16 (0); + OK (2); + + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data_buf = cpu_to_le32 (0); + OK (4); + + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *) data_buf = + cpu_to_le32 (getPortStatusAndChange (hci)); + OK (4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): + OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + OK (0); + + case (RH_C_HUB_OVER_CURRENT): + /* Over Current Not Implemented */ + OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + clrPortStatus (hci, PORT_ENABLE_STAT); + OK (0); + + case (RH_PORT_SUSPEND): + clrPortStatus (hci, PORT_SUSPEND_STAT); + OK (0); + + case (RH_PORT_POWER): + clrPortStatus (hci, PORT_POWER_STAT); + OK (0); + + case (RH_C_PORT_CONNECTION): + clrPortChange (hci, PORT_CONNECT_STAT); + OK (0); + + case (RH_C_PORT_ENABLE): + clrPortChange (hci, PORT_ENABLE_STAT); + OK (0); + + case (RH_C_PORT_SUSPEND): + clrPortChange (hci, PORT_SUSPEND_STAT); + OK (0); + + case (RH_C_PORT_OVER_CURRENT): + clrPortChange (hci, PORT_OVER_CURRENT_STAT); + OK (0); + + case (RH_C_PORT_RESET): + clrPortChange (hci, PORT_RESET_STAT); + OK (0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + setPortStatus (hci, PORT_SUSPEND_STAT); + OK (0); + + case (RH_PORT_RESET): + setPortStatus (hci, PORT_RESET_STAT); + // USBReset(hci); + clrPortChange (hci, + PORT_CONNECT_CHANGE | PORT_ENABLE_CHANGE + | PORT_SUSPEND_CHANGE | + PORT_OVER_CURRENT_CHANGE); + setPortChange (hci, PORT_RESET_CHANGE); + clrPortStatus (hci, PORT_RESET_STAT); + setPortStatus (hci, PORT_ENABLE_STAT); + + OK (0); + + case (RH_PORT_POWER): + setPortStatus (hci, PORT_POWER_STAT); + OK (0); + + case (RH_PORT_ENABLE): + setPortStatus (hci, PORT_ENABLE_STAT); + OK (0); + } + break; + + case RH_SET_ADDRESS: + hci->rh.devnum = wValue; + OK (0); + + case RH_GET_DESCRIPTOR: + DBGVERBOSE ("rh_submit_urb: RH_GET_DESCRIPTOR, wValue = 0x%x\n", wValue); + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min (leni, min ((__u16)sizeof (root_hub_dev_des), wLength)); + data_buf = root_hub_dev_des; + OK (len); + + case (0x02): /* configuration descriptor */ + len = min (leni, min ((__u16)sizeof (root_hub_config_des), wLength)); + data_buf = root_hub_config_des; + OK (len); + + case (0x03): /* string descriptors */ + len = usb_root_hub_string (wValue & 0xff, (int) (long) 0, + "SL811HS", data, wLength); + if (len > 0) { + data_buf = data; + OK (min (leni, len)); + } + + default: + status = SL11H_STATMASK_STALL; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + len = min_t(unsigned int, leni, + min_t(unsigned int, sizeof (root_hub_hub_des), wLength)); + memcpy (data_buf, root_hub_hub_des, len); + OK (len); + + case RH_GET_CONFIGURATION: + *(__u8 *) data_buf = 0x01; + OK (1); + + case RH_SET_CONFIGURATION: + OK (0); + + default: + DBGERR ("unsupported root hub command"); + status = SL11H_STATMASK_STALL; + } + + len = min (len, leni); + if (data != data_buf) + memcpy (data, data_buf, len); + urb->actual_length = len; + urb->status = cc_to_error (status); + + urb->hcpriv = NULL; + urb->dev = NULL; + if (urb->complete) { + urb->complete (urb); + } + + return 0; +} + +/*************************************************************************** + * Function Name : rh_unlink_urb + * + * This function unlinks the URB + * + * Input: urb = USB request block + * + * Return: 0 + **************************************************************************/ +static int rh_unlink_urb (struct urb * urb) +{ + hci_t *hci = urb->dev->bus->hcpriv; + + DBGFUNC ("enter rh_unlink_urb\n"); + if (hci->rh.urb == urb) { + hci->rh.send = 0; + del_timer (&hci->rh.rh_int_timer); + hci->rh.urb = NULL; + + urb->hcpriv = NULL; + /* --- + usb_put_dev (urb->dev); + + Can not found this function, copy from old source and other source. + usbnet.c us this to. Its a macro to usb_dec_dev_use. (hne) + --- */ + usb_dec_dev_use(urb->dev); + urb->dev = NULL; + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + urb->status = -ECONNRESET; + if (urb->complete) { + urb->complete (urb); + } + } else + urb->status = -ENOENT; + } + return 0; +} + +/*************************************************************************** + * Function Name : rh_connect_rh + * + * This function connect the virtual root hub to the USB stack + * + * Input: urb = USB request block + * + * Return: 0 + **************************************************************************/ +static int rh_connect_rh (hci_t * hci) +{ + struct usb_device *usb_dev; + + hci->rh.devnum = 0; + usb_dev = usb_alloc_dev (NULL, hci->bus); + if (!usb_dev) + return -ENOMEM; + + hci->bus->root_hub = usb_dev; + usb_connect (usb_dev); + if (usb_new_device (usb_dev) != 0) { + usb_free_dev (usb_dev); + return -ENODEV; + } + + return 0; +} diff -urN linux-2.4.22-bk41/drivers/usb/host/sl811.c linux-2.4.22-bk42/drivers/usb/host/sl811.c --- linux-2.4.22-bk41/drivers/usb/host/sl811.c 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.22-bk42/drivers/usb/host/sl811.c 2003-10-26 02:49:55.000000000 -0800 @@ -3,18 +3,43 @@ * * Copyright (c) 2003/06, Courage Co., Ltd. * - * Based on: + * Based on: * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, * Adam Richter, Gregory P. Smith; - 2.Original SL811 driver (hc_sl811.o) by Pei Liu + * 2.Original SL811 driver (hc_sl811.o) by Pei Liu + * 3.Rewrited as sl811.o by Yin Aihua * - * It's now support isosynchronous mode and more effective than hc_sl811.o + * It's now support isochornous mode and more effective than hc_sl811.o + * Support x86 architecture now. + * + * 19.09.2003 (05.06.2003) HNE + * sl811_alloc_hc: Set "bus->bus_name" at init. + * sl811_reg_test (hc_reset,regTest): + * Stop output at first failed pattern. + * Down-Grade for Kernel 2.4.20 and from 2.4.22 + * Splitt hardware depens into file sl811-x86.h and sl811-arm.h. + * + * 22.09.2003 HNE + * sl811_found_hc: First patterntest, than interrupt enable. + * Do nothing, if patterntest failed. Release io, if failed. + * Stop Interrupts first, than remove handle. (Old blocked Shred IRQ) + * Alternate IO-Base for second Controller (CF/USB1). + * + * 24.09.2003 HNE + * Remove all arm specific source (moved into include/asm/sl811-hw.h). + * + * 03.10.2003 HNE + * Low level only for port io into hardware-include. + * * To do: * 1.Modify the timeout part, it's some messy * 2.Use usb-a and usb-b set in Ping-Pong mode - + * o Floppy do not work. + * o driver crash, if io region can't register + * o Only as module tested! Compiled in Version not tested! + * 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 @@ -41,13 +66,10 @@ #include "../hub.h" #include "sl811.h" -#define DRIVER_VERSION "v0.28" -#define DRIVER_AUTHOR "Yin Aihua " -#define DRIVER_DESC "Sl811 USB Host Controller Driver" - -static int sl811_addr_io = 0xf100000e; -static int sl811_data_io = 0xf100000f; -static int sl811_irq = 44; +#define DRIVER_VERSION "v0.30" +#define MODNAME "SL811" +#define DRIVER_AUTHOR "Yin Aihua , Henry Nestler " +#define DRIVER_DESC "Sl811 USB Host Controller Alternate Driver" static LIST_HEAD(sl811_hcd_list); @@ -57,70 +79,53 @@ * 2, error maybe occur in normal * 3, useful and detail debug information * 4, function level enter and level inforamtion - * 5, endless information will output because of timer function or interrupt + * 5, endless information will output because of timer function or interrupt */ static int debug = 0; - -MODULE_PARM(sl811_addr_io,"i"); -MODULE_PARM_DESC(sl811_addr_io,"sl811 address io port 0xf100000e"); -MODULE_PARM(sl811_data_io,"i"); -MODULE_PARM_DESC(sl811_data_io,"sl811 data io port 0xf100000f"); -MODULE_PARM(sl811_irq,"i"); -MODULE_PARM_DESC(sl811_irq,"sl811 irq 44(default)"); MODULE_PARM(debug,"i"); MODULE_PARM_DESC(debug,"debug level"); +#include /* Include hardware and board depens */ + static void sl811_rh_int_timer_do(unsigned long ptr); -static void sl811_transfer_done(struct sl811_hc *hc, int sof); +static void sl811_transfer_done(struct sl811_hc *hc, int sof); /* * Read a byte of data from the SL811H/SL11H */ -static __u8 sl811_read(struct sl811_hc *hc, __u8 offset) +static __u8 inline sl811_read(struct sl811_hc *hc, __u8 offset) { - __u8 data; - - writeb(offset, hc->addr_io); - wmb(); - data = readb(hc->data_io); - rmb(); - - return data; + sl811_write_index (hc, offset); + return (sl811_read_data (hc)); } /* * Write a byte of data to the SL811H/SL11H */ -static void sl811_write(struct sl811_hc *hc, __u8 offset, __u8 data) +static void inline sl811_write(struct sl811_hc *hc, __u8 offset, __u8 data) { - writeb(offset, hc->addr_io); - writeb(data, hc->data_io); - wmb(); + sl811_write_index_data (hc, offset, data); } /* * Read consecutive bytes of data from the SL811H/SL11H buffer */ -static void sl811_read_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) +static void inline sl811_read_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) { - writeb(offset, hc->addr_io); - wmb(); + sl811_write_index (hc, offset); while (size--) { - *buf++ = readb(hc->data_io); - rmb(); + *buf++ = sl811_read_data(hc); } } /* * Write consecutive bytes of data to the SL811H/SL11H buffer */ -static void sl811_write_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) +static void inline sl811_write_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) { - writeb(offset, hc->addr_io); - wmb(); + sl811_write_index (hc, offset); while (size--) { - writeb(*buf, hc->data_io); - wmb(); + sl811_write_data (hc, *buf); buf++; } } @@ -147,6 +152,10 @@ if (data != i) { PDEBUG(1, "Pattern test failed!! value = 0x%x, s/b 0x%x", data, i); result = -1; + + /* If no Debug, show only first failed Address */ + if (!debug) + break; } } @@ -160,6 +169,7 @@ /* * Display all SL811HS register values */ +#if 0 /* unused (hne) */ static void sl811_reg_show(struct sl811_hc *hc) { int i; @@ -167,6 +177,7 @@ for (i = 0; i < 256; i++) PDEBUG(4, "offset %d: 0x%x", i, sl811_read(hc, i)); } +#endif /* * This function enables SL811HS interrupts @@ -796,9 +807,9 @@ static void sl811_print_td(int level, struct sl811_td *td) { - PDEBUG(level, "td = %p, ctrl = %x, addr = %x, len = %x, pidep = %x\n - dev = %x, status = %x, left = %x, errcnt = %x, done = %x\n - buf = %p, bustime = %d, td_status = %d\n", + PDEBUG(level, "td = %p, ctrl = %x, addr = %x, len = %x, pidep = %x\n " + "dev = %x, status = %x, left = %x, errcnt = %x, done = %x\n " + "buf = %p, bustime = %d, td_status = %d\n", td, td->ctrl, td->addr, td->len, td->pidep, td->dev, td->status, td->left, td->errcnt, td->done, td->buf, td->bustime, td->td_status); @@ -845,7 +856,7 @@ if (urbp->cur_td == NULL) urbp->cur_td = urbp->first_td = td; } - + urbp->last_td = td; PDEBUG(4, "leave success"); @@ -2397,7 +2408,7 @@ struct sl811_td *next_td; #ifdef SL811_DEBUG static struct sl811_td *repeat_td = NULL; - static repeat_cnt = 1; + static int repeat_cnt = 1; #endif if (++hc->frame_number > 1024) hc->frame_number = 0; @@ -2464,7 +2475,7 @@ mdelay(20); - // Disable hardware SOF generation. + // Disable hardware SOF generation, clear all irq status. sl811_write(hc, SL811_CTRL1, 0); mdelay(2); sl811_write(hc, SL811_INTRSTS, 0xff); @@ -2532,7 +2543,7 @@ status = sl811_read(hc, SL811_INTRSTS); if (status == 0) - return ; + return ; /* Not me */ sl811_write(hc, SL811_INTRSTS, 0xff); @@ -2603,6 +2614,7 @@ } hc->bus = bus; + bus->bus_name = MODNAME; bus->hcpriv = hc; return hc; @@ -2619,14 +2631,23 @@ if (hc->bus->root_hub) usb_disconnect(&hc->bus->root_hub); - if (hc->addr_io) - release_region(hc->addr_io, 1); - - if (hc->data_io) - release_region(hc->data_io, 1); - + // Stop interrupt handle if (hc->irq) free_irq(hc->irq, hc); + hc->irq = 0; + + /* Stop interrupt for sharing, but only, if PatternTest ok */ + if (hc->addr_io) { + /* Disable Interrupts */ + sl811_write(hc, SL811_INTR, 0); + + /* Remove all Interrupt events */ + mdelay(2); + sl811_write(hc, SL811_INTRSTS, 0xff); + } + + /* free io regions */ + sl811_release_regions(hc); usb_deregister_bus(hc->bus); usb_free_bus(hc->bus); @@ -2638,16 +2659,6 @@ } /* - * This function is board specific. It sets up the interrupt to - * be an edge trigger and trigger on the rising edge - */ -static void sl811_init_irq(void) -{ - GPDR &= ~(1<<23); - set_GPIO_IRQ_edge(1<<23, GPIO_RISING_EDGE); -} - -/* * This function request IO memory regions, request IRQ, and * allocate all other resources. * @@ -2667,40 +2678,47 @@ if (!hc) return -ENOMEM; - sl811_init_irq(); - - if (!request_region(addr_io, 1, "SL811 USB HOST")) { - PDEBUG(1, "request address %d failed", addr_io); + if (sl811_request_regions (hc, addr_io, data_io, MODNAME)) { + PDEBUG(1, "ioport %X,%X is in use!", addr_io, data_io); sl811_release_hc(hc); return -EBUSY; } - hc->addr_io = addr_io; - if (!request_region(data_io, 1, "SL811 USB HOST")) { - PDEBUG(1, "request address %d failed", data_io); - sl811_release_hc (hc); - return -EBUSY; + if (sl811_reg_test(hc)) { + PDEBUG(1, "SL811 register test failed!"); + sl811_release_hc(hc); + return -ENODEV; + } + +#ifdef SL811_DEBUG_VERBOSE + { + __u8 u = SL811Read (hci, SL11H_HWREVREG); + + // Show the hardware revision of chip + PDEBUG(1, "SL811 HW: %02Xh", u); + switch (u & 0xF0) { + case 0x00: PDEBUG(1, "SL11H"); break; + case 0x10: PDEBUG(1, "SL811HS rev1.2"); break; + case 0x20: PDEBUG(1, "SL811HS rev1.5"); break; + default: PDEBUG(1, "Revision unknown!"); + } } - hc->data_io = data_io; +#endif // SL811_DEBUG_VERBOSE + + sl811_init_irq(); usb_register_bus(hc->bus); - if (request_irq(irq, sl811_interrupt, 0, "SL811", hc)) { + if (request_irq(irq, sl811_interrupt, SA_SHIRQ, MODNAME, hc)) { PDEBUG(1, "request interrupt %d failed", irq); sl811_release_hc(hc); return -EBUSY; } hc->irq = irq; - printk(KERN_INFO __FILE__ ": USB SL811 at %x, addr2 = %x, IRQ %d\n", + printk(KERN_INFO __FILE__ ": USB SL811 at %x,%x, IRQ %d\n", addr_io, data_io, irq); - if (sl811_reg_test(hc)) { - PDEBUG(1, "SL811 register test failed!"); - sl811_release_hc(hc); - return -ENODEV; - } - sl811_hc_reset(hc); sl811_connect_rh(hc); @@ -2714,13 +2732,21 @@ */ static int __init sl811_hcd_init(void) { - int ret; - - PDEBUG(5, "etner"); + int ret = -ENODEV; + int count; + + PDEBUG(5, "enter"); info(DRIVER_VERSION " : " DRIVER_DESC); - - ret = sl811_found_hc(sl811_addr_io, sl811_data_io, sl811_irq); + + // registering some instance + for (count = 0; count < MAX_CONTROLERS; count++) { + if (io[count]) { + ret = sl811_found_hc(io[count], io[count]+OFFSET_DATA_REG, irq[count]); + if (ret) + return (ret); + } + } return ret; } diff -urN linux-2.4.22-bk41/drivers/usb/scanner.c linux-2.4.22-bk42/drivers/usb/scanner.c --- linux-2.4.22-bk41/drivers/usb/scanner.c 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/scanner.c 2003-10-26 02:49:55.000000000 -0800 @@ -378,6 +378,14 @@ * Sergey Vlasov. * - Clean up irq urb when not enough memory is available (Sergey Vlasov). * + * 0.4.15 2003-10-03 + * - Added vendor/product ids for Canon, HP, Microtek, Mustek, Siemens, UMAX, and + * Visioneer scanners. + * - Added test for USB_CLASS_CDC_DATA which is used by some fingerprint scanners + * - Use static declarations for usb_scanner_init/usb_scanner_exit + * (Daniele Bellucci). + * + * * TODO * - Performance * - Select/poll methods @@ -921,6 +929,7 @@ if (interface[0].bInterfaceClass != USB_CLASS_VENDOR_SPEC && interface[0].bInterfaceClass != USB_CLASS_PER_INTERFACE && + interface[0].bInterfaceClass != USB_CLASS_CDC_DATA && interface[0].bInterfaceClass != SCN_CLASS_SCANJET) { dbg("probe_scanner: This interface doesn't look like a scanner (class=0x%x).", interface[0].bInterfaceClass); return NULL; @@ -1159,13 +1168,13 @@ we match a user defined vendor/product ID. */ }; -void __exit +static void __exit usb_scanner_exit(void) { usb_deregister(&scanner_driver); } -int __init +static int __init usb_scanner_init (void) { if (usb_register(&scanner_driver) < 0) diff -urN linux-2.4.22-bk41/drivers/usb/scanner.h linux-2.4.22-bk42/drivers/usb/scanner.h --- linux-2.4.22-bk41/drivers/usb/scanner.h 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/scanner.h 2003-10-26 02:49:55.000000000 -0800 @@ -44,7 +44,7 @@ // #define DEBUG -#define DRIVER_VERSION "0.4.14" +#define DRIVER_VERSION "0.4.15" #define DRIVER_DESC "USB Scanner Driver" #include @@ -123,7 +123,10 @@ { USB_DEVICE(0x04a9, 0x220d) }, /* CanoScan N670U/N676U/LIDE 20 */ { USB_DEVICE(0x04a9, 0x220e) }, /* CanoScan N1240U/LIDE 30 */ { USB_DEVICE(0x04a9, 0x220f) }, /* CanoScan 8000F */ + { USB_DEVICE(0x04a9, 0x2210) }, /* CanoScan 9900F */ + { USB_DEVICE(0x04a9, 0x2212) }, /* CanoScan 5000F */ { USB_DEVICE(0x04a9, 0x2213) }, /* LIDE 50 */ + { USB_DEVICE(0x04a9, 0x2215) }, /* CanoScan 3000 */ { USB_DEVICE(0x04a9, 0x3042) }, /* FS4000US */ /* Colorado -- See Primax/Colorado below */ /* Compaq */ @@ -159,7 +162,9 @@ // { USB_DEVICE(0x03f0, 0x0701) }, /* ScanJet 5300C - NOT SUPPORTED - use hpusbscsi driver */ { USB_DEVICE(0x03f0, 0x0705) }, /* ScanJet 4400C */ // { USB_DEVICE(0x03f0, 0x0801) }, /* ScanJet 7400C - NOT SUPPORTED - use hpusbscsi driver */ + { USB_DEVICE(0x03f0, 0x0805) }, /* ScanJet 4470c */ { USB_DEVICE(0x03f0, 0x0901) }, /* ScanJet 2300C */ + { USB_DEVICE(0x03f0, 0x0a01) }, /* ScanJet 2400c */ { USB_DEVICE(0x03F0, 0x1005) }, /* ScanJet 5400C */ { USB_DEVICE(0x03F0, 0x1105) }, /* ScanJet 5470C */ { USB_DEVICE(0x03f0, 0x1205) }, /* ScanJet 5550C */ @@ -178,9 +183,11 @@ /* Memorex */ { USB_DEVICE(0x0461, 0x0346) }, /* 6136u - repackaged Primax ? */ /* Microtek */ + { USB_DEVICE(0x05da, 0x20a7) }, /* ScanMaker 5600 */ { USB_DEVICE(0x05da, 0x20c9) }, /* ScanMaker 6700 */ { USB_DEVICE(0x05da, 0x30ce) }, /* ScanMaker 3800 */ { USB_DEVICE(0x05da, 0x30cf) }, /* ScanMaker 4800 */ + { USB_DEVICE(0x05da, 0x30d4) }, /* ScanMaker 3830 + 3840 */ { USB_DEVICE(0x04a7, 0x0224) }, /* Scanport 3000 (actually Visioneer?)*/ /* The following SCSI-over-USB Microtek devices are supported by the microtek driver: Enable SCSI and USB Microtek in kernel config */ @@ -215,6 +222,7 @@ { USB_DEVICE(0x055f, 0x021e) }, /* BearPaw 1200 TA/CS */ { USB_DEVICE(0x055f, 0x0400) }, /* BearPaw 2400 TA PRO */ { USB_DEVICE(0x055f, 0x0401) }, /* P 3600 A3 Pro */ + { USB_DEVICE(0x055f, 0x0409) }, /* BearPaw 2448TA Pro */ { USB_DEVICE(0x055f, 0x0873) }, /* ScanExpress 600 USB */ { USB_DEVICE(0x055f, 0x1000) }, /* BearPaw 4800 TA PRO */ // { USB_DEVICE(0x05d8, 0x4002) }, /* BearPaw 1200 CU and ScanExpress 1200 UB Plus (see Artec) */ @@ -280,6 +288,9 @@ { USB_DEVICE(0x04b8, 0x011e) }, /* Perfection 1660 Photo */ { USB_DEVICE(0x04b8, 0x0801) }, /* Stylus CX5200 */ { USB_DEVICE(0x04b8, 0x0802) }, /* Stylus CX3200 */ + /* Siemens */ + { USB_DEVICE(0x0681, 0x0005) }, /* ID Mouse Professional */ + { USB_DEVICE(0x0681, 0x0010) }, /* Cherry FingerTIP ID Board - Sensor */ /* SYSCAN */ { USB_DEVICE(0x0a82, 0x4600) }, /* TravelScan 460/464 */ /* Trust */ @@ -290,6 +301,7 @@ { USB_DEVICE(0x1606, 0x0010) }, /* Astra 1220U */ { USB_DEVICE(0x1606, 0x0030) }, /* Astra 2000U */ { USB_DEVICE(0x1606, 0x0060) }, /* Astra 3400U/3450U */ + { USB_DEVICE(0x1606, 0x0070) }, /* Astra 4400 */ { USB_DEVICE(0x1606, 0x0130) }, /* Astra 2100U */ { USB_DEVICE(0x1606, 0x0160) }, /* Astra 5400U */ { USB_DEVICE(0x1606, 0x0230) }, /* Astra 2200U */ @@ -297,7 +309,8 @@ { USB_DEVICE(0x04a7, 0x0211) }, /* OneTouch 7600 USB */ { USB_DEVICE(0x04a7, 0x0221) }, /* OneTouch 5300 USB */ { USB_DEVICE(0x04a7, 0x0224) }, /* OneTouch 4800 USB */ - { USB_DEVICE(0x04a7, 0x0226) }, /* OneTouch 5300 USB */ + { USB_DEVICE(0x04a7, 0x0226) }, /* OneTouch 5800 USB */ + { USB_DEVICE(0x04a7, 0x022c) }, /* OneTouch 9020 USB */ { USB_DEVICE(0x04a7, 0x0231) }, /* 6100 USB */ { USB_DEVICE(0x04a7, 0x0311) }, /* 6200 EPP/USB */ { USB_DEVICE(0x04a7, 0x0321) }, /* OneTouch 8100 EPP/USB */ diff -urN linux-2.4.22-bk41/drivers/usb/serial/ftdi_sio.c linux-2.4.22-bk42/drivers/usb/serial/ftdi_sio.c --- linux-2.4.22-bk41/drivers/usb/serial/ftdi_sio.c 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/serial/ftdi_sio.c 2003-10-26 02:49:55.000000000 -0800 @@ -17,6 +17,15 @@ * See http://ftdi-usb-sio.sourceforge.net for upto date testing info * and extra documentation * + * (21/Oct/2003) Ian Abbott + * Renamed some VID/PID macros for Matrix Orbital and Perle Systems + * devices. Removed Matrix Orbital and Perle Systems devices from the + * 8U232AM device table, but left them in the FT232BM table, as they are + * known to use only FT232BM. + * + * (17/Oct/2003) Scott Allen + * Added vid/pid for Perle Systems UltraPort USB serial converters + * * (21/Sep/2003) Ian Abbott * Added VID/PID for Omnidirectional Control Technology US101 USB to * RS-232 adapter (also rebadged as Dick Smith Electronics XH6381). @@ -236,7 +245,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.3.4" +#define DRIVER_VERSION "v1.3.5" #define DRIVER_AUTHOR "Greg Kroah-Hartman , Bill Ryder , Kuba Ober " #define DRIVER_DESC "USB FTDI Serial Converters Driver" @@ -276,13 +285,6 @@ { USB_DEVICE_VER(FTDI_VID, FTDI_XF_642_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_VNHCPCUSB_D_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_DSS20_PID, 0, 0x3ff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID, 0, 0x3ff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID, 0, 0x3ff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID, 0, 0x3ff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_3_PID, 0, 0x3ff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_4_PID, 0, 0x3ff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_5_PID, 0, 0x3ff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_6_PID, 0, 0x3ff) }, { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2101_PID, 0, 0x3ff) }, { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2102_PID, 0, 0x3ff) }, { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2103_PID, 0, 0x3ff) }, @@ -349,13 +351,14 @@ { USB_DEVICE_VER(FTDI_VID, FTDI_XF_642_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_VNHCPCUSB_D_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_DSS20_PID, 0x400, 0xffff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID, 0x400, 0xffff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID, 0x400, 0xffff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID, 0x400, 0xffff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_3_PID, 0x400, 0xffff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_4_PID, 0x400, 0xffff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_5_PID, 0x400, 0xffff) }, - { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_6_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_0_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_1_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_2_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_3_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_4_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_5_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_6_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2101_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2102_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2103_PID, 0x400, 0xffff) }, @@ -435,13 +438,14 @@ { USB_DEVICE(FTDI_VID, FTDI_DSS20_PID) }, { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) }, { USB_DEVICE(FTDI_VID, FTDI_VNHCPCUSB_D_PID) }, - { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID) }, - { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID) }, - { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID) }, - { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_3_PID) }, - { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_4_PID) }, - { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_5_PID) }, - { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_6_PID) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_0_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_1_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_2_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_3_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_4_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_5_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_6_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID, 0x400, 0xffff) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) }, diff -urN linux-2.4.22-bk41/drivers/usb/serial/ftdi_sio.h linux-2.4.22-bk42/drivers/usb/serial/ftdi_sio.h --- linux-2.4.22-bk41/drivers/usb/serial/ftdi_sio.h 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/serial/ftdi_sio.h 2003-10-26 02:49:55.000000000 -0800 @@ -50,7 +50,6 @@ * The following are the values for the Matrix Orbital LCD displays, * which are the FT232BM ( similar to the 8U232AM ) */ -#define FTDI_MTXORB_VID FTDI_VID /* Matrix Orbital Product Id */ #define FTDI_MTXORB_0_PID 0xFA00 /* Matrix Orbital Product Id */ #define FTDI_MTXORB_1_PID 0xFA01 /* Matrix Orbital Product Id */ #define FTDI_MTXORB_2_PID 0xFA02 /* Matrix Orbital Product Id */ @@ -60,6 +59,12 @@ #define FTDI_MTXORB_6_PID 0xFA06 /* Matrix Orbital Product Id */ /* + * The following are the values for the Perle Systems + * UltraPort USB serial converters + */ +#define FTDI_PERLE_ULTRAPORT_PID 0xF0C0 /* Perle UltraPort Product Id */ + +/* * The following are the values for the Sealevel SeaLINK+ adapters. * (Original list sent by Tuan Hoang. Ian Abbott renamed the macros and * removed some PIDs that don't seem to match any existing products.) diff -urN linux-2.4.22-bk41/drivers/usb/serial/visor.c linux-2.4.22-bk42/drivers/usb/serial/visor.c --- linux-2.4.22-bk41/drivers/usb/serial/visor.c 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/serial/visor.c 2003-10-26 02:49:55.000000000 -0800 @@ -195,6 +195,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) }, { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID) }, + { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO600_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) }, @@ -223,6 +224,7 @@ static __devinitdata struct usb_device_id id_table_combined [] = { { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) }, { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID) }, + { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO600_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) }, diff -urN linux-2.4.22-bk41/drivers/usb/serial/visor.h linux-2.4.22-bk42/drivers/usb/serial/visor.h --- linux-2.4.22-bk41/drivers/usb/serial/visor.h 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/serial/visor.h 2003-10-26 02:49:55.000000000 -0800 @@ -20,6 +20,7 @@ #define HANDSPRING_VENDOR_ID 0x082d #define HANDSPRING_VISOR_ID 0x0100 #define HANDSPRING_TREO_ID 0x0200 +#define HANDSPRING_TREO600_ID 0x0300 #define PALM_VENDOR_ID 0x0830 #define PALM_M500_ID 0x0001 diff -urN linux-2.4.22-bk41/drivers/usb/storage/unusual_devs.h linux-2.4.22-bk42/drivers/usb/storage/unusual_devs.h --- linux-2.4.22-bk41/drivers/usb/storage/unusual_devs.h 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/storage/unusual_devs.h 2003-10-26 02:49:55.000000000 -0800 @@ -548,10 +548,10 @@ * - They don't like the INQUIRY command. So we must handle this command * of the SCSI layer ourselves. */ -UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x5009, +UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x9999, "Casio", "QV DigitalCamera", - US_SC_8070, US_PR_CB, NULL, + US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), /* Submitted by Hartmut Wahl */ @@ -631,6 +631,16 @@ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), +/* + * Entry for Jenoptik JD 5200z3 + * + * email: car.busse@gmx.de + */ +UNUSUAL_DEV( 0x0d96, 0x5200, 0x0001, 0x0200, + "Jenoptik", + "JD 5200 z3", + US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), + /* Reported by Kevin Cernekee * Tested on hardware version 1.10. * Entry is needed only for the initializer function override. diff -urN linux-2.4.22-bk41/drivers/usb/usbnet.c linux-2.4.22-bk42/drivers/usb/usbnet.c --- linux-2.4.22-bk41/drivers/usb/usbnet.c 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/usbnet.c 2003-10-26 02:49:55.000000000 -0800 @@ -381,14 +381,14 @@ #ifdef DEBUG #define devdbg(usbnet, fmt, arg...) \ - printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net.name, ## arg) + printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net.name , ## arg) #else #define devdbg(usbnet, fmt, arg...) do {} while(0) #endif #define devinfo(usbnet, fmt, arg...) \ do { if ((usbnet)->msg_level >= 1) \ - printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net.name, ## arg); \ + printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net.name , ## arg); \ } while (0) diff -urN linux-2.4.22-bk41/drivers/usb/w9968cf.c linux-2.4.22-bk42/drivers/usb/w9968cf.c --- linux-2.4.22-bk41/drivers/usb/w9968cf.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/w9968cf.c 2003-10-26 02:49:55.000000000 -0800 @@ -0,0 +1,3989 @@ +/*************************************************************************** + * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. * + * * + * Copyright (C) 2002 2003 by Luca Risolia * + * * + * - 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 * + * 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 Frédéric Jouault. * + * * + * 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 "w9968cf.h" +#include "w9968cf_decoder.h" + +EXPORT_NO_SYMBOLS; + + + +/**************************************************************************** + * Modules paramaters * + ****************************************************************************/ + +static u8 vppmod_load = W9968CF_VPPMOD_LOAD; +static u8 simcams = W9968CF_SIMCAMS; +static int video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /* -1=first free */ +static u16 packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_PACKET_SIZE}; +static u8 max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BUFFERS}; +static u8 double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_DOUBLE_BUFFER}; +static u8 clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING}; +static u8 filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FILTER_TYPE}; +static u8 largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW}; +static u8 decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_DECOMPRESSION}; +static u8 upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING}; +static u8 force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0}; +static u8 force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB}; +static u8 autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT}; +static u8 autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP}; +static u8 lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LIGHTFREQ}; +static u8 bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]= + W9968CF_BANDINGFILTER}; +static int clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV}; +static u8 backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT}; +static u8 mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR}; +static u8 sensor_mono[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_SENSOR_MONO}; +static u16 brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BRIGHTNESS}; +static u16 hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE}; +static u16 colour[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR}; +static u16 contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CONTRAST}; +static u16 whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_WHITENESS}; +#ifdef W9968CF_DEBUG +static u8 debug = W9968CF_DEBUG_LEVEL; +static u8 specific_debug = W9968CF_SPECIFIC_DEBUG; +#endif + +MODULE_AUTHOR("Luca Risolia "); + +MODULE_DESCRIPTION("Video4Linux driver for " + "W996[87]CF JPEG USB Dual Mode Camera Chip"); + +MODULE_SUPPORTED_DEVICE("Video"); + +MODULE_LICENSE("GPL"); + +MODULE_PARM(vppmod_load, "i"); +MODULE_PARM(simcams, "i"); +MODULE_PARM(video_nr, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(packet_size, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(max_buffers, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(double_buffer, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(clamping, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(filter_type, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(largeview, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(decompression, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(upscaling, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(force_palette, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(force_rgb, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(autobright, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(autoexp, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(lightfreq, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(bandingfilter, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(clockdiv, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(backlight, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(mirror, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(sensor_mono, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(brightness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(hue, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(colour, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(contrast, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(whiteness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +#ifdef W9968CF_DEBUG +MODULE_PARM(debug, "i"); +MODULE_PARM(specific_debug, "i"); +#endif + +MODULE_PARM_DESC(vppmod_load, + "\n<0|1> Automatic 'w9968cf-vpp' module loading." + "\n0 disable, 1 enable." + "\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." + "\nDefault value is "__MODULE_STRING(W9968CF_VPPMOD_LOAD)"." + "\n"); +MODULE_PARM_DESC(simcams, + "\n Number of cameras allowed to stream simultaneously." + "\nn may vary from 0 to " + __MODULE_STRING(W9968CF_MAX_DEVICES)"." + "\nDefault value is "__MODULE_STRING(W9968CF_SIMCAMS)"." + "\n"); +MODULE_PARM_DESC(video_nr, + "\n<-1|n[,...]> Specify V4L minor mode number." + "\n -1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" + "\nYou can specify " __MODULE_STRING(W9968CF_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"); +MODULE_PARM_DESC(packet_size, + "\n Specify the maximum data payload" + "\nsize in bytes for alternate settings, for each device." + "\nn is scaled between 63 and 1023 " + "(default is "__MODULE_STRING(W9968CF_PACKET_SIZE)")." + "\n"); +MODULE_PARM_DESC(max_buffers, + "\n Only for advanced users." + "\nSpecify the maximum number of video frame buffers" + "\nto allocate for each device, from 2 to " + __MODULE_STRING(W9968CF_MAX_BUFFERS) + ". (default is "__MODULE_STRING(W9968CF_BUFFERS)")." + "\n"); +MODULE_PARM_DESC(double_buffer, + "\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." + "\nDefault value is "__MODULE_STRING(W9968CF_DOUBLE_BUFFER) + " for every device." + "\n"); +MODULE_PARM_DESC(clamping, + "\n<0|1[,...]> Video data clamping: 0 disabled, 1 enabled." + "\nDefault value is "__MODULE_STRING(W9968CF_CLAMPING) + " for every device." + "\n"); +MODULE_PARM_DESC(filter_type, + "\n<0|1|2[,...]> Video filter type." + "\n0 none, 1 (1-2-1) 3-tap filter, " + "2 (2-3-6-3-2) 5-tap filter." + "\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" + " process." + "\n"); +MODULE_PARM_DESC(largeview, + "\n<0|1[,...]> Large view: 0 disabled, 1 enabled." + "\nDefault value is "__MODULE_STRING(W9968CF_LARGEVIEW) + " for every device." + "\n"); +MODULE_PARM_DESC(upscaling, + "\n<0|1[,...]> Software scaling (for non-compressed video):" + "\n0 disabled, 1 enabled." + "\nDisable it if you have a slow CPU or you don't have" + " enough memory." + "\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING) + " for every device." + "\nIf 'w9968cf-vpp' is not loaded, this paramater is" + " set to 0."); +MODULE_PARM_DESC(decompression, + "\n<0|1|2[,...]> Software video decompression:" + "\n- 0 disables decompression (doesn't allow formats needing" + " decompression)" + "\n- 1 forces decompression (allows formats needing" + " decompression only);" + "\n- 2 allows any permitted formats." + "\nFormats supporting compressed video are YUV422P and" + " YUV420P/YUV420 " + "\nin any resolutions where both width and height are " + "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."); +MODULE_PARM_DESC(force_palette, + "\n<0" + "|" __MODULE_STRING(VIDEO_PALETTE_UYVY) + "|" __MODULE_STRING(VIDEO_PALETTE_YUV420) + "|" __MODULE_STRING(VIDEO_PALETTE_YUV422P) + "|" __MODULE_STRING(VIDEO_PALETTE_YUV420P) + "|" __MODULE_STRING(VIDEO_PALETTE_YUYV) + "|" __MODULE_STRING(VIDEO_PALETTE_YUV422) + "|" __MODULE_STRING(VIDEO_PALETTE_GREY) + "|" __MODULE_STRING(VIDEO_PALETTE_RGB555) + "|" __MODULE_STRING(VIDEO_PALETTE_RGB565) + "|" __MODULE_STRING(VIDEO_PALETTE_RGB24) + "|" __MODULE_STRING(VIDEO_PALETTE_RGB32) + "[,...]>" + " Force picture palette." + "\nIn order:" + "\n- 0 allows any of the following formats:" + "\n- UYVY 16 bpp - Original video, compression disabled" + "\n- YUV420 12 bpp - Original video, compression enabled" + "\n- YUV422P 16 bpp - Original video, compression enabled" + "\n- YUV420P 12 bpp - Original video, compression enabled" + "\n- YUVY 16 bpp - Software conversion from UYVY" + "\n- YUV422 16 bpp - Software conversion from UYVY" + "\n- GREY 8 bpp - Software conversion from UYVY" + "\n- RGB555 16 bpp - Software conversion from UYVY" + "\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'." + "\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" + " set to 9 (UYVY)."); +MODULE_PARM_DESC(force_rgb, + "\n<0|1[,...]> Read RGB video data instead of BGR:" + "\n 1 = use RGB component ordering." + "\n 0 = use BGR component ordering." + "\nThis parameter has effect when using RGBX palettes only." + "\nDefault value is "__MODULE_STRING(W9968CF_FORCE_RGB) + " for every device." + "\n"); +MODULE_PARM_DESC(autobright, + "\n<0|1[,...]> CMOS 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 = no, 1 = yes" + "\nDefault value is "__MODULE_STRING(W9968CF_AUTOEXP) + " for every device." + "\n"); +MODULE_PARM_DESC(lightfreq, + "\n<50|60[,...]> Light frequency in Hz:" + "\n 50 for European and Asian lighting," + " 60 for American lighting." + "\nDefault value is "__MODULE_STRING(W9968CF_LIGHTFREQ) + " for every device." + "\n"); +MODULE_PARM_DESC(bandingfilter, + "\n<0|1[,...]> Banding filter to reduce effects of" + " fluorescent lighting:" + "\n 0 disabled, 1 enabled." + "\nThis filter tries to reduce the pattern of horizontal" + "\nlight/dark bands caused by some (usually fluorescent)" + " lighting." + "\nDefault value is "__MODULE_STRING(W9968CF_BANDINGFILTER) + " for every device." + "\n"); +MODULE_PARM_DESC(clockdiv, + "\n<-1|n[,...]> " + "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." + "\nDefault value is "__MODULE_STRING(W9968CF_CLOCKDIV) + " for every device." + "\n"); +MODULE_PARM_DESC(backlight, + "\n<0|1[,...]> Objects are lit from behind:" + "\n 0 = no, 1 = yes" + "\nDefault value is "__MODULE_STRING(W9968CF_BACKLIGHT) + " for every device." + "\n"); +MODULE_PARM_DESC(mirror, + "\n<0|1[,...]> Reverse image horizontally:" + "\n 0 = no, 1 = yes" + "\nDefault value is "__MODULE_STRING(W9968CF_MIRROR) + " for every device." + "\n"); +MODULE_PARM_DESC(sensor_mono, + "\n<0|1[,...]> The OV CMOS sensor is monochrome:" + "\n 0 = no, 1 = yes" + "\nDefault value is "__MODULE_STRING(W9968CF_SENSOR_MONO) + " for every device." + "\n"); +MODULE_PARM_DESC(brightness, + "\n Set picture brightness (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_BRIGHTNESS) + " for every device." + "\nThis parameter has no effect if 'autobright' is enabled." + "\n"); +MODULE_PARM_DESC(hue, + "\n Set picture hue (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_HUE) + " for every device." + "\n"); +MODULE_PARM_DESC(colour, + "\n Set picture saturation (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_COLOUR) + " for every device." + "\n"); +MODULE_PARM_DESC(contrast, + "\n Set picture contrast (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_CONTRAST) + " for every device." + "\n"); +MODULE_PARM_DESC(whiteness, + "\n Set picture whiteness (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_WHITENESS) + " for every device." + "\n"); +#ifdef W9968CF_DEBUG +MODULE_PARM_DESC(debug, + "\n Debugging information level, from 0 to 6:" + "\n0 = none (be cautious)" + "\n1 = critical errors" + "\n2 = significant informations" + "\n3 = configuration or general messages" + "\n4 = warnings" + "\n5 = called functions" + "\n6 = function internals" + "\nLevel 5 and 6 are useful for testing only, when just " + "one device is used." + "\nDefault value is "__MODULE_STRING(W9968CF_DEBUG_LEVEL)"." + "\n"); +MODULE_PARM_DESC(specific_debug, + "\n<0|1> Enable or disable specific debugging messages:" + "\n0 = print messages concerning every level" + " <= 'debug' level." + "\n1 = print messages concerning the level" + " indicated by 'debug'." + "\nDefault value is " + __MODULE_STRING(W9968CF_SPECIFIC_DEBUG)"." + "\n"); +#endif /* W9968CF_DEBUG */ + + + +/**************************************************************************** + * Some prototypes * + ****************************************************************************/ + +/* Video4linux interface */ +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*, size_t, loff_t*); +static int w9968cf_mmap(struct file*, struct vm_area_struct*); +static int w9968cf_ioctl(struct inode*, struct file*, + unsigned int, unsigned long); +static int w9968cf_do_ioctl(struct w9968cf_device*, unsigned int, void*); + +/* /proc interface */ +#if defined(CONFIG_VIDEO_PROC_FS) +static void w9968cf_proc_create(void); +static void w9968cf_proc_destroy(void); +static void w9968cf_proc_create_dev(struct w9968cf_device*); +static void w9968cf_proc_destroy_dev(struct w9968cf_device*); +static int w9968cf_proc_read_global(char*, char**, off_t, int, int*, void*); +static int w9968cf_proc_read_dev(char*, char**, off_t, int, int*, void*); +#else +static inline void w9968cf_proc_create(void) {} +static inline void w9968cf_proc_destroy(void) {} +static inline void w9968cf_proc_create_dev(struct w9968cf_device* cam) {} +static inline void w9968cf_proc_destroy_dev(struct w9968cf_device* cam) {} +#endif + +/* USB-specific */ +static void w9968cf_urb_complete(struct urb *urb); +static int w9968cf_start_transfer(struct w9968cf_device*); +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); +static int w9968cf_write_fsb(struct w9968cf_device*, u16* data); +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*); + + +/* Low-level I2C (SMBus) I/O */ +static int w9968cf_smbus_start(struct w9968cf_device*); +static int w9968cf_smbus_stop(struct w9968cf_device*); +static int w9968cf_smbus_write_byte(struct w9968cf_device*, u8 v); +static int w9968cf_smbus_read_byte(struct w9968cf_device*, u8* v); +static int w9968cf_smbus_write_ack(struct w9968cf_device*); +static int w9968cf_smbus_read_ack(struct w9968cf_device*); +static int w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam, + u16 address, u8* value); +static int w9968cf_i2c_adap_read_byte_data(struct w9968cf_device*, u16 address, + u8 subaddress, u8* value); +static int w9968cf_i2c_adap_write_byte(struct w9968cf_device*, + u16 address, u8 subaddress); +static int w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device*, + u16 address, u8 subaddress, + u8 value); + +/* I2C interface to kernel */ +static int w9968cf_i2c_init(struct w9968cf_device*); +static int w9968cf_i2c_smbus_xfer(struct i2c_adapter*, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data*); +static u32 w9968cf_i2c_func(struct i2c_adapter*); +static int w9968cf_i2c_attach_inform(struct i2c_client*); +static int w9968cf_i2c_detach_inform(struct i2c_client*); +static int w9968cf_i2c_control(struct i2c_adapter*, unsigned int cmd, + unsigned long arg); +static void w9968cf_i2c_inc_use(struct i2c_adapter*); +static void w9968cf_i2c_dec_use(struct i2c_adapter*); + +/* Memory management */ +static inline unsigned long kvirt_to_pa(unsigned long adr); +static void* rvmalloc(unsigned long size); +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 */ +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 inline int w9968cf_sensor_cmd(struct w9968cf_device*, + unsigned int cmd, void *arg); +static void w9968cf_sensor_configure(struct w9968cf_device*); +static int w9968cf_sensor_change_settings(struct w9968cf_device*); +static int w9968cf_sensor_get_picture(struct w9968cf_device*, + struct video_picture*); +static int w9968cf_sensor_set_picture(struct w9968cf_device*, + struct video_picture pict); + +/* Other helper functions */ +static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*, + enum w9968cf_model_id, + const unsigned short dev_nr); +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 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*); +static int w9968cf_adjust_window_size(struct w9968cf_device*, u16* w, u16* h); +static void w9968cf_init_framelist(struct w9968cf_device*); +static void w9968cf_push_frame(struct w9968cf_device*, u8 f_num); +static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**); +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); + + + +/**************************************************************************** + * Symbolic names * + ****************************************************************************/ + +/* Used to represent a list of values and their respective symbolic names */ +struct w9968cf_symbolic_list { + const int num; + const char *name; +}; + +/*-------------------------------------------------------------------------- + Returns the name of the matching element in the symbolic_list array. The + end of the list must be marked with an element that has a NULL name. + --------------------------------------------------------------------------*/ +static inline const char * +symbolic(struct w9968cf_symbolic_list list[], const int num) +{ + int i; + + for (i = 0; list[i].name != NULL; i++) + if (list[i].num == num) + return (list[i].name); + + return "Unknown"; +} + +static struct w9968cf_symbolic_list camlist[] = { + { W9968CF_MOD_GENERIC, "W996[87]CF JPEG USB Dual Mode Camera" }, + { 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_CLVBWG, "Creative Labs Video Blaster WebCam Go" }, + { W9968CF_MOD_DLLDK, "Die Lebon LDC-D35A Digital Kamera" }, + { W9968CF_MOD_EEEMC, "Ezonics EZ-802 EZMega Cam" }, + { W9968CF_MOD_ODPVDMPC, "OPCOM Digi Pen VGA Dual Mode Pen Camera" }, + + { -1, NULL } +}; + +static struct w9968cf_symbolic_list senlist[] = { + { CC_OV76BE, "OV76BE" }, + { CC_OV7610, "OV7610" }, + { CC_OV7620, "OV7620" }, + { CC_OV7620AE, "OV7620AE" }, + { CC_OV6620, "OV6620" }, + { CC_OV6630, "OV6630" }, + { CC_OV6630AE, "OV6630AE" }, + { CC_OV6630AF, "OV6630AF" }, + { -1, NULL } +}; + +/* Video4Linux1 palettes */ +static struct w9968cf_symbolic_list v4l1_plist[] = { + { VIDEO_PALETTE_GREY, "GREY" }, + { VIDEO_PALETTE_HI240, "HI240" }, + { VIDEO_PALETTE_RGB565, "RGB565" }, + { VIDEO_PALETTE_RGB24, "RGB24" }, + { VIDEO_PALETTE_RGB32, "RGB32" }, + { VIDEO_PALETTE_RGB555, "RGB555" }, + { VIDEO_PALETTE_YUV422, "YUV422" }, + { VIDEO_PALETTE_YUYV, "YUYV" }, + { VIDEO_PALETTE_UYVY, "UYVY" }, + { VIDEO_PALETTE_YUV420, "YUV420" }, + { VIDEO_PALETTE_YUV411, "YUV411" }, + { VIDEO_PALETTE_RAW, "RAW" }, + { VIDEO_PALETTE_YUV422P, "YUV422P" }, + { VIDEO_PALETTE_YUV411P, "YUV411P" }, + { VIDEO_PALETTE_YUV420P, "YUV420P" }, + { VIDEO_PALETTE_YUV410P, "YUV410P" }, + { -1, NULL } +}; + +/* Decoder error codes: */ +static struct w9968cf_symbolic_list decoder_errlist[] = { + { W9968CF_DEC_ERR_CORRUPTED_DATA, "Corrupted data" }, + { W9968CF_DEC_ERR_BUF_OVERFLOW, "Buffer overflow" }, + { W9968CF_DEC_ERR_NO_SOI, "SOI marker not found" }, + { W9968CF_DEC_ERR_NO_SOF0, "SOF0 marker not found" }, + { W9968CF_DEC_ERR_NO_SOS, "SOS marker not found" }, + { W9968CF_DEC_ERR_NO_EOI, "EOI marker not found" }, + { -1, NULL } +}; + +/* URB error codes: */ +static struct w9968cf_symbolic_list urb_errlist[] = { + { -ENOMEM, "No memory for allocation of internal structures" }, + { -ENOSPC, "The host controller's bandwidth is already consumed" }, + { -ENOENT, "URB was canceled by unlink_urb" }, + { -EXDEV, "ISO transfer only partially completed" }, + { -EAGAIN, "Too match scheduled for the future" }, + { -ENXIO, "URB already queued" }, + { -EFBIG, "Too much ISO frames requested" }, + { -ENOSR, "Buffer error (overrun)" }, + { -EPIPE, "Specified endpoint is stalled (device not responding)"}, + { -EOVERFLOW, "Babble (bad cable?)" }, + { -EPROTO, "Bit-stuff error (bad cable?)" }, + { -EILSEQ, "CRC/Timeout" }, + { -ETIMEDOUT, "NAK (device does not respond)" }, + { -1, NULL } +}; + + + +/**************************************************************************** + * Memory management functions * + ****************************************************************************/ + +/* Shameless copy from bttv-driver.c */ + +/* Here we want the physical address of the memory. + This is used when initializing the contents of the area. */ +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); /* restore the offset */ + ret = __pa(kva); + return ret; +} + + +static void* rvmalloc(unsigned long size) +{ + void* mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + mem_map_reserve(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + + +static void rvfree(void* mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + mem_map_unreserve(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} +/* End of shameless copy */ + + +/*-------------------------------------------------------------------------- + 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) +{ + u8 i; + + /* Free the isochronous transfer buffers */ + for (i = 0; i < W9968CF_URBS; i++) { + kfree(cam->transfer_buffer[i]); + cam->transfer_buffer[i] = NULL; + } + + /* Free temporary frame buffer */ + if (cam->frame_tmp.buffer) { + rvfree(cam->frame_tmp.buffer, W9968CF_HW_BUF_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; + } + + /* Free video frame buffers */ + if (cam->frame[0].buffer) { + rvfree(cam->frame[0].buffer, + cam->nbuffers * w9968cf_get_max_bufsize(cam)); + cam->frame[0].buffer = NULL; + } + + cam->nbuffers = 0; + + DBG(5, "Memory successfully deallocated.") +} + + +/*-------------------------------------------------------------------------- + Allocate memory buffers for USB transfers and video frames. + This function is called by open() only. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +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; + + /* NOTE: Deallocation is done elsewhere in case of error */ + + /* 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).", + p_size * W9968CF_ISO_PACKETS) + return -ENOMEM; + } + memset(cam->transfer_buffer[i], 0, W9968CF_ISO_PACKETS*p_size); + } + + /* Allocate memory for the temporary frame buffer */ + if (!(cam->frame_tmp.buffer = rvmalloc(W9968CF_HW_BUF_SIZE))) { + DBG(1, "Couldn't allocate memory for the temporary " + "video frame buffer (%i bytes).", W9968CF_HW_BUF_SIZE) + return -ENOMEM; + } + + /* Allocate memory for the helper buffer */ + if (w9968cf_vppmod_present) { + if (!(cam->vpp_buffer = rvmalloc(bufsize))) { + DBG(1, "Couldn't allocate memory for the helper buffer" + " (%li bytes).", bufsize) + return -ENOMEM; + } + } else + cam->vpp_buffer = NULL; + + /* Allocate memory for video frame buffers */ + cam->nbuffers = cam->max_buffers; + while (cam->nbuffers >= 2) { + if ((buff = rvmalloc(cam->nbuffers * bufsize))) + break; + else + cam->nbuffers--; + } + + if (!buff) { + 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.", + cam->max_buffers, cam->nbuffers) + + for (i = 0; i < cam->nbuffers; i++) { + cam->frame[i].buffer = buff + i*bufsize; + /* Circular list */ + if (i != cam->nbuffers-1) + cam->frame[i].next = &cam->frame[i+1]; + else + cam->frame[i].next = &cam->frame[0]; + cam->frame[i].status = F_UNUSED; + } + + DBG(5, "Memory successfully allocated.") + return 0; +} + + + +/**************************************************************************** + * /proc interface * + ****************************************************************************/ + +#if defined(CONFIG_VIDEO_PROC_FS) + +static struct proc_dir_entry* w9968cf_proc_entry, * w9968cf_proc_entry_global; +extern struct proc_dir_entry* video_proc_entry; + +#define YES_NO(x) ((x) ? "yes" : "no") + +/*-------------------------------------------------------------------------- + Read /proc/video/w9968cf/global + --------------------------------------------------------------------------*/ +static int +w9968cf_proc_read_global(char* page, char** start, off_t offset, + int count, int* eof, void* data) +{ + struct list_head *list = (struct list_head*)data, *ptr; + struct w9968cf_device* cam; + char* out = page; + int len; + u8 ncams = 0; + + if (down_interruptible(&w9968cf_devlist_sem)) + return -ERESTARTSYS; + + /* How many connected cameras ? */ + list_for_each(ptr, list) + ncams++; + + out += sprintf(out,"driver_name : %s\n", W9968CF_MODULE_NAME); + out += sprintf(out,"driver_version : %s\n", W9968CF_MODULE_VERSION); + out += sprintf(out,"driver_author : %s\n", W9968CF_MODULE_AUTHOR); + out += sprintf(out,"author_email : %s\n", W9968CF_AUTHOR_EMAIL); + out += sprintf(out,"vpp_w9968cf_module: %s\n", + w9968cf_vppmod_present ? "detected" : "not detected"); + out += sprintf(out,"max_allowed_cams : %d\n", simcams); + out += sprintf(out,"registered_cameras: %d\n", ncams); + + list_for_each(ptr, list) { + cam = list_entry(ptr, struct w9968cf_device, v4llist); + out += sprintf(out,"/dev/video%d : %s\n", + cam->v4ldev.minor, symbolic(camlist, cam->id)); + } + + up(&w9968cf_devlist_sem); + + len = out - page; + len -= offset; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + + *start = page + offset; + + return len; +} + + +/*-------------------------------------------------------------------------- + Read /proc/video/w9968cf/dev + --------------------------------------------------------------------------*/ +static int +w9968cf_proc_read_dev(char* page, char** start, off_t offset, + int count, int* eof, void* data) +{ + struct w9968cf_device* cam = (struct w9968cf_device* )data; + int len; + char* out = page; + + static struct video_picture pict; /* it has to be static */ + static int rc = 0; + + if (down_interruptible(&cam->procfs_sem)) + return -ERESTARTSYS; + + if (offset == 0) + rc = w9968cf_sensor_get_picture(cam, &pict); + + out += sprintf(out,"camera_model : %s\n", + symbolic(camlist, cam->id)); + out += sprintf(out,"sensor_model : %s\n", + symbolic(senlist, cam->sensor)); + out += sprintf(out,"sensor_monochrome : %s\n", + YES_NO(cam->sensor_mono)); + if (cam->users) + out += sprintf(out,"user_program : %s\n",cam->command); + out += sprintf(out,"packet_size_bytes : %d\n", + wMaxPacketSize[cam->altsetting-1]); + out += sprintf(out,"allocated_buffers : %d\n",cam->nbuffers); + out += sprintf(out,"streaming : %s\n",YES_NO(cam->streaming)); + out += sprintf(out,"compression : %s\n", + YES_NO(cam->vpp_flag & VPP_DECOMPRESSION)); + out += sprintf(out,"uyvy_to_rgbx : %s\n", + YES_NO(cam->vpp_flag & VPP_UYVY_TO_RGBX)); + out += sprintf(out,"uv-y_reordering : %s\n", + YES_NO(cam->vpp_flag & VPP_SWAP_YUV_BYTES)); + out += sprintf(out,"software_upscaling: %s\n", + YES_NO(cam->vpp_flag & VPP_UPSCALE)); + out += sprintf(out,"clock_divisor : %d\n",cam->clockdiv); + out += sprintf(out,"palette : %s\n", + symbolic(v4l1_plist, cam->picture.palette)); + out += sprintf(out,"depth_bpp : %d\n",cam->picture.depth); + out += sprintf(out,"x_window_offset : %d\n",cam->window.x); + out += sprintf(out,"y_window_offset : %d\n",cam->window.y); + out += sprintf(out,"width : %d\n",cam->window.width); + out += sprintf(out,"height : %d\n",cam->window.height); + out += sprintf(out,"max_buffers : %d\n",cam->max_buffers); + out += sprintf(out,"decompression : %s\n", + (!cam->decompression)? "off" : + ((cam->decompression == 1) ? "force" : "on")); + out += sprintf(out,"force_rgb : %s\n",YES_NO(cam->force_rgb)); + out += sprintf(out,"video_clamping : %s\n",YES_NO(cam->clamping)); + out += sprintf(out,"hw_double_buffer : %s\n", + YES_NO(cam->double_buffer)); + out += sprintf(out,"filter_type : %s\n", + (!cam->filter_type) ? "none" : + ((cam->filter_type == 1) ? "3-tap" : "5-tap")); + out += sprintf(out,"large_view : %s\n",YES_NO(cam->largeview)); + out += sprintf(out,"auto_brightness : %s\n",YES_NO(cam->auto_brt)); + out += sprintf(out,"auto_exposure : %s\n",YES_NO(cam->auto_exp)); + out += sprintf(out,"light_frequency : %d\n",cam->lightfreq); + out += sprintf(out,"banding_filter : %s\n",YES_NO(cam->bandfilt)); + out += sprintf(out,"back_light : %s\n",YES_NO(cam->backlight)); + out += sprintf(out,"mirror : %s\n",YES_NO(cam->mirror)); + if (!rc) { + out += sprintf(out,"brightness : %d\n",pict.brightness); + out += sprintf(out,"colour : %d\n",pict.colour); + out += sprintf(out,"contrast : %d\n",pict.contrast); + out += sprintf(out,"hue : %d\n",pict.hue); + out += sprintf(out,"whiteness : %d\n",pict.whiteness); + } + out += sprintf(out,"hs_polarity : %d\n",cam->hs_polarity); + out += sprintf(out,"vs_polarity : %d\n",cam->vs_polarity); +#ifdef W9968CF_DEBUG + out += sprintf(out,"debug_level : %d\n",debug); + out += sprintf(out,"specific_debug : %s\n",YES_NO(specific_debug)); +#endif + + len = out - page; + len -= offset; + if (len < count) { + *eof = 1; + if (len <= 0) { + up(&cam->procfs_sem); + return 0; + } + } else + len = count; + + *start = page + offset; + + up(&cam->procfs_sem); + return len; +} + + +static void w9968cf_proc_create_dev(struct w9968cf_device* cam) +{ + char name[5]; + + if (!w9968cf_proc_entry) + return; + + /* Create per-device readable entry */ + sprintf(name, "dev%d", cam->v4ldev.minor); + cam->proc_dev = create_proc_read_entry(name, S_IFREG|S_IRUGO|S_IWUSR, + w9968cf_proc_entry, + w9968cf_proc_read_dev, + (void*)cam); + if (!cam->proc_dev) + return; + cam->proc_dev->owner = THIS_MODULE; +} + + +static void w9968cf_proc_destroy_dev(struct w9968cf_device* cam) +{ + char name[5]; + + if (!cam->proc_dev) + return; + + sprintf(name, "dev%d", cam->v4ldev.minor); + + /* Destroy per-device entry */ + remove_proc_entry(name, w9968cf_proc_entry); +} + + +static void w9968cf_proc_create(void) +{ + if (!video_proc_entry) { + DBG(2, "Error: /proc/video/ does not exist.") + return; + } + + w9968cf_proc_entry = create_proc_entry("w9968cf", S_IFDIR, + video_proc_entry); + if (!w9968cf_proc_entry) { + DBG(2, "Unable to create /proc/video/w9968cf/") + return; + } + w9968cf_proc_entry->owner = THIS_MODULE; + + w9968cf_proc_entry_global = create_proc_read_entry("global", + S_IFREG|S_IRUGO|S_IWUSR, + w9968cf_proc_entry, + w9968cf_proc_read_global, + (void*)&w9968cf_dev_list); + if (w9968cf_proc_entry_global) + w9968cf_proc_entry_global->owner = THIS_MODULE; + else + DBG(2, "Unable to create /proc/video/w9968cf/global") + + DBG(5, "/proc entries successfully created.") +} + + +static void w9968cf_proc_destroy(void) +{ + if (!w9968cf_proc_entry) + return; + + if (w9968cf_proc_entry_global) + remove_proc_entry("global", w9968cf_proc_entry); + + remove_proc_entry("w9968cf", video_proc_entry); + + DBG(5, "/proc entries removed.") +} + +#endif /* CONFIG_VIDEO_PROC_FS */ + + + +/**************************************************************************** + * USB-specific functions * + ****************************************************************************/ + +/*-------------------------------------------------------------------------- + This is an handler function which is called after the URBs are completed. + It collects multiple data packets coming from the camera by putting them + into frame buffers: one or more zero data length data packets are used to + mark the end of a video frame; the first non-zero data packet is the start + of the next video frame; if an error is encountered in a packet, the entire + video frame is discarded and grabbed again. + If there are no requested frames in the FIFO list, packets are collected into + a temporary buffer. + --------------------------------------------------------------------------*/ +static void w9968cf_urb_complete(struct urb *urb) +{ + 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.") + 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; + + /* If a frame has been requested and we are grabbing into + the temporary frame, we'll switch to that requested frame */ + if ((*f) == &cam->frame_tmp && *cam->requested_frame) { + if (cam->frame_tmp.status == F_GRABBING) { + w9968cf_pop_frame(cam, &cam->frame_current); + (*f)->status = F_GRABBING; + (*f)->length = cam->frame_tmp.length; + memcpy((*f)->buffer, cam->frame_tmp.buffer, + (*f)->length); + DBG(6, "Switched from temp. frame to frame #%d", + (*f) - &cam->frame[0]) + } + } + + for (i = 0; i < urb->number_of_packets; i++) { + 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 && len != 0) { + DBG(4, "URB failed, error in data packet " + "(error #%d, %s).", + status, symbolic(urb_errlist, status)) + (*f)->status = F_ERROR; + continue; + } + + if (len) { /* start of frame */ + + if ((*f)->status == F_UNUSED) { + (*f)->status = F_GRABBING; + (*f)->length = 0; + } + + /* Buffer overflows shouldn't happen, however...*/ + if ((*f)->length + len > maxbufsize) { + DBG(4, "Buffer overflow: bad data packets.") + (*f)->status = F_ERROR; + } + + if ((*f)->status == F_GRABBING) { + memcpy((*f)->buffer + (*f)->length, pos, len); + (*f)->length += len; + } + + } else if ((*f)->status == F_GRABBING) { /* end of frame */ + + DBG(6, "Frame #%d successfully grabbed.", + ((*f)==&cam->frame_tmp ? -1 : (*f)-&cam->frame[0])) + + if (cam->vpp_flag & VPP_DECOMPRESSION) { + err=(*w9968cf_vpp_check_headers)((*f)->buffer, + (*f)->length); + if (err) { + DBG(4, "Skip corrupted frame: %s", + symbolic(decoder_errlist, err)) + (*f)->status = F_UNUSED; + continue; /* grab this frame again */ + } + } + + (*f)->status = F_READY; + (*f)->queued = 0; + + /* Take a pointer to the new frame from the FIFO list. + If the list is empty,we'll use the temporary frame*/ + if (*cam->requested_frame) + w9968cf_pop_frame(cam, &cam->frame_current); + else { + cam->frame_current = &cam->frame_tmp; + (*f)->status = F_UNUSED; + } + + } else if ((*f)->status == F_ERROR) + (*f)->status = F_UNUSED; /* grab it again */ + + PDBGG("Frame length %li | pack.#%d | pack.len. %d | state %d", + (unsigned long)(*f)->length, i, len, (*f)->status) + + } /* end for */ + + /* Resubmit this URB */ + urb->dev = cam->usbdev; + urb->status = 0; + spin_lock(&cam->urb_lock); + if (cam->streaming) + if ((err = usb_submit_urb(urb))) { + cam->misconfigured = 1; + DBG(1, "Couldn't resubmit the URB: error %d, %s", + err, symbolic(urb_errlist, err)); + } + spin_unlock(&cam->urb_lock); + + /* Wake up the user process */ + if (waitqueue_active(&cam->wait_queue)) + wake_up_interruptible(&cam->wait_queue); +} + + +/*--------------------------------------------------------------------------- + Setup the URB structures for the isochronous transfer. + Submit the URBs so that the data transfer begins. + Return 0 on success, a negative number otherwise. + ---------------------------------------------------------------------------*/ +static int w9968cf_start_transfer(struct w9968cf_device* cam) +{ + struct usb_device *udev = cam->usbdev; + struct urb* urb; + const u16 p_size = wMaxPacketSize[cam->altsetting-1]; + u16 w, h, d; + int vidcapt; + u32 t_size; + int err = 0; + s8 i, j; + + for (i = 0; i < W9968CF_URBS; i++) { + urb = usb_alloc_urb(W9968CF_ISO_PACKETS); + cam->urb[i] = urb; + + if (!urb) { + for (j = 0; j < i; j++) + usb_free_urb(cam->urb[j]); + DBG(1, "Couldn't allocate the URB structures.") + return -ENOMEM; + } + + urb->dev = udev; + urb->context = (void*)cam; + urb->pipe = usb_rcvisocpipe(udev, 1); + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = W9968CF_ISO_PACKETS; + urb->complete = w9968cf_urb_complete; + urb->transfer_buffer = cam->transfer_buffer[i]; + urb->transfer_buffer_length = p_size*W9968CF_ISO_PACKETS; + urb->interval = 1; + for (j = 0; j < W9968CF_ISO_PACKETS; j++) { + urb->iso_frame_desc[j].offset = p_size*j; + urb->iso_frame_desc[j].length = p_size; + } + } + + /* Transfer size per frame, in WORD ! */ + d = cam->hw_depth; + w = cam->hw_width; + h = cam->hw_height; + + t_size = (w*h*d)/16; + + err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */ + err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */ + + /* Transfer size */ + err += w9968cf_write_reg(cam, t_size & 0xffff, 0x3d); /* low bits */ + err += w9968cf_write_reg(cam, t_size >> 16, 0x3e); /* high bits */ + + if (cam->vpp_flag & VPP_DECOMPRESSION) + err += w9968cf_upload_quantizationtables(cam); + + vidcapt = w9968cf_read_reg(cam, 0x16); /* read picture settings */ + err += w9968cf_write_reg(cam, vidcapt|0x8000, 0x16); /* capt. enable */ + + err += usb_set_interface(udev, 0, cam->altsetting); + err += w9968cf_write_reg(cam, 0x8a05, 0x3c); /* USB FIFO enable */ + + 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.") + return err; + } + + w9968cf_init_framelist(cam); + + /* Begin to grab into the temporary buffer */ + cam->frame_tmp.status = F_UNUSED; + cam->frame_tmp.queued = 0; + cam->frame_current = &cam->frame_tmp; + + if (!(cam->vpp_flag & VPP_DECOMPRESSION)) + DBG(5, "Isochronous transfer size: %li bytes/frame.", + (unsigned long)t_size*2) + + DBG(5, "Starting the isochronous transfer...") + + /* Submit the URBs */ + for (i = 0; i < W9968CF_URBS; i++) { + err = usb_submit_urb(cam->urb[i]); + if (err) { + for (j = i-1; j >= 0; j--) + if (!usb_unlink_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, + symbolic(urb_errlist, err)) + } + } + + cam->streaming = 1; + + return 0; +} + + +/*-------------------------------------------------------------------------- + Stop the isochronous transfer and set alternate setting to 0 (0Mb/s). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_stop_transfer(struct w9968cf_device* cam) +{ + struct usb_device *udev = cam->usbdev; + unsigned long lock_flags; + int err = 0; + s8 i; + + /* This avoids race conditions with usb_submit_urb() + in the URB completition handler */ + spin_lock_irqsave(&cam->urb_lock, lock_flags); + cam->streaming = 0; + spin_unlock_irqrestore(&cam->urb_lock, lock_flags); + + 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; + } + + if (cam->disconnected) + goto exit; + + err = w9968cf_write_reg(cam, 0x0a05, 0x3c); /* stop USB transfer */ + err += usb_set_interface(udev, 0, 0); /* 0 Mb/s */ + err += w9968cf_write_reg(cam, 0x0000, 0x39); /* disable JPEG encoder */ + err += w9968cf_write_reg(cam, 0x0000, 0x16); /* stop video capture */ + + if (err) { + DBG(2, "Failed to tell the camera to stop the isochronous " + "transfer. However this is not a critical error.") + return -EIO; + } + +exit: + DBG(5, "Isochronous transfer stopped.") + return 0; +} + + +/*-------------------------------------------------------------------------- + Write a W9968CF register. + Return 0 on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_reg(struct w9968cf_device* cam, u16 value, u16 index) +{ + struct usb_device* udev = cam->usbdev; + int res; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + value, index, NULL, 0, W9968CF_USB_CTRL_TIMEOUT); + + if (res < 0) + DBG(4, "Failed to write a register " + "(value 0x%04X, index 0x%02X, error #%d, %s).", + value, index, res, symbolic(urb_errlist, res)) + + return (res >= 0) ? 0 : -1; +} + + +/*-------------------------------------------------------------------------- + Read a W9968CF register. + Return the register value on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u16* buff = cam->control_buffer; + int res; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, (void*)buff, + 2, W9968CF_USB_CTRL_TIMEOUT); + + if (res < 0) + DBG(4, "Failed to read a register " + "(index 0x%02X, error #%d, %s).", + index, res, symbolic(urb_errlist, res)) + + return (res >= 0) ? (int)(*buff) : -1; +} + + +/*-------------------------------------------------------------------------- + Write data to the fast serial bus registers. + Return 0 on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data) +{ + struct usb_device* udev = cam->usbdev; + u16 value; + int res; + + value = *data++; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + value, 0x06, (void*)data, 6, + W9968CF_USB_CTRL_TIMEOUT); + + if (res < 0) + DBG(4, "Failed to write the FSB registers " + "(error #%d, %s).", res, symbolic(urb_errlist, res)) + + return (res >= 0) ? 0 : -1; +} + + +/*-------------------------------------------------------------------------- + Write data to the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_sb(struct w9968cf_device* cam, u16 value) +{ + int err = 0; + + err = w9968cf_write_reg(cam, value, 0x01); + udelay(W9968CF_I2C_BUS_DELAY); + + return err; +} + + +/*-------------------------------------------------------------------------- + Read data from the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_read_sb(struct w9968cf_device* cam) +{ + int v = 0; + + v = w9968cf_read_reg(cam, 0x01); + udelay(W9968CF_I2C_BUS_DELAY); + + return v; +} + + +/*-------------------------------------------------------------------------- + Upload quantization tables for the JPEG compression. + This function is called by w9968cf_start_transfer(). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_upload_quantizationtables(struct w9968cf_device* cam) +{ + u16 a, b; + int err = 0, i, j; + + err += w9968cf_write_reg(cam, 0x0010, 0x39); /* JPEG clock enable */ + + for (i = 0, j = 0; i < 32; i++, j += 2) { + a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8); + b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8); + err += w9968cf_write_reg(cam, a, 0x40+i); + err += w9968cf_write_reg(cam, b, 0x60+i); + } + err += w9968cf_write_reg(cam, 0x0012, 0x39); /* JPEG encoder enable */ + + return err; +} + + + +/**************************************************************************** + * Low-level I2C I/O functions. * + * The adapter supports the following I2C transfer functions: * + * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) * + * i2c_adap_read_byte_data() * + * i2c_adap_read_byte() * + ****************************************************************************/ + +static int w9968cf_smbus_start(struct w9968cf_device* cam) +{ + int err = 0; + + err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + + return err; +} + + +static int w9968cf_smbus_stop(struct w9968cf_device* cam) +{ + int err = 0; + + err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + + return err; +} + + +static int w9968cf_smbus_write_byte(struct w9968cf_device* cam, u8 v) +{ + u8 bit; + int err = 0, sda; + + for (bit = 0 ; bit < 8 ; bit++) { + sda = (v & 0x80) ? 2 : 0; + v <<= 1; + /* SDE=1, SDA=sda, SCL=0 */ + err += w9968cf_write_sb(cam, 0x10 | sda); + /* SDE=1, SDA=sda, SCL=1 */ + err += w9968cf_write_sb(cam, 0x11 | sda); + /* SDE=1, SDA=sda, SCL=0 */ + err += w9968cf_write_sb(cam, 0x10 | sda); + } + + return err; +} + + +static int w9968cf_smbus_read_byte(struct w9968cf_device* cam, u8* v) +{ + u8 bit; + int err = 0; + + *v = 0; + for (bit = 0 ; bit < 8 ; bit++) { + *v <<= 1; + err += w9968cf_write_sb(cam, 0x0013); + *v |= (w9968cf_read_sb(cam) & 0x0008) ? 1 : 0; + err += w9968cf_write_sb(cam, 0x0012); + } + + return err; +} + + +static int w9968cf_smbus_write_ack(struct w9968cf_device* cam) +{ + int err = 0; + + err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + + return err; +} + + +static int w9968cf_smbus_read_ack(struct w9968cf_device* cam) +{ + int err = 0, sda; + + err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + sda = (w9968cf_read_sb(cam) & 0x08) ? 1 : 0; /* sda = SDA */ + err += w9968cf_write_sb(cam, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + if (sda < 0) + err += sda; + if (sda == 1) { + DBG(6, "Couldn't receive the ACK.") + err += -1; + } + + return err; +} + + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */ +static int +w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam, + u16 address, u8 subaddress,u8 value) +{ + u16* data = cam->data_buffer; + int err = 0; + + /* Enable SBUS outputs */ + err += w9968cf_write_reg(cam, 0x0020, 0x01); + + data[0] = 0x082f | ((address & 0x80) ? 0x1500 : 0x0); + data[0] |= (address & 0x40) ? 0x4000 : 0x0; + data[1] = 0x2082 | ((address & 0x40) ? 0x0005 : 0x0); + data[1] |= (address & 0x20) ? 0x0150 : 0x0; + data[1] |= (address & 0x10) ? 0x5400 : 0x0; + data[2] = 0x8208 | ((address & 0x08) ? 0x0015 : 0x0); + data[2] |= (address & 0x04) ? 0x0540 : 0x0; + data[2] |= (address & 0x02) ? 0x5000 : 0x0; + data[3] = 0x1d20 | ((address & 0x02) ? 0x0001 : 0x0); + data[3] |= (address & 0x01) ? 0x0054 : 0x0; + + err += w9968cf_write_fsb(cam, data); + + data[0] = 0x8208 | ((subaddress & 0x80) ? 0x0015 : 0x0); + data[0] |= (subaddress & 0x40) ? 0x0540 : 0x0; + data[0] |= (subaddress & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((subaddress & 0x20) ? 0x0001 : 0x0); + data[1] |= (subaddress & 0x10) ? 0x0054 : 0x0; + data[1] |= (subaddress & 0x08) ? 0x1500 : 0x0; + data[1] |= (subaddress & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((subaddress & 0x04) ? 0x0005 : 0x0); + data[2] |= (subaddress & 0x02) ? 0x0150 : 0x0; + data[2] |= (subaddress & 0x01) ? 0x5400 : 0x0; + data[3] = 0x001d; + + err += w9968cf_write_fsb(cam, data); + + data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0); + data[0] |= (value & 0x40) ? 0x0540 : 0x0; + data[0] |= (value & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0); + data[1] |= (value & 0x10) ? 0x0054 : 0x0; + data[1] |= (value & 0x08) ? 0x1500 : 0x0; + data[1] |= (value & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0); + data[2] |= (value & 0x02) ? 0x0150 : 0x0; + data[2] |= (value & 0x01) ? 0x5400 : 0x0; + data[3] = 0xfe1d; + + err += w9968cf_write_fsb(cam, data); + + /* Disable SBUS outputs */ + err += w9968cf_write_reg(cam, 0x0000, 0x01); + + if (!err) + DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X " + "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) + + return err; +} + + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */ +static int +w9968cf_i2c_adap_read_byte_data(struct w9968cf_device* cam, + u16 address, u8 subaddress, + u8* value) +{ + int err = 0; + + /* Serial data enable */ + err += w9968cf_write_sb(cam, 0x0013); /* don't change ! */ + + err += w9968cf_smbus_start(cam); + err += w9968cf_smbus_write_byte(cam, address); + err += w9968cf_smbus_read_ack(cam); + err += w9968cf_smbus_write_byte(cam, subaddress); + err += w9968cf_smbus_read_ack(cam); + err += w9968cf_smbus_stop(cam); + err += w9968cf_smbus_start(cam); + err += w9968cf_smbus_write_byte(cam, address + 1); + err += w9968cf_smbus_read_ack(cam); + err += w9968cf_smbus_read_byte(cam, value); + err += w9968cf_smbus_write_ack(cam); + err += w9968cf_smbus_stop(cam); + + /* Serial data disable */ + err += w9968cf_write_sb(cam, 0x0000); + + if (!err) + DBG(5, "I2C read byte data done, addr.0x%04X, " + "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.", + address, subaddress, *value) + + return err; +} + + +/* SMBus protocol: S Addr+1 Rd [A] [Value] NA P */ +static int +w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam, + u16 address, u8* value) +{ + int err = 0; + + /* Serial data enable */ + err += w9968cf_write_sb(cam, 0x0013); + + err += w9968cf_smbus_start(cam); + err += w9968cf_smbus_write_byte(cam, address + 1); + err += w9968cf_smbus_read_ack(cam); + err += w9968cf_smbus_read_byte(cam, value); + err += w9968cf_smbus_write_ack(cam); + err += w9968cf_smbus_stop(cam); + + /* Serial data disable */ + err += w9968cf_write_sb(cam, 0x0000); + + if (!err) + 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) + + return err; +} + + +/* SMBus protocol: S Addr Wr [A] Value [A] P */ +static int +w9968cf_i2c_adap_write_byte(struct w9968cf_device* cam, + u16 address, u8 value) +{ + DBG(4, "i2c_write_byte() is an unsupported transfer mode.") + return -EINVAL; +} + + + +/**************************************************************************** + * I2C interface to kernel * + ****************************************************************************/ + +static int +w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct w9968cf_device* cam = adapter->data; + u8 i, j; + int rc = 0, err = 0; + + switch (addr) { + case OV6xx0_SID: + case OV7xx0_SID: + break; + default: + DBG(4, "Rejected slave ID 0x%04X", addr) + return -EINVAL; + } + + if (size == I2C_SMBUS_BYTE) { + /* Why addr <<= 1? See OVXXX0_SID defines in ovcamchip.h */ + addr <<= 1; + + if (read_write == I2C_SMBUS_WRITE) + rc = w9968cf_i2c_adap_write_byte(cam, addr, command); + else if (read_write == I2C_SMBUS_READ) + rc = w9968cf_i2c_adap_read_byte(cam,addr, &data->byte); + + } else if (size == I2C_SMBUS_BYTE_DATA) { + addr <<= 1; + + if (read_write == I2C_SMBUS_WRITE) + rc = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr, + command, data->byte); + else if (read_write == I2C_SMBUS_READ) { + for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) { + rc = w9968cf_i2c_adap_read_byte_data(cam, addr, + command, &data->byte); + if (rc < 0) { + /* Work around: this seems to wake up + the EEPROM from the stall state */ + for (j = 0; j <= 10; j++) { + err += w9968cf_write_sb(cam,0x0020); + err += w9968cf_write_sb(cam,0x0000); + if (err) + break; + } + } + else + break; + } + + } else + return -EINVAL; + + } else { + DBG(4, "Unsupported I2C transfer mode (%d)", size) + return -EINVAL; + } + + /* This works around a bug in the I2C core */ + if (rc > 0) + rc = 0; + + return rc; +} + + +static u32 w9968cf_i2c_func(struct i2c_adapter* adap) +{ + return I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA; +} + + +static int w9968cf_i2c_attach_inform(struct i2c_client* client) +{ + struct w9968cf_device* cam = client->adapter->data; + const char* clientname = client->name; + int id = client->driver->id; + + if (id == I2C_DRIVERID_OVCAMCHIP) { + int rc = 0; + + cam->sensor_client = client; + + rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE, + &cam->sensor_mono); + if (rc < 0) { + DBG(1, "CMOS sensor initialization failed (rc=%d)",rc); + cam->sensor_client = NULL; + return rc; + } + + if (w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE, + &cam->sensor) < 0) + rc = -EIO; + else if (client->addr==OV7xx0_SID || client->addr==OV6xx0_SID) + w9968cf_sensor_configure(cam); + else + rc = -EINVAL; + + if (rc < 0) { + cam->sensor_client = NULL; + cam->sensor = CC_UNKNOWN; + return rc; + } + } else { + DBG(4, "Rejected client [%s] with [%s]", + clientname, client->driver->name) + return -1; + } + + DBG(2, "I2C attach client [%s] with [%s]", + clientname, client->driver->name) + + return 0; +} + + +static int w9968cf_i2c_detach_inform(struct i2c_client* client) +{ + struct w9968cf_device* cam = client->adapter->data; + const char* clientname = client->name; + + if (cam->sensor_client == client) { + cam->sensor_client = NULL; + } + + DBG(2, "I2C detach [%s]", clientname) + + return 0; +} + + +static int +w9968cf_i2c_control(struct i2c_adapter* adapter, unsigned int cmd, + unsigned long arg) +{ + return 0; +} + + +static void w9968cf_i2c_inc_use(struct i2c_adapter* adap) +{ + MOD_INC_USE_COUNT; +} + + +static void w9968cf_i2c_dec_use(struct i2c_adapter* adap) +{ + MOD_DEC_USE_COUNT; +} + + +static int w9968cf_i2c_init(struct w9968cf_device* cam) +{ + int rc = 0; + + static struct i2c_algorithm algo = { + .name = "W996[87]CF algorithm", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = w9968cf_i2c_smbus_xfer, + .algo_control = w9968cf_i2c_control, + .functionality = w9968cf_i2c_func, + }; + + static struct i2c_adapter adap = { + .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_W9968CF, + .inc_use = w9968cf_i2c_inc_use, + .dec_use = w9968cf_i2c_dec_use, + .client_register = w9968cf_i2c_attach_inform, + .client_unregister = w9968cf_i2c_detach_inform, + .algo = &algo, + }; + + memcpy(&cam->i2c_adapter, &adap, sizeof(struct i2c_adapter)); + strcpy(cam->i2c_adapter.name, "w9968cf"); + cam->i2c_adapter.data = cam; + + DBG(6, "Registering I2C bus with kernel...") + + rc = i2c_add_adapter(&cam->i2c_adapter); + if (rc) + DBG(5, "Failed to register the I2C bus.") + else + DBG(5, "I2C bus registered.") + + return rc; +} + + + +/**************************************************************************** + * Helper functions * + ****************************************************************************/ + +/*-------------------------------------------------------------------------- + Turn on the LED on some webcams. A beep should be heard too. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_turn_on_led(struct w9968cf_device* cam) +{ + int err = 0; + + err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power-down */ + err += w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */ + err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */ + err += w9968cf_write_reg(cam, 0x0010, 0x01); /* serial bus, SDS high */ + err += w9968cf_write_reg(cam, 0x0000, 0x01); /* serial bus, SDS low */ + err += w9968cf_write_reg(cam, 0x0010, 0x01); /* ..high 'beep-beep' */ + + if (err) + DBG(2, "Couldn't turn on the LED.") + + DBG(5, "LED turned on.") + + return err; +} + + +/*-------------------------------------------------------------------------- + Write some registers for the device initialization. + This function is called once on open(). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_init_chip(struct w9968cf_device* cam) +{ + int err = 0, rc = 0; + + err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */ + err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* power on */ + + 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, 0x0000, 0x36);/* JPEG restart interval */ + err += w9968cf_write_reg(cam, 0x0804, 0x37);/*JPEG VLE FIFO threshold*/ + err += w9968cf_write_reg(cam, 0x0000, 0x38);/* disable hw up-scaling */ + err += w9968cf_write_reg(cam, 0x0000, 0x3f); /* JPEG/MCTL test data */ + + err += w9968cf_set_picture(cam, cam->picture); /* this before */ + err += w9968cf_set_window(cam, cam->window); + + if (err) + goto error; + + rc = w9968cf_sensor_change_settings(cam); + if (rc) + goto error; + + DBG(5, "Chip successfully initialized."); + + return 0; + +error: + DBG(1, "Chip initialization failed.") + if (err) + return err; + else + return rc; +} + + +/*-------------------------------------------------------------------------- + Change the picture settings of the camera. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict) +{ + u16 fmt, hw_depth, hw_palette, reg_v = 0x0000; + int err = 0, rc = 0; + + /* Make sure we are using a valid depth */ + pict.depth = w9968cf_valid_depth(pict.palette); + + fmt = pict.palette; + + hw_depth = pict.depth; /* depth used by the winbond chip */ + hw_palette = pict.palette; /* palette used by the winbond chip */ + + /* VS & HS polarities */ + reg_v = (cam->vs_polarity << 12) | (cam->hs_polarity << 11); + + switch (fmt) + { + case VIDEO_PALETTE_UYVY: + reg_v |= 0x0000; + cam->vpp_flag = VPP_NONE; + break; + case VIDEO_PALETTE_YUV422P: + reg_v |= 0x0002; + cam->vpp_flag = VPP_DECOMPRESSION; + break; + case VIDEO_PALETTE_YUV420: + case VIDEO_PALETTE_YUV420P: + reg_v |= 0x0003; + cam->vpp_flag = VPP_DECOMPRESSION; + break; + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_YUV422: + reg_v |= 0x0000; + cam->vpp_flag = VPP_SWAP_YUV_BYTES; + hw_palette = VIDEO_PALETTE_UYVY; + break; + /* Original video is used instead of RGBX palettes. + Software conversion later. */ + case VIDEO_PALETTE_GREY: + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + reg_v |= 0x0000; /* UYVY 16 bit is used */ + hw_depth = 16; + hw_palette = VIDEO_PALETTE_UYVY; + cam->vpp_flag = VPP_UYVY_TO_RGBX; + break; + } + + /* FIXME: 'hardware double buffer' doesn't work when compressed video + is enabled (corrupted frames). */ + if (cam->double_buffer && !(cam->vpp_flag & VPP_DECOMPRESSION)) + reg_v |= 0x0080; + + if (cam->clamping) + reg_v |= 0x0020; + + if (cam->filter_type == 1) + reg_v |= 0x0008; + else if (cam->filter_type == 2) + reg_v |= 0x000c; + + err = w9968cf_write_reg(cam, reg_v, 0x16); + if (err) + goto error; + + rc = w9968cf_sensor_set_picture(cam, pict); + if (rc) + goto error; + + /* If all went well, update the device data structure */ + memcpy(&cam->picture, &pict, sizeof(pict)); + cam->hw_depth = hw_depth; + 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)); + + DBG(4, "Palette is %s, depth is %d bpp.", + symbolic(v4l1_plist, pict.palette), pict.depth) + + return 0; + +error: + DBG(1, "Failed to change picture settings.") + if (err) + return err; + else + return rc; +} + + +/*-------------------------------------------------------------------------- + Change the capture area size of the camera. + This function _must_ be called _after_ w9968cf_set_picture(). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_set_window(struct w9968cf_device* cam, struct video_window win) +{ + u16 x, y, w, h, scx, scy, cw, ch, ax, ay; + unsigned long fw, fh; + struct ovcamchip_window s_win; + int err=0, rc=0; + + /* Work around to avoid FP arithmetics */ + #define __SC(x) ((x) << 10) + #define __UNSC(x) ((x) >> 10) + + /* Make sure we are using a supported resolution */ + if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width, + (u16*)&win.height))) + goto error; + + /* Scaling factors */ + fw = __SC(win.width) / cam->maxwidth; + fh = __SC(win.height) / cam->maxheight; + + /* Set up the width and height values used by the chip */ + if ((win.width > cam->maxwidth) || (win.height > cam->maxheight)) { + cam->vpp_flag |= VPP_UPSCALE; + /* Calculate largest w,h mantaining the same w/h ratio */ + w = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh; + h = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight; + if (w < cam->minwidth) /* just in case */ + w = cam->minwidth; + if (h < cam->minheight) /* just in case */ + h = cam->minheight; + } else { + cam->vpp_flag &= ~VPP_UPSCALE; + w = win.width; + h = win.height; + } + + /* x,y offsets of the cropped area */ + scx = cam->start_cropx; + scy = cam->start_cropy; + + /* Calculate cropped area manteining the right w/h ratio */ + if (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) { + cw = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh; + ch = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight; + } else { + cw = w; + ch = h; + } + + /* Setup the sensor window */ + s_win.format = SENSOR_FORMAT; + s_win.width = cam->maxwidth; + s_win.height = cam->maxheight; + s_win.quarter = 0; /* full progressive video */ + + /* Center it */ + s_win.x = (s_win.width - cw) / 2; + s_win.y = (s_win.height - ch) / 2; + + /* Clock divisor */ + if (cam->clockdiv >= 0) + s_win.clockdiv = cam->clockdiv; /* manual override */ + else + switch (cam->sensor) { + case CC_OV6620: + s_win.clockdiv = 0; + break; + case CC_OV6630: + s_win.clockdiv = 0; + break; + case CC_OV76BE: + case CC_OV7610: + case CC_OV7620: + s_win.clockdiv = 0; + break; + default: + s_win.clockdiv = W9968CF_DEF_CLOCKDIVISOR; + } + + /* We have to scale win.x and win.y offsets */ + if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) + || (cam->vpp_flag & VPP_UPSCALE) ) { + ax = __SC(win.x)/fw; + ay = __SC(win.y)/fh; + } else { + ax = win.x; + ay = win.y; + } + + if ((ax + cw) > cam->maxwidth) + ax = cam->maxwidth - cw; + + if ((ay + ch) > cam->maxheight) + ay = cam->maxheight - ch; + + /* Adjust win.x, win.y */ + if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) + || (cam->vpp_flag & VPP_UPSCALE) ) { + win.x = __UNSC(ax*fw); + win.y = __UNSC(ay*fh); + } else { + win.x = ax; + win.y = ay; + } + + /* Offsets used by the chip */ + x = ax + s_win.x; + y = ay + s_win.y; + + /* Go ! */ + if ((rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win))) + goto error; + + err += w9968cf_write_reg(cam, scx + x, 0x10); + err += w9968cf_write_reg(cam, scy + y, 0x11); + err += w9968cf_write_reg(cam, scx + x + cw, 0x12); + err += w9968cf_write_reg(cam, scy + y + ch, 0x13); + err += w9968cf_write_reg(cam, w, 0x14); + err += w9968cf_write_reg(cam, h, 0x15); + + /* JPEG width & height */ + err += w9968cf_write_reg(cam, w, 0x30); + err += w9968cf_write_reg(cam, h, 0x31); + + /* Y & UV frame buffer strides (in WORD) */ + if (cam->vpp_flag & VPP_DECOMPRESSION) { + err += w9968cf_write_reg(cam, w/2, 0x2c); + err += w9968cf_write_reg(cam, w/4, 0x2d); + } else + err += w9968cf_write_reg(cam, w, 0x2c); + + if (err) + goto error; + + /* If all went well, update the device data structure */ + memcpy(&cam->window, &win, sizeof(win)); + cam->hw_width = w; + 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)); + + DBG(4, "The capture area is %dx%d, Offset (x,y)=(%d,%d).", + 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", + 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.") + if (err) + return err; + else + return rc; +} + + +/*-------------------------------------------------------------------------- + Return non-zero if the palette is supported, 0 otherwise. + --------------------------------------------------------------------------*/ +static inline u16 w9968cf_valid_palette(u16 palette) +{ + u8 i = 0; + while (w9968cf_formatlist[i].palette != 0) { + if (palette == w9968cf_formatlist[i].palette) + return palette; + i++; + } + return 0; +} + + +/*-------------------------------------------------------------------------- + Return the depth corresponding to the given palette. + Palette _must_ be supported ! + --------------------------------------------------------------------------*/ +static inline u16 w9968cf_valid_depth(u16 palette) +{ + u8 i=0; + while (w9968cf_formatlist[i].palette != palette) + i++; + + return w9968cf_formatlist[i].depth; +} + + +/*-------------------------------------------------------------------------- + Return non-zero if the format requires decompression, 0 otherwise. + --------------------------------------------------------------------------*/ +static inline u8 w9968cf_need_decompression(u16 palette) +{ + u8 i = 0; + while (w9968cf_formatlist[i].palette != 0) { + if (palette == w9968cf_formatlist[i].palette) + return w9968cf_formatlist[i].compression; + i++; + } + return 0; +} + + +/*-------------------------------------------------------------------------- + Adjust the asked values for window width and height. + Return 0 on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height) +{ + u16 maxw, maxh; + + 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; + + if (*width > maxw) + *width = maxw; + if (*height > maxh) + *height = maxh; + + if (cam->vpp_flag & VPP_DECOMPRESSION) { + *width &= ~15L; /* multiple of 16 */ + *height &= ~15L; + } + + PDBGG("Window size adjusted w=%d, h=%d ", *width, *height) + + return 0; +} + + +/*-------------------------------------------------------------------------- + Initialize the FIFO list of requested frames. + --------------------------------------------------------------------------*/ +static void w9968cf_init_framelist(struct w9968cf_device* cam) +{ + u8 i; + + for (i = 0; i < cam->nbuffers; i++) { + cam->requested_frame[i] = NULL; + cam->frame[i].queued = 0; + cam->frame[i].status = F_UNUSED; + } +} + + +/*-------------------------------------------------------------------------- + Add a frame in the FIFO list of requested frames. + This function is called in process context. + --------------------------------------------------------------------------*/ +static void w9968cf_push_frame(struct w9968cf_device* cam, u8 f_num) +{ + u8 f; + unsigned long lock_flags; + + spin_lock_irqsave(&cam->flist_lock, lock_flags); + + for (f=0; cam->requested_frame[f] != NULL; f++); + cam->requested_frame[f] = &cam->frame[f_num]; + cam->frame[f_num].queued = 1; + cam->frame[f_num].status = F_UNUSED; /* clear the status */ + + spin_unlock_irqrestore(&cam->flist_lock, lock_flags); + + DBG(6, "Frame #%d pushed into the FIFO list. Position %d.", f_num, f) +} + + +/*-------------------------------------------------------------------------- + Read, store and remove the first pointer in the FIFO list of requested + frames. This function is called in interrupt context. + --------------------------------------------------------------------------*/ +static void +w9968cf_pop_frame(struct w9968cf_device* cam, struct w9968cf_frame_t** framep) +{ + u8 i; + + spin_lock(&cam->flist_lock); + + *framep = cam->requested_frame[0]; + + /* Shift the list of pointers */ + for (i = 0; i < cam->nbuffers-1; i++) + cam->requested_frame[i] = cam->requested_frame[i+1]; + cam->requested_frame[i] = NULL; + + spin_unlock(&cam->flist_lock); + + DBG(6,"Popped frame #%d from the list.",*framep-&cam->frame[0]) +} + + +/*-------------------------------------------------------------------------- + High-level video post-processing routine on grabbed frames. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_postprocess_frame(struct w9968cf_device* cam, + struct w9968cf_frame_t* fr) +{ + void *pIn = fr->buffer, *pOut = cam->vpp_buffer, *tmp; + u16 w = cam->window.width, + h = cam->window.height, + d = cam->picture.depth, + fmt = cam->picture.palette, + rgb = cam->force_rgb, + hw_w = cam->hw_width, + hw_h = cam->hw_height, + hw_d = cam->hw_depth; + int err = 0; + + #define _PSWAP(pIn, pOut) {tmp = (pIn); (pIn) = (pOut); (pOut) = tmp;} + + 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) + 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)) + 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.") + } + + if (cam->vpp_flag & VPP_UPSCALE) { + (*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", + 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); + fr->length = (w*h*d)/8; + _PSWAP(pIn, pOut) + DBG(6, "UYVY-16bit to %s conversion done.", + symbolic(v4l1_plist, fmt)) + } + + if (pOut == fr->buffer) + memcpy(fr->buffer, cam->vpp_buffer, fr->length); + + return 0; +} + + + +/**************************************************************************** + * CMOS sensor control routines * + ****************************************************************************/ + +static int +w9968cf_sensor_set_control(struct w9968cf_device* cam, int cid, int val) +{ + struct ovcamchip_control ctl; + int rc; + + ctl.id = cid; + ctl.value = val; + + rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl); + + return rc; +} + +static int +w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int *val) +{ + struct ovcamchip_control ctl; + int rc; + + ctl.id = cid; + + rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl); + if (rc >= 0) + *val = ctl.value; + + return rc; +} + + +static inline int +w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void *arg) +{ + struct i2c_client* c = cam->sensor_client; + + DBG(6, "Executing CMOS sensor command...") + + if (c && c->driver->command) + return c->driver->command(cam->sensor_client, cmd, arg); + else + return -ENODEV; +} + + +/*-------------------------------------------------------------------------- + Change some settings of the CMOS sensor. + Returns: 0 for success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_sensor_change_settings(struct w9968cf_device* cam) +{ + int rc; + + /* Auto brightness */ + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT, + cam->auto_brt); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + /* Auto exposure */ + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP, + cam->auto_exp); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + /* Banding filter */ + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT, + cam->bandfilt); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + /* Light frequency */ + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ, + cam->lightfreq); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + /* Back light */ + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT, + cam->backlight); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + /* Mirror */ + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR, + cam->mirror); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + return 0; +} + + +/*-------------------------------------------------------------------------- + Get some current picture settings from the CMOS sensor. + Returns: 0 for success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_sensor_get_picture(struct w9968cf_device* cam, + struct video_picture* pict) +{ + int rc, v; + + /* Don't return error if a setting is unsupported, or rest of settings + will not be performed */ + + rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + pict->contrast = v; + + rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + pict->brightness = v; + + rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + pict->colour = v; + + rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + pict->hue = v; + + pict->whiteness = W9968CF_WHITENESS; /* to do! */ + + DBG(5, "Got picture settings from the CMOS sensor.") + + PDBGG("Brightness, contrast, hue, colour, whiteness are " + "%d,%d,%d,%d,%d.", pict->brightness, pict->contrast, + pict->hue, pict->colour, pict->whiteness) + + return 0; +} + + +/*-------------------------------------------------------------------------- + Change picture settings of the CMOS sensor. + Returns: 0 for success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_sensor_set_picture(struct w9968cf_device* cam, + struct video_picture pict) +{ + int rc; + + rc = w9968cf_sensor_set_control(cam,OVCAMCHIP_CID_CONT, pict.contrast); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + if (!cam->auto_brt) { + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT, + pict.brightness); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + } + + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT, pict.colour); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE, pict.hue); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + PDBGG("Brightness, contrast, hue, colour, whiteness are " + "%d,%d,%d,%d,%d.", pict.brightness, pict.contrast, + pict.hue, pict.colour, pict.whiteness) + + return 0; +} + + + +/**************************************************************************** + * Camera configuration * + ****************************************************************************/ + +/*-------------------------------------------------------------------------- + This function is called when the CMOS sensor is detected. + --------------------------------------------------------------------------*/ +static void w9968cf_sensor_configure(struct w9968cf_device* cam) +{ + /* NOTE: Make sure width and height are a multiple of 16 */ + + switch (cam->sensor_client->addr) { + case OV6xx0_SID: + cam->maxwidth = 352; + cam->maxheight = 288; + cam->minwidth = 64; + cam->minheight = 48; + break; + case OV7xx0_SID: + cam->maxwidth = 640; + cam->maxheight = 480; + cam->minwidth = 64; + cam->minheight = 48; + break; + } + + /* These values depend on the ones in the ovxxx0.c sources */ + switch (cam->sensor) { + case CC_OV7620: + cam->start_cropx = 287; + cam->start_cropy = 35; + /* Seems to work around a bug in the CMOS sensor */ + cam->vs_polarity = 1; + cam->hs_polarity = 1; + break; + default: + cam->start_cropx = 320; + cam->start_cropy = 35; + cam->vs_polarity = 1; + cam->hs_polarity = 0; + } + + DBG(5, "CMOS sensor %s configured.", symbolic(senlist, cam->sensor)) +} + + +/*-------------------------------------------------------------------------- + Fill some basic fields in the main device data structure. + This function is called once on w9968cf_usb_probe() for each recognized + camera. + --------------------------------------------------------------------------*/ +static void +w9968cf_configure_camera(struct w9968cf_device* cam, + struct usb_device* udev, + enum w9968cf_model_id mod_id, + const unsigned short dev_nr) +{ +#if defined(CONFIG_VIDEO_PROC_FS) + init_MUTEX(&cam->procfs_sem); +#endif + init_MUTEX(&cam->fileop_sem); + init_waitqueue_head(&cam->open); + spin_lock_init(&cam->urb_lock); + spin_lock_init(&cam->flist_lock); + + cam->users = 0; + cam->disconnected = 0; + cam->usbdev = udev; + cam->id = mod_id; + cam->sensor = CC_UNKNOWN; + + strcpy(cam->v4ldev.name, symbolic(camlist, mod_id)); + cam->v4ldev.type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + cam->v4ldev.hardware = VID_HARDWARE_W9968CF; + cam->v4ldev.fops = &w9968cf_fops; + cam->v4ldev.priv = (void*)cam; + cam->v4ldev.minor = video_nr[dev_nr]; + + /* Calculate the alternate setting number (from 1 to 16) + according to the 'packet_size' module parameter */ + if (packet_size[dev_nr] < W9968CF_MIN_PACKET_SIZE) + packet_size[dev_nr] = W9968CF_MIN_PACKET_SIZE; + for (cam->altsetting = 1; + packet_size[dev_nr] < wMaxPacketSize[cam->altsetting-1]; + cam->altsetting++); + + cam->max_buffers = (max_buffers[dev_nr] < 2 || + max_buffers[dev_nr] > W9968CF_MAX_BUFFERS) + ? W9968CF_BUFFERS : max_buffers[dev_nr]; + + cam->double_buffer = (double_buffer[dev_nr] == 0 || + double_buffer[dev_nr] == 1) + ? double_buffer[dev_nr] : W9968CF_DOUBLE_BUFFER; + + cam->clamping = (clamping[dev_nr] == 0 || clamping[dev_nr] == 1) + ? clamping[dev_nr] : W9968CF_CLAMPING; + + cam->filter_type = (filter_type[dev_nr] == 0 || + filter_type[dev_nr] == 1 || + filter_type[dev_nr] == 2) + ? filter_type[dev_nr] : W9968CF_FILTER_TYPE; + + cam->capture = 1; + + cam->largeview = (largeview[dev_nr] == 0 || largeview[dev_nr] == 1) + ? largeview[dev_nr] : W9968CF_LARGEVIEW; + + cam->decompression = (decompression[dev_nr] == 0 || + decompression[dev_nr] == 1 || + decompression[dev_nr] == 2) + ? decompression[dev_nr] : W9968CF_DECOMPRESSION; + + cam->upscaling = (upscaling[dev_nr] == 0 || + upscaling[dev_nr] == 1) + ? upscaling[dev_nr] : W9968CF_UPSCALING; + + cam->auto_brt = (autobright[dev_nr] == 0 || autobright[dev_nr] == 1) + ? autobright[dev_nr] : W9968CF_AUTOBRIGHT; + + cam->auto_exp = (autoexp[dev_nr] == 0 || autoexp[dev_nr] == 1) + ? autoexp[dev_nr] : W9968CF_AUTOEXP; + + cam->lightfreq = (lightfreq[dev_nr] == 50 || lightfreq[dev_nr] == 60) + ? lightfreq[dev_nr] : W9968CF_LIGHTFREQ; + + cam->bandfilt = (bandingfilter[dev_nr] == 0 || + bandingfilter[dev_nr] == 1) + ? bandingfilter[dev_nr] : W9968CF_BANDINGFILTER; + + cam->backlight = (backlight[dev_nr] == 0 || backlight[dev_nr] == 1) + ? backlight[dev_nr] : W9968CF_BACKLIGHT; + + cam->clockdiv = (clockdiv[dev_nr] == -1 || clockdiv[dev_nr] >= 0) + ? clockdiv[dev_nr] : W9968CF_CLOCKDIV; + + cam->mirror = (mirror[dev_nr] == 0 || mirror[dev_nr] == 1) + ? mirror[dev_nr] : W9968CF_MIRROR; + + cam->sensor_mono = (sensor_mono[dev_nr]==0 || sensor_mono[dev_nr]==1) + ? sensor_mono[dev_nr] : W9968CF_SENSOR_MONO; + + cam->picture.brightness = brightness[dev_nr]; + cam->picture.hue = hue[dev_nr]; + cam->picture.colour = colour[dev_nr]; + cam->picture.contrast = contrast[dev_nr]; + cam->picture.whiteness = whiteness[dev_nr]; + if (w9968cf_valid_palette(force_palette[dev_nr])) { + cam->picture.palette = force_palette[dev_nr]; + cam->force_palette = 1; + } else { + cam->force_palette = 0; + if (cam->decompression == 0) + cam->picture.palette = W9968CF_PALETTE_DECOMP_OFF; + else if (cam->decompression == 1) + cam->picture.palette = W9968CF_PALETTE_DECOMP_FORCE; + else + cam->picture.palette = W9968CF_PALETTE_DECOMP_ON; + } + + cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1) + ? force_rgb[dev_nr] : W9968CF_FORCE_RGB; + + cam->window.x = 0; + cam->window.y = 0; + cam->window.width = W9968CF_WIDTH; + cam->window.height = W9968CF_HEIGHT; + cam->window.chromakey = 0; + 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:", + symbolic(camlist, cam->id), dev_nr) + + DBG(3, "- Data packet size for USB isochrnous transfer: %d bytes.", + wMaxPacketSize[cam->altsetting-1]) + + DBG(3, "- Number of requested video frame buffers: %d", + cam->max_buffers) + + if (cam->double_buffer) + DBG(3, "- Hardware double buffering enabled.") + else + DBG(3, "- Hardware double buffering disabled.") + + if (cam->filter_type == 0) + DBG(3, "- Video filtering disabled.") + else if (cam->filter_type == 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.") + + if (cam->clamping) + DBG(3, "- Video data clamping (CCIR-601 format) enabled.") + else + DBG(3, "- Video data clamping (CCIR-601 format) disabled.") + + if (cam->largeview) + DBG(3, "- Large view enabled.") + else + DBG(3, "- Large view disabled.") + + if ((cam->decompression) == 0 && (!cam->force_palette)) + DBG(3, "- Decompression disabled.") + else if ((cam->decompression) == 1 && (!cam->force_palette)) + DBG(3, "- Decompression forced.") + else if ((cam->decompression) == 2 && (!cam->force_palette)) + DBG(3, "- Decompression allowed.") + + if (cam->upscaling) + DBG(3, "- Software image scaling enabled.") + else + DBG(3, "- Software image scaling disabled.") + + if (cam->force_palette) + 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.") + + if (cam->auto_brt) + DBG(3, "- Auto brightness enabled.") + else + DBG(3, "- Auto brightness disabled.") + + if (cam->auto_exp) + DBG(3, "- Auto exposure enabled.") + else + DBG(3, "- Auto exposure disabled.") + + if (cam->backlight) + DBG(3, "- Backlight exposure algorithm enabled.") + else + DBG(3, "- Backlight exposure algorithm disabled.") + + if (cam->mirror) + DBG(3, "- Mirror enabled.") + else + DBG(3, "- Mirror disabled.") + + if (cam->bandfilt) + DBG(3, "- Banding filter enabled.") + else + DBG(3, "- Banding filter disabled.") + + DBG(3, "- Power lighting frequency: %d", cam->lightfreq) + + if (cam->clockdiv == -1) + DBG(3, "- Automatic clock divisor enabled.") + else + DBG(3, "- Clock divisor: %d", cam->clockdiv) + + if (cam->sensor_mono) + DBG(3, "- CMOS sensor used as monochrome.") + else + DBG(3, "- CMOS sensor not used as monochrome.") +} + + +/*-------------------------------------------------------------------------- + Release the resources used by the driver. + This function is called on disconnect + (or on close if deallocation has been deferred) + --------------------------------------------------------------------------*/ +static void w9968cf_release_resources(struct w9968cf_device* cam) +{ + down(&w9968cf_devlist_sem); + + DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev.minor) + + w9968cf_proc_destroy_dev(cam); + video_unregister_device(&cam->v4ldev); + list_del(&cam->v4llist); + i2c_del_adapter(&cam->i2c_adapter); + w9968cf_deallocate_memory(cam); + kfree(cam->control_buffer); + kfree(cam->data_buffer); + + up(&w9968cf_devlist_sem); + + DBG(5, "Resources released.") +} + + + +/**************************************************************************** + * Video4Linux interface * + ****************************************************************************/ + +static int w9968cf_open(struct inode* inode, struct file* filp) +{ + struct w9968cf_device* cam = + (struct w9968cf_device*)video_devdata(filp)->priv; + int err; + + down(&cam->dev_sem); + + if (cam->sensor == CC_UNKNOWN) { + DBG(2, "No supported CMOS sensor has been detected by the " + "'ovcamchip' module for the %s (/dev/video%d). Make " + "sure it is loaded *before* the 'w9968cf' module.", + symbolic(camlist, cam->id),cam->v4ldev.minor) + up(&cam->dev_sem); + return -ENODEV; + } + + if (cam->users) { + 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); + return -EWOULDBLOCK; + } + up(&cam->dev_sem); + err = wait_event_interruptible(cam->open, cam->disconnected || + (cam->users == 0)); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + down(&cam->dev_sem); + } + + DBG(5, "Opening the %s, /dev/video%d ...", + symbolic(camlist, cam->id), cam->v4ldev.minor) + + cam->streaming = 0; + cam->misconfigured = 0; + + if (!w9968cf_vppmod_present) + w9968cf_vppmod_detect(); + + if ((err = w9968cf_allocate_memory(cam))) + goto deallocate_memory; + + if ((err = w9968cf_init_chip(cam))) + goto deallocate_memory; + + if ((err = w9968cf_start_transfer(cam))) + goto deallocate_memory; + + filp->private_data = (void*)cam; + + cam->users++; + strcpy(cam->command, current->comm); + + init_waitqueue_head(&cam->wait_queue); + + up(&cam->dev_sem); + + DBG(5, "Video device is open.") + return 0; + +deallocate_memory: + w9968cf_deallocate_memory(cam); + DBG(2, "Failed to open the video device.") + up(&cam->dev_sem); + return err; +} + + +static int w9968cf_release(struct inode* inode, struct file* filp) +{ + struct w9968cf_device* cam = + (struct w9968cf_device*)video_devdata(filp)->priv; + + down(&cam->dev_sem); /* prevent disconnect() to be called */ + + w9968cf_stop_transfer(cam); + + if (cam->disconnected) { + w9968cf_release_resources(cam); + up(&cam->dev_sem); + kfree(cam); + return 0; + } + + cam->users--; + w9968cf_deallocate_memory(cam); + + if (waitqueue_active(&cam->open)) + wake_up_interruptible(&cam->open); + + DBG(5, "Video device closed.") + up(&cam->dev_sem); + return 0; +} + + +static ssize_t +w9968cf_read(struct file* filp, char* buf, size_t count, loff_t* f_pos) +{ + struct w9968cf_device* cam = + (struct w9968cf_device*)video_devdata(filp)->priv; + struct w9968cf_frame_t* fr; + int err = 0; + + if (filp->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + + if (down_interruptible(&cam->fileop_sem)) + return -ERESTARTSYS; + + if (cam->disconnected) { + DBG(2, "Device not present.") + up(&cam->fileop_sem); + return -ENODEV; + } + + if (cam->misconfigured) { + DBG(2, "The camera is misconfigured. Close and open it again.") + up(&cam->fileop_sem); + return -EIO; + } + + if (!cam->frame[0].queued) + w9968cf_push_frame(cam, 0); + + if (!cam->frame[1].queued) + w9968cf_push_frame(cam, 1); + + err = wait_event_interruptible(cam->wait_queue, + cam->frame[0].status == F_READY || + cam->frame[1].status == F_READY || + cam->disconnected); + if (err) { + up(&cam->fileop_sem); + return err; + } + if (cam->disconnected) { + up(&cam->fileop_sem); + return -ENODEV; + } + + fr = (cam->frame[0].status == F_READY) ? &cam->frame[0]:&cam->frame[1]; + + if (w9968cf_vppmod_present) + w9968cf_postprocess_frame(cam, fr); + + if (count > fr->length) + count = fr->length; + + if (copy_to_user(buf, fr->buffer, count)) { + fr->status = F_UNUSED; + up(&cam->fileop_sem); + return -EFAULT; + } + *f_pos += count; + + fr->status = F_UNUSED; + + DBG(5, "%d bytes read.", count) + + up(&cam->fileop_sem); + return count; +} + + +static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma) +{ + struct w9968cf_device* cam = + (struct w9968cf_device*)video_devdata(filp)->priv; + + unsigned long vsize = vma->vm_end - vma->vm_start, + psize = cam->nbuffers * w9968cf_get_max_bufsize(cam), + start = vma->vm_start, + pos = (unsigned long)cam->frame[0].buffer, + page; + + if (cam->disconnected) { + DBG(2, "Device not present.") + return -ENODEV; + } + + if (cam->misconfigured) { + DBG(2, "The camera is misconfigured. Close and open it again.") + return -EIO; + } + + PDBGG("mmapping %li bytes...", vsize) + + if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT)) + return -EAGAIN; + + while (vsize > 0) { + page = kvirt_to_pa(pos) + vma->vm_pgoff; + if(remap_page_range(start, page, PAGE_SIZE, vma->vm_page_prot)) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + vsize = (vsize > PAGE_SIZE) ? vsize-PAGE_SIZE : 0; + } + + DBG(5, "mmap method successfully called.") + return 0; +} + + +static int +w9968cf_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, unsigned long arg) +{ + struct w9968cf_device* cam = + (struct w9968cf_device*)video_devdata(filp)->priv; + int err; + + if (down_interruptible(&cam->fileop_sem)) + return -ERESTARTSYS; + + if (cam->disconnected) { + DBG(2, "Device not present.") + up(&cam->fileop_sem); + return -ENODEV; + } + + if (cam->misconfigured) { + DBG(2, "The camera is misconfigured. Close and open it again.") + up(&cam->fileop_sem); + return -EIO; + } + + err = w9968cf_do_ioctl(cam, cmd, (void*)arg); + + up(&cam->fileop_sem); + return err; +} + + +static int +w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg) +{ + const char* v4l1_ioctls[] = { + "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", + "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF", + "SFBUF", "KEY", "GFREQ", "SFREQ", "GAUDIO", "SAUDIO", + "SYNC", "MCAPTURE", "GMBUF", "GUNIT", "GCAPTURE", "SCAPTURE", + "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", "SMICROCODE", + "GVBIFMT", "SVBIFMT" + }; + + #define V4L1_IOCTL(cmd) \ + ((_IOC_NR((cmd)) < sizeof(v4l1_ioctls)/sizeof(char*)) ? \ + v4l1_ioctls[_IOC_NR((cmd))] : "???") + + switch (cmd) { + + case VIDIOCGCAP: /* get video capability */ + { + struct video_capability cap = { + .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES, + .channels = 1, + .audios = 0, + .minwidth = cam->minwidth, + .minheight = cam->minheight, + }; + 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; + + if (copy_to_user(arg, &cap, sizeof(cap))) + return -EFAULT; + + DBG(5, "VIDIOCGCAP successfully called.") + return 0; + } + + case VIDIOCGCHAN: /* get video channel informations */ + { + struct video_channel chan; + if (copy_from_user(&chan, arg, sizeof(chan))) + return -EFAULT; + + if (chan.channel != 0) + return -EINVAL; + + strcpy(chan.name, "Camera"); + chan.tuners = 0; + chan.flags = 0; + chan.type = VIDEO_TYPE_CAMERA; + chan.norm = VIDEO_MODE_AUTO; + + if (copy_to_user(arg, &chan, sizeof(chan))) + return -EFAULT; + + DBG(5, "VIDIOCGCHAN successfully called.") + return 0; + } + + case VIDIOCSCHAN: /* set active channel */ + { + struct video_channel chan; + + if (copy_from_user(&chan, arg, sizeof(chan))) + return -EFAULT; + + if (chan.channel != 0) + return -EINVAL; + + DBG(5, "VIDIOCSCHAN successfully called.") + return 0; + } + + case VIDIOCGPICT: /* get image properties of the picture */ + { + struct video_picture pict; + + if (w9968cf_sensor_get_picture(cam, &pict)) + return -EIO; + + pict.depth = cam->picture.depth; + pict.palette = cam->picture.palette; + + if (copy_to_user(arg, &pict, sizeof(pict))) + return -EFAULT; + + DBG(5, "VIDIOCGPICT successfully called.") + return 0; + } + + case VIDIOCSPICT: /* change picture settings */ + { + struct video_picture pict; + int err = 0; + + if (copy_from_user(&pict, arg, sizeof(pict))) + return -EFAULT; + + if ( (cam->force_palette || !w9968cf_vppmod_present) + && pict.palette != cam->picture.palette ) { + 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.", + symbolic(v4l1_plist, pict.palette)) + return -EINVAL; + } + + if (pict.depth != w9968cf_valid_depth(pict.palette)) { + DBG(4, "Depth %d bpp is not supported for %s palette. " + "VIDIOCSPICT failed.", + pict.depth, symbolic(v4l1_plist, pict.palette)) + return -EINVAL; + } + + if (!cam->force_palette) { + if (cam->decompression == 0) { + if (w9968cf_need_decompression(pict.palette)) { + DBG(4, "Decompression disabled: palette %s is not " + "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.", + symbolic(v4l1_plist, pict.palette)) + return -EINVAL; + } + } + } + + if (pict.palette != cam->picture.palette || + pict.depth != cam->picture.depth) + { + if(*cam->requested_frame + || cam->frame_current->queued) { + err = wait_event_interruptible + ( cam->wait_queue, + cam->disconnected || + (!*cam->requested_frame && + !cam->frame_current->queued) ); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + } + + if (w9968cf_stop_transfer(cam)) + goto ioctl_fail; + + if (w9968cf_set_picture(cam, pict)) + goto ioctl_fail; + + if (w9968cf_start_transfer(cam)) + goto ioctl_fail; + + } else if ( ((pict.brightness != cam->picture.brightness) && + (!cam->auto_brt)) || + pict.hue != cam->picture.hue || + pict.colour != cam->picture.colour || + pict.contrast != cam->picture.contrast || + pict.whiteness != cam->picture.whiteness ) { + if (w9968cf_sensor_set_picture(cam, pict)) + return -EIO; + } + + DBG(5, "VIDIOCSPICT successfully called.") + return 0; + } + + case VIDIOCSWIN: /* set capture area */ + { + struct video_window win; + int err = 0; + + 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, + win.x, win.y, win.width, win.height) + + if (win.clipcount != 0 || win.flags != 0) + return -EINVAL; + + 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) + return err; + } + + if (win.x != cam->window.x || + win.y != cam->window.y || + win.width != cam->window.width || + win.height != cam->window.height) { + + if(*cam->requested_frame + || cam->frame_current->queued) { + err = wait_event_interruptible + ( cam->wait_queue, + cam->disconnected || + (!*cam->requested_frame && + !cam->frame_current->queued) ); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + } + + if (w9968cf_stop_transfer(cam)) + goto ioctl_fail; + + /* This _must_ be called before set_window() */ + if (w9968cf_set_picture(cam, cam->picture)) + goto ioctl_fail; + + if (w9968cf_set_window(cam, win)) + goto ioctl_fail; + + if (w9968cf_start_transfer(cam)) + goto ioctl_fail; + } + + DBG(5, "VIDIOCSWIN successfully called. ") + return 0; + } + + case VIDIOCGWIN: /* get current window properties */ + { + if (copy_to_user(arg,&cam->window,sizeof(struct video_window))) + return -EFAULT; + + DBG(5, "VIDIOCGWIN successfully called.") + return 0; + } + + case VIDIOCGMBUF: /* request for memory (mapped) buffer */ + { + struct video_mbuf mbuf; + u8 i; + + mbuf.size = cam->nbuffers * w9968cf_get_max_bufsize(cam); + 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(arg, &mbuf, sizeof(mbuf))) + return -EFAULT; + + DBG(5, "VIDIOCGMBUF successfully called.") + return 0; + } + + case VIDIOCMCAPTURE: /* start the capture to a frame */ + { + struct video_mmap mmap; + struct w9968cf_frame_t* fr; + int err = 0; + + if (copy_from_user(&mmap, arg, sizeof(mmap))) + return -EFAULT; + + DBG(6, "VIDIOCMCAPTURE called: frame #%d, 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) + return -EINVAL; + } + + if (mmap.format!=cam->picture.palette && + (cam->force_palette || !w9968cf_vppmod_present)) { + DBG(4, "Palette %s rejected. Only %s is allowed.", + symbolic(v4l1_plist, mmap.format), + symbolic(v4l1_plist, cam->picture.palette)) + return -EINVAL; + } + + if (!w9968cf_valid_palette(mmap.format)) { + DBG(4, "Palette %s not supported. " + "VIDIOCMCAPTURE failed.", + symbolic(v4l1_plist, mmap.format)) + return -EINVAL; + } + + if (!cam->force_palette) { + if (cam->decompression == 0) { + if (w9968cf_need_decompression(mmap.format)) { + DBG(4, "Decompression disabled: palette %s is not " + "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.", + symbolic(v4l1_plist, mmap.format)) + return -EINVAL; + } + } + } + + if (w9968cf_adjust_window_size(cam, (u16*)&mmap.width, + (u16*)&mmap.height)) { + DBG(4, "Resolution not supported (%dx%d). " + "VIDIOCMCAPTURE failed.", + mmap.width, mmap.height) + return -EINVAL; + } + + fr = &cam->frame[mmap.frame]; + + if (mmap.width != cam->window.width || + mmap.height != cam->window.height || + mmap.format != cam->picture.palette) { + + struct video_window win; + struct video_picture pict; + + if(*cam->requested_frame + || cam->frame_current->queued) { + DBG(6, "VIDIOCMCAPTURE. Change settings for " + "frame #%d: %dx%d, format %s. Wait...", + mmap.frame, mmap.width, mmap.height, + symbolic(v4l1_plist, mmap.format)) + err = wait_event_interruptible + ( cam->wait_queue, + cam->disconnected || + (!*cam->requested_frame && + !cam->frame_current->queued) ); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + } + + memcpy(&win, &cam->window, sizeof(win)); + memcpy(&pict, &cam->picture, sizeof(pict)); + win.width = mmap.width; + win.height = mmap.height; + pict.palette = mmap.format; + + if (w9968cf_stop_transfer(cam)) + goto ioctl_fail; + + /* This before set_window */ + if (w9968cf_set_picture(cam, pict)) + goto ioctl_fail; + + if (w9968cf_set_window(cam, win)) + goto ioctl_fail; + + if (w9968cf_start_transfer(cam)) + goto ioctl_fail; + + } else if (fr->queued) { + + DBG(6, "Wait until frame #%d is free.", mmap.frame) + + err = wait_event_interruptible(cam->wait_queue, + cam->disconnected || + (!fr->queued)); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + } + + w9968cf_push_frame(cam, mmap.frame); + DBG(5, "VIDIOCMCAPTURE(%d): successfully called.", mmap.frame) + return 0; + } + + case VIDIOCSYNC: /* wait until the capture of a frame is finished */ + { + unsigned int f_num = *((unsigned int *) arg); + struct w9968cf_frame_t* fr; + int err = 0; + + if (f_num >= cam->nbuffers) { + DBG(4, "Invalid frame number (%d). " + "VIDIOCMCAPTURE failed.", f_num) + return -EINVAL; + } + + DBG(6, "VIDIOCSYNC called for frame #%d", f_num) + + fr = &cam->frame[f_num]; + + switch (fr->status) { + case F_UNUSED: + if (!fr->queued) { + DBG(4, "VIDIOSYNC: Frame #%d not requested!", + f_num) + return -EFAULT; + } + case F_ERROR: + case F_GRABBING: + err = wait_event_interruptible(cam->wait_queue, + (fr->status == F_READY) + || cam->disconnected); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + break; + case F_READY: + break; + } + + if (w9968cf_vppmod_present) + w9968cf_postprocess_frame(cam, fr); + + fr->status = F_UNUSED; + + DBG(5, "VIDIOCSYNC(%d) successfully called.", f_num) + return 0; + } + + case VIDIOCGUNIT:/* report the unit numbers of the associated devices*/ + { + struct video_unit unit = { + .video = cam->v4ldev.minor, + .vbi = VIDEO_NO_UNIT, + .radio = VIDEO_NO_UNIT, + .audio = VIDEO_NO_UNIT, + .teletext = VIDEO_NO_UNIT, + }; + + if (copy_to_user(arg, &unit, sizeof(unit))) + return -EFAULT; + + DBG(5, "VIDIOCGUNIT successfully called.") + return 0; + } + + case VIDIOCKEY: + return 0; + + case VIDIOCGFBUF: + { + struct video_buffer* buffer = (struct video_buffer*)arg; + + memset(buffer, 0, sizeof(struct video_buffer)); + + DBG(5, "VIDIOCGFBUF successfully called.") + return 0; + } + + case VIDIOCGTUNER: + { + struct video_tuner tuner; + if (copy_from_user(&tuner, arg, sizeof(tuner))) + return -EFAULT; + + if (tuner.tuner != 0); + return -EINVAL; + + strcpy(tuner.name, "no_tuner"); + tuner.rangelow = 0; + tuner.rangehigh = 0; + tuner.flags = VIDEO_TUNER_NORM; + tuner.mode = VIDEO_MODE_AUTO; + tuner.signal = 0xffff; + + if (copy_to_user(arg, &tuner, sizeof(tuner))) + return -EFAULT; + + DBG(5, "VIDIOCGTUNER successfully called.") + return 0; + } + + case VIDIOCSTUNER: + { + struct video_tuner tuner; + if (copy_from_user(&tuner, arg, sizeof(tuner))) + return -EFAULT; + + if (tuner.tuner != 0) + return -EINVAL; + + if (tuner.mode != VIDEO_MODE_AUTO) + return -EINVAL; + + DBG(5, "VIDIOCSTUNER successfully called.") + return 0; + } + + case VIDIOCSFBUF: + case VIDIOCCAPTURE: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + case VIDIOCSPLAYMODE: + case VIDIOCSWRITEMODE: + case VIDIOCGPLAYINFO: + case VIDIOCSMICROCODE: + case VIDIOCGVBIFMT: + case VIDIOCSVBIFMT: + DBG(4, "Unsupported V4L1 IOCtl: VIDIOC%s " + "(type 0x%01X, " + "n. 0x%01X, " + "dir. 0x%01X, " + "size 0x%02X).", + V4L1_IOCTL(cmd), + _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd)) + + return -EINVAL; + + default: + DBG(4, "Invalid V4L1 IOCtl: VIDIOC%s " + "type 0x%01X, " + "n. 0x%01X, " + "dir. 0x%01X, " + "size 0x%02X.", + V4L1_IOCTL(cmd), + _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd)) + + return -ENOIOCTLCMD; + + } /* end of switch */ + +ioctl_fail: + cam->misconfigured = 1; + DBG(1, "VIDIOC%s failed because of hardware problems. " + "To use the camera, close and open it again.", V4L1_IOCTL(cmd)) + return -EFAULT; +} + + +static struct file_operations w9968cf_fops = { + .owner = THIS_MODULE, + .open = w9968cf_open, + .release = w9968cf_release, + .read = w9968cf_read, + .ioctl = w9968cf_ioctl, + .mmap = w9968cf_mmap, + .llseek = no_llseek, +}; + + + +/**************************************************************************** + * USB probe and V4L registration, disconnect and id_table[] definition * + ****************************************************************************/ + +static void* +w9968cf_usb_probe(struct usb_device* udev, + unsigned int ifnum, const struct usb_device_id* id) +{ + struct w9968cf_device* cam; + int err = 0; + enum w9968cf_model_id mod_id; + struct list_head* ptr; + u8 sc = 0; /* number of simultaneous cameras */ + static unsigned short dev_nr = 0; /* we are handling device number n */ + + 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 NULL; + + /* We don't handle multi-config cameras */ + if (udev->descriptor.bNumConfigurations != 1) + return NULL; + + DBG(2, "%s detected.", symbolic(camlist, mod_id)) + + if (simcams > W9968CF_MAX_DEVICES) + simcams = W9968CF_SIMCAMS; + + /* How many cameras are connected ? */ + down(&w9968cf_devlist_sem); + list_for_each(ptr, &w9968cf_dev_list) + sc++; + up(&w9968cf_devlist_sem); + + if (sc >= simcams) { + DBG(2, "Device rejected: too many connected cameras " + "(max. %d)", simcams) + return NULL; + } + + err = usb_set_configuration(udev, 1); + err += usb_set_interface(udev, 0, 0); + + if (err) { + DBG(1, "Device configuration failed.") + return NULL; + } + + cam = (struct w9968cf_device*) + kmalloc(sizeof(struct w9968cf_device), GFP_KERNEL); + + if (!cam) { + DBG(1, "Couldn't allocate %d bytes of kernel memory.", + sizeof(struct w9968cf_device)) + err = -ENOMEM; + 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.") + err = -ENOMEM; + goto fail; + } + memset(cam->control_buffer, 0, 2); + + /* 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.") + err = -ENOMEM; + goto fail; + } + memset(cam->data_buffer, 0, 8); + + /* Set some basic constants */ + w9968cf_configure_camera(cam, udev, mod_id, dev_nr); + + err = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, + video_nr[dev_nr]); + if (err) { + DBG(1, "V4L device registration failed.") + if (err == -ENFILE && video_nr[dev_nr] == -1) + 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) + + /* Ok, add a new entry into the list of V4L registered devices */ + down(&w9968cf_devlist_sem); + list_add(&cam->v4llist, &w9968cf_dev_list); + up(&w9968cf_devlist_sem); + + dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0; + + w9968cf_turn_on_led(cam); + + w9968cf_i2c_init(cam); + + w9968cf_proc_create_dev(cam); + + up(&cam->dev_sem); + return (void*)cam; + +fail: /* Free unused memory */ + if (cam) { + if (cam->control_buffer) + kfree(cam->control_buffer); + if (cam->data_buffer) + kfree(cam->data_buffer); + up(&cam->dev_sem); + kfree(cam); + } + return NULL; +} + + +static void w9968cf_usb_disconnect(struct usb_device* udev, void* drv_context) +{ + struct w9968cf_device* cam = (struct w9968cf_device*)drv_context; + + 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); + + 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); + } else + w9968cf_release_resources(cam); + + up(&cam->dev_sem); + + if (!cam->users) + kfree(cam); + } +} + + +static struct usb_driver w9968cf_usb_driver = { + .owner = THIS_MODULE, + .name = "w9968cf", + .id_table = winbond_id_table, + .probe = w9968cf_usb_probe, + .disconnect = w9968cf_usb_disconnect, +}; + + + +/**************************************************************************** + * Module init, exit and intermodule communication * + ****************************************************************************/ + +static int w9968cf_vppmod_detect(void) +{ + w9968cf_vpp_init_decoder = inter_module_get("w9968cf_init_decoder"); + + if (!w9968cf_vpp_init_decoder) { + 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; + } + } + + 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_vppmod_present = 1; + + /* Initialization */ + (*w9968cf_vpp_init_decoder)(); + + DBG(2, "Video post-processing module detected.") + return 0; +} + + +static void w9968cf_vppmod_release(void) +{ + 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"); + + DBG(2, "Video post-processing module released.") +} + + +static int __init w9968cf_module_init(void) +{ + int err; + + DBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION) + DBG(3, W9968CF_MODULE_AUTHOR) + + init_MUTEX(&w9968cf_devlist_sem); + + w9968cf_proc_create(); + + w9968cf_vppmod_detect(); + + if ((err = usb_register(&w9968cf_usb_driver))) { + if (w9968cf_vppmod_present) + w9968cf_vppmod_release(); + w9968cf_proc_destroy(); + return err; + } + + return 0; +} + + +static void __exit w9968cf_module_exit(void) +{ + /* w9968cf_usb_disconnect() will be called */ + usb_deregister(&w9968cf_usb_driver); + + w9968cf_proc_destroy(); + + if (w9968cf_vppmod_present) + w9968cf_vppmod_release(); + + DBG(2, W9968CF_MODULE_NAME" deregistered.") +} + + +module_init(w9968cf_module_init); +module_exit(w9968cf_module_exit); diff -urN linux-2.4.22-bk41/drivers/usb/w9968cf.h linux-2.4.22-bk42/drivers/usb/w9968cf.h --- linux-2.4.22-bk41/drivers/usb/w9968cf.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/w9968cf.h 2003-10-26 02:49:55.000000000 -0800 @@ -0,0 +1,321 @@ +/*************************************************************************** + * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. * + * * + * Copyright (C) 2002 2003 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_H_ +#define _W9968CF_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "w9968cf_externaldef.h" + + +/**************************************************************************** + * Default values * + ****************************************************************************/ + +#define W9968CF_VPPMOD_LOAD 1 /* automatic 'w9968cf-vpp' module loading */ + +/* Comment/uncomment the following line to enable/disable debugging messages */ +#define W9968CF_DEBUG + +/* These have effect only if W9968CF_DEBUG is defined */ +#define W9968CF_DEBUG_LEVEL 2 /* from 0 to 6. 0 for no debug informations */ +#define W9968CF_SPECIFIC_DEBUG 0 /* 0 or 1 */ + +#define W9968CF_MAX_DEVICES 32 +#define W9968CF_SIMCAMS W9968CF_MAX_DEVICES /* simultaneous cameras */ + +#define W9968CF_MAX_BUFFERS 32 +#define W9968CF_BUFFERS 2 /* n. of frame buffers from 2 to MAX_BUFFERS */ + +/* Maximum data payload sizes in bytes for alternate settings */ +static const u16 wMaxPacketSize[] = {1023, 959, 895, 831, 767, 703, 639, 575, + 511, 447, 383, 319, 255, 191, 127, 63}; +#define W9968CF_PACKET_SIZE 1023 /* according to wMaxPacketSizes[] */ +#define W9968CF_MIN_PACKET_SIZE 63 /* minimum value */ +#define W9968CF_ISO_PACKETS 5 /* n.of packets for isochronous transfers */ +#define W9968CF_USB_CTRL_TIMEOUT HZ /* timeout for usb control commands */ +#define W9968CF_URBS 2 /* n. of scheduled URBs for ISO transfer */ + +#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */ +#define W9968CF_I2C_RW_RETRIES 15 /* number of max I2C r/w retries */ + +/* Available video formats */ +struct w9968cf_format { + const u16 palette; + const u16 depth; + const u8 compression; +}; + +static const struct w9968cf_format w9968cf_formatlist[] = { + { VIDEO_PALETTE_UYVY, 16, 0 }, /* original video */ + { VIDEO_PALETTE_YUV422P, 16, 1 }, /* with JPEG compression */ + { VIDEO_PALETTE_YUV420P, 12, 1 }, /* with JPEG compression */ + { VIDEO_PALETTE_YUV420, 12, 1 }, /* same as YUV420P */ + { VIDEO_PALETTE_YUYV, 16, 0 }, /* software conversion */ + { VIDEO_PALETTE_YUV422, 16, 0 }, /* software conversion */ + { VIDEO_PALETTE_GREY, 8, 0 }, /* software conversion */ + { VIDEO_PALETTE_RGB555, 16, 0 }, /* software conversion */ + { VIDEO_PALETTE_RGB565, 16, 0 }, /* software conversion */ + { VIDEO_PALETTE_RGB24, 24, 0 }, /* software conversion */ + { VIDEO_PALETTE_RGB32, 32, 0 }, /* software conversion */ + { 0, 0, 0 } /* 0 is a terminating entry */ +}; + +#define W9968CF_DECOMPRESSION 2 /* decomp:0=disable,1=force,2=any formats */ +#define W9968CF_PALETTE_DECOMP_OFF VIDEO_PALETTE_UYVY /* when decomp=0 */ +#define W9968CF_PALETTE_DECOMP_FORCE VIDEO_PALETTE_YUV420P /* when decomp=1 */ +#define W9968CF_PALETTE_DECOMP_ON VIDEO_PALETTE_UYVY /* when decomp=2 */ + +#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_WIDTH 320 /* from 128 to 352, multiple of 16 */ +#define W9968CF_HEIGHT 240 /* from 96 to 288, multiple of 16 */ + +#define W9968CF_CLAMPING 0 /* 0 disable, 1 enable video data clamping */ +#define W9968CF_FILTER_TYPE 0 /* 0 disable 1 (1-2-1), 2 (2-3-6-3-2) */ +#define W9968CF_DOUBLE_BUFFER 1 /* 0 disable, 1 enable double buffer */ +#define W9968CF_LARGEVIEW 1 /* 0 disable, 1 enable */ +#define W9968CF_UPSCALING 0 /* 0 disable, 1 enable */ + +#define W9968CF_SENSOR_MONO 0 /* 0 not monochrome, 1 monochrome sensor */ +#define W9968CF_BRIGHTNESS 31000 /* from 0 to 65535 */ +#define W9968CF_HUE 32768 /* from 0 to 65535 */ +#define W9968CF_COLOUR 32768 /* from 0 to 65535 */ +#define W9968CF_CONTRAST 50000 /* from 0 to 65535 */ +#define W9968CF_WHITENESS 32768 /* from 0 to 65535 */ + +#define W9968CF_AUTOBRIGHT 0 /* 0 disable, 1 enable automatic brightness */ +#define W9968CF_AUTOEXP 1 /* 0 disable, 1 enable automatic exposure */ +#define W9968CF_LIGHTFREQ 50 /* light frequency. 50Hz (Europe) or 60Hz */ +#define W9968CF_BANDINGFILTER 0 /* 0 disable, 1 enable banding filter */ +#define W9968CF_BACKLIGHT 0 /* 0 or 1, 1=object is lit from behind */ +#define W9968CF_MIRROR 0 /* 0 or 1 [don't] reverse image horizontally*/ + +#define W9968CF_CLOCKDIV -1 /* -1 = automatic clock divisor */ +#define W9968CF_DEF_CLOCKDIVISOR 0 /* default sensor clock divisor value */ + + +/**************************************************************************** + * Globals * + ****************************************************************************/ + +#define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \ + "Dual Mode Camera Chip" +#define W9968CF_MODULE_VERSION "v1.22" +#define W9968CF_MODULE_AUTHOR "(C) 2002 2003 Luca Risolia" +#define W9968CF_AUTHOR_EMAIL "" + +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 */ + USB_DEVICE(0x041e, 0x4003), + .driver_info = (unsigned long)"w9968cf", + }, + { + /* Generic W996[87]CF JPEG USB Dual Mode Camera */ + USB_DEVICE(0x1046, 0x9967), + .driver_info = (unsigned long)"w9968cf", + }, + { } /* 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_EEEMC = 40, /* Ezonics EZ-802 EZMega Cam */ + W9968CF_MOD_ODPVDMPC = 43,/* OPCOM Digi Pen VGA Dual Mode Pen Camera */ +}; + +enum w9968cf_frame_status { + F_READY, /* finished grabbing & ready to be read/synced */ + F_GRABBING, /* in the process of being grabbed into */ + F_ERROR, /* something bad happened while processing */ + F_UNUSED /* unused (no VIDIOCMCAPTURE) */ +}; + +struct w9968cf_frame_t { + void* buffer; + u32 length; + enum w9968cf_frame_status status; + struct w9968cf_frame_t* next; + u8 queued; +}; + +enum w9968cf_vpp_flag { + VPP_NONE = 0x00, + VPP_UPSCALE = 0x01, + VPP_SWAP_YUV_BYTES = 0x02, + VPP_DECOMPRESSION = 0x04, + VPP_UYVY_TO_RGBX = 0x08, +}; + +struct list_head w9968cf_dev_list; /* head of V4L registered cameras list */ +LIST_HEAD(w9968cf_dev_list); +struct semaphore w9968cf_devlist_sem; /* semaphore for list traversal */ + +/* Main device driver structure */ +struct w9968cf_device { + enum w9968cf_model_id id; /* private device identifier */ + + struct video_device v4ldev; /* V4L structure */ + struct list_head v4llist; /* entry of the list of V4L cameras */ + + struct usb_device* usbdev; /* -> main USB structure */ + struct urb* urb[W9968CF_URBS]; /* -> USB request block structs */ + void* transfer_buffer[W9968CF_URBS]; /* -> ISO transfer buffers */ + u16* control_buffer; /* -> buffer for control req.*/ + 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_current; /* -> frame being grabbed */ + struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS]; + void* vpp_buffer; /* -> helper buffer for post-processing routines */ + + u8 max_buffers, /* number of requested buffers */ + force_palette, /* yes=1/no=0 */ + force_rgb, /* read RGB instead of BGR, yes=1, no=0 */ + double_buffer, /* hardware double buffering yes=1/no=0 */ + clamping, /* video data clamping yes=1/no=0 */ + filter_type, /* 0=disabled, 1=3 tap, 2=5 tap filter */ + capture, /* 0=disabled, 1=enabled */ + largeview, /* 0=disabled, 1=enabled */ + decompression, /* 0=disabled, 1=forced, 2=allowed */ + upscaling; /* software image scaling, 0=enabled, 1=disabled */ + + struct video_picture picture; /* current window settings */ + struct video_window window; /* current picture settings */ + + u16 hw_depth, /* depth (used by the chip) */ + hw_palette, /* palette (used by the chip) */ + hw_width, /* width (used by the chip) */ + hw_height, /* height (used by the chip) */ + hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */ + vs_polarity; /* 0=negative sync pulse, 1=positive sync pulse */ + + enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */ + + u8 nbuffers, /* number of allocated frame buffers */ + altsetting, /* camera alternate setting */ + disconnected, /* flag: yes=1, no=0 */ + misconfigured, /* flag: yes=1, no=0 */ + users, /* flag: number of users holding the device */ + streaming; /* flag: yes=1, no=0 */ + + int sensor; /* type of image CMOS sensor chip (CC_*) */ + + /* Determined by CMOS sensor type */ + u16 maxwidth, + maxheight, + minwidth, + minheight, + start_cropx, + start_cropy; + + u8 auto_brt, /* auto brightness enabled flag */ + auto_exp, /* auto exposure enabled flag */ + backlight, /* backlight exposure algorithm flag */ + mirror, /* image is reversed horizontally */ + lightfreq, /* power (lighting) frequency */ + bandfilt; /* banding filter enabled flag */ + s8 clockdiv; /* clock divisor */ + int sensor_mono; /* CMOS sensor is (probably) monochrome */ + + /* I2C interface to kernel */ + struct i2c_adapter i2c_adapter; + struct i2c_client* sensor_client; + +#if defined(CONFIG_VIDEO_PROC_FS) + /* /proc entries, relative to /proc/video/w9968cf/ */ + struct proc_dir_entry *proc_dev; /* readable per-device entry */ +#endif + + /* Locks */ + struct semaphore dev_sem, /* for probe, disconnect,open and close */ + fileop_sem; /* for read and ioctl */ +#if defined(CONFIG_VIDEO_PROC_FS) + struct semaphore procfs_sem; /* for /proc read/write calls */ +#endif + 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; +}; + +#define W9968CF_HW_BUF_SIZE 640*480*2 /* buf. size for original video frames */ + +#define SENSOR_FORMAT VIDEO_PALETTE_UYVY +#define SENSOR_FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM) + + +/**************************************************************************** + * Macros and other constants * + ****************************************************************************/ + +#undef DBG +#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, \ + __PRETTY_FUNCTION__, __LINE__ , ## args); \ +} \ +} +#else + /* Not debugging: nothing */ +# define DBG(level, fmt, args...) do {;} while(0); +#endif + +#undef PDBG +#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 -urN linux-2.4.22-bk41/drivers/usb/w9968cf_decoder.h linux-2.4.22-bk42/drivers/usb/w9968cf_decoder.h --- linux-2.4.22-bk41/drivers/usb/w9968cf_decoder.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/w9968cf_decoder.h 2003-10-26 02:49:55.000000000 -0800 @@ -0,0 +1,86 @@ +/*************************************************************************** + * Video decoder for the W996[87]CF driver for Linux. * + * * + * Copyright (C) 2003 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_DECODER_H_ +#define _W9968CF_DECODER_H_ + +/* Comment/uncomment this for high/low quality of compressed video */ +#define W9968CF_DEC_FAST_LOWQUALITY_VIDEO + +#ifdef W9968CF_DEC_FAST_LOWQUALITY_VIDEO +static const unsigned char Y_QUANTABLE[64] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; + +static const unsigned char UV_QUANTABLE[64] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; +#else +static const unsigned char Y_QUANTABLE[64] = { + 8, 5, 5, 8, 12, 20, 25, 30, + 6, 6, 7, 9, 13, 29, 30, 27, + 7, 6, 8, 12, 20, 28, 34, 28, + 7, 8, 11, 14, 25, 43, 40, 31, + 9, 11, 18, 28, 34, 54, 51, 38, + 12, 17, 27, 32, 40, 52, 56, 46, + 24, 32, 39, 43, 51, 60, 60, 50, + 36, 46, 47, 49, 56, 50, 51, 49 +}; + +static const unsigned char UV_QUANTABLE[64] = { + 8, 9, 12, 23, 49, 49, 49, 49, + 9, 10, 13, 33, 49, 49, 49, 49, + 12, 13, 28, 49, 49, 49, 49, 49, + 23, 33, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49 +}; +#endif + +#define W9968CF_DEC_ERR_CORRUPTED_DATA -1 +#define W9968CF_DEC_ERR_BUF_OVERFLOW -2 +#define W9968CF_DEC_ERR_NO_SOI -3 +#define W9968CF_DEC_ERR_NO_SOF0 -4 +#define W9968CF_DEC_ERR_NO_SOS -5 +#define W9968CF_DEC_ERR_NO_EOI -6 + +extern void w9968cf_init_decoder(void); +extern int w9968cf_check_headers(const unsigned char* Pin, + const unsigned long BUF_SIZE); +extern int w9968cf_decode(const char* Pin, const unsigned long BUF_SIZE, + const unsigned W, const unsigned H, char* Pout); + +#endif /* _W9968CF_DECODER_H_ */ diff -urN linux-2.4.22-bk41/drivers/usb/w9968cf_externaldef.h linux-2.4.22-bk42/drivers/usb/w9968cf_externaldef.h --- linux-2.4.22-bk41/drivers/usb/w9968cf_externaldef.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/drivers/usb/w9968cf_externaldef.h 2003-10-26 02:49:55.000000000 -0800 @@ -0,0 +1,95 @@ +/*************************************************************************** + * Various definitions for compatibility with external modules. * + * This file is part of the W996[87]CF driver for Linux. * + * * + * Copyright (C) 2002 2003 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_EXTERNALDEF_H_ +#define _W9968CF_EXTERNALDEF_H_ + +#include +#include +#include +#include + +/* The following values have been copied from the "ovcamchip" module. */ + +#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 -urN linux-2.4.22-bk41/include/asm-arm/hc_sl811-hw.h linux-2.4.22-bk42/include/asm-arm/hc_sl811-hw.h --- linux-2.4.22-bk41/include/asm-arm/hc_sl811-hw.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/include/asm-arm/hc_sl811-hw.h 2003-10-26 02:49:55.000000000 -0800 @@ -0,0 +1,139 @@ +/* +File: include/asm-arm/hc_sl811-hw.h +*/ + +/* The base_addr, data_reg_addr, and irq number are board specific. + * The current values are design to run on the Accelent SA1110 IDP + * NOTE: values need to modify for different development boards + */ + +#define SIZEOF_IO_REGION 256 /* Size for request/release region */ +static int base_addr = 0xd3800000; +static int data_reg_addr = 0xd3810000; +static int irq = 34; /* Also change SL811_SET_GPDR! */ +static int gprd = 13; /* also change irq !!! */ + +MODULE_PARM (base_addr, "i"); +MODULE_PARM_DESC (base_addr, "sl811 base address 0xd3800000"); +MODULE_PARM (data_reg_addr, "i"); +MODULE_PARM_DESC (data_reg_addr, "sl811 data register address 0xd3810000"); +MODULE_PARM (irq, "i"); +MODULE_PARM_DESC (irq, "IRQ 34 (default)"); +MODULE_PARM(gprd,"i"); +MODULE_PARM_DESC(gprd,"sl811 GPRD port 13(default)"); + +/* + * Low level: Read from Data port [arm] + */ +static __u8 inline sl811_read_data (hcipriv_t *hp) +{ + __u8 data; + data = readb (hp->hcport2); + rmb (); + return (data); +} + +/* + * Low level: Write to index register [arm] + */ +static void inline sl811_write_index (hcipriv_t *hp, __u8 index) +{ + writeb (offset, hp->hcport); + wmb (); +} + +/* + * Low level: Write to Data port [arm] + */ +static void inline sl811_write_data (hcipriv_t *hp, __u8 data) +{ + writeb (data, hp->hcport2); +} + +/* + * Low level: Write to index register and data port [arm] + */ +static void inline sl811_write_index_data (hcipriv_t *hp, __u8 index, __u8 data) +{ + writeb (offset, hp->hcport); + writeb (data, hp->hcport2); + wmb (); +} + +/***************************************************************** + * + * Function Name: init_irq [arm] + * + * This function is board specific. It sets up the interrupt to + * be an edge trigger and trigger on the rising edge + * + * Input: none + * + * Return value : none + * + *****************************************************************/ +static void inline init_irq (void) +{ + GPDR &= ~(1 << gprd); + set_GPIO_IRQ_edge (1 << gprd, GPIO_RISING_EDGE); +} + +/***************************************************************** + * + * Function Name: release_regions [arm] + * + * This function is board specific. It frees all io address. + * + * Input: hcipriv_t * + * + * Return value : none + * + *****************************************************************/ +static void inline release_regions (hcipriv_t *hp) +{ + if (hp->hcport > 0) { + release_region (hp->hcport, SIZEOF_IO_REGION); + hp->hcport = 0; + } + + if (hp->hcport2 > 0) { + release_region (hp->hcport2, SIZEOF_IO_REGION); + hp->hcport2 = 0; + } +} + +/***************************************************************** + * + * Function Name: request_regions [arm] + * + * This function is board specific. It request all io address and + * maps into memory (if can). + * + * Input: hcipriv_t * + * + * Return value : 0 = OK + * + *****************************************************************/ +static int inline request_regions (hcipriv_t *hp, int addr1, int addr2) +{ + if (!request_region (addr1, SIZEOF_IO_REGION, "SL811 USB HOST")) { + DBGERR ("request address %d failed", addr1); + return -EBUSY; + } + hp->hcport = addr1; + if (!hp->hcport) { + DBGERR ("Error mapping SL811 Memory 0x%x", addr1); + } + + if (!request_region (addr2, SIZEOF_IO_REGION, "SL811 USB HOST")) { + DBGERR ("request address %d failed", addr2); + return -EBUSY; + } + hp->hcport2 = addr2; + if (!hp->hcport2) { + DBGERR ("Error mapping SL811 Memory 0x%x", addr2); + } + + return 0; +} + diff -urN linux-2.4.22-bk41/include/asm-arm/sl811-hw.h linux-2.4.22-bk42/include/asm-arm/sl811-hw.h --- linux-2.4.22-bk41/include/asm-arm/sl811-hw.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/include/asm-arm/sl811-hw.h 2003-10-26 02:49:55.000000000 -0800 @@ -0,0 +1,148 @@ +/* +File: include/asm-arm/sl811-hw.h + +19.09.2003 hne@ist1.de +Use Kernel 2.4.20 and this source from 2.4.22 +Splitt hardware depens into file sl811-x86.h and sl811-arm.h. +Functions as inline. + +23.09.2003 hne +Move Hardware depend header sl811-arm.h into include/asm-arm/sl811-hw.h. +GPRD as parameter. + +24.09.2003 hne +Use Offset from ADDR to DATA instand of direct io. + +03.10.2003 hne +Low level only for port io into hardware-include. +*/ + +#ifndef __LINUX_SL811_HW_H +#define __LINUX_SL811_HW_H + +#define MAX_CONTROLERS 1 /* Max number of sl811 controllers */ + /* Always 1 for this architecture! */ + +#define SIZEOF_IO_REGION 1 /* Size for request/release region */ + +#define OFFSET_DATA_REG data_off /* Offset from ADDR_IO to DATA_IO (future) */ + /* Can change by arg */ + +static int io = 0xf100000e; /* Base addr_io */ +static int data_off = 1; /* Offset from addr_io to addr_io */ +static int irq = 44; /* also change gprd !!! */ +static int gprd = 23; /* also change irq !!! */ + +MODULE_PARM(io,"i"); +MODULE_PARM_DESC(io,"sl811 address io port 0xf100000e"); +MODULE_PARM(data_off,"i"); +MODULE_PARM_DESC(data_off,"sl811 data io port offset from address port (default 1)"); +MODULE_PARM(irq,"i"); +MODULE_PARM_DESC(irq,"sl811 irq 44(default)"); +MODULE_PARM(gprd,"i"); +MODULE_PARM_DESC(gprd,"sl811 GPRD port 23(default)"); + +/* + * Low level: Read from Data port [arm] + */ +static __u8 inline sl811_read_data (struct sl811_hc *hc) +{ + __u8 data; + data = readb(hc->data_io); + rmb(); + return data; +} + +/* + * Low level: Write to index register [arm] + */ +static void inline sl811_write_index (struct sl811_hc *hc, __u8 index) +{ + writeb(index, hc->addr_io); + wmb(); +} + +/* + * Low level: Write to Data port [arm] + */ +static void inline sl811_write_data (struct sl811_hc *hc, __u8 data) +{ + writeb(data, hc->data_io); + wmb(); +} + +/* + * Low level: Write to index register and data port [arm] + */ +static void inline sl811_write_index_data (struct sl811_hc *hc, __u8 index, __u8 data) +{ + writeb(index, hc->addr_io); + writeb(data, hc->data_io); + wmb(); +} + + +/* + * This function is board specific. It sets up the interrupt to + * be an edge trigger and trigger on the rising edge + */ +static void inline sl811_init_irq(void) +{ + GPDR &= ~(1<addr_io) + release_region(hc->addr_io, SIZEOF_IO_REGION); + hc->addr_io = 0; + + if (hc->data_io) + release_region(hc->data_io, SIZEOF_IO_REGION); + hc->data_io = 0; +} + +/***************************************************************** + * + * Function Name: request_regions [arm] + * + * This function is board specific. It request all io address and + * maps into memory (if can). + * + * Input: struct sl811_hc * + * + * Return value : 0 = OK + * + *****************************************************************/ +static int inline sl811_request_regions (struct sl811_hc *hc, int addr_io, int data_io, const char *name) +{ + if (!request_region(addr_io, SIZEOF_IO_REGION, name)) { + PDEBUG(3, "request address %d failed", addr_io); + return -EBUSY; + } + hc->addr_io = addr_io; + + if (!request_region(data_io, SIZEOF_IO_REGION, MODNAME)) { + PDEBUG(3, "request address %d failed", data_io); + /* release_region(hc->addr_io, SIZEOF_IO_REGION); */ + return -EBUSY; + } + hc->data_io = data_io; + + return 0; +} + +#endif // __LINUX_SL811_HW_H diff -urN linux-2.4.22-bk41/include/asm-i386/hc_sl811-hw.h linux-2.4.22-bk42/include/asm-i386/hc_sl811-hw.h --- linux-2.4.22-bk41/include/asm-i386/hc_sl811-hw.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/include/asm-i386/hc_sl811-hw.h 2003-10-26 02:49:55.000000000 -0800 @@ -0,0 +1,139 @@ +/* +File: include/asm-i386/hc_sl811-hw.h + +18.11.2002 hne@ist1.de +Use Kernel 2.4.19 and Prepatch 2.4.20 +Splitt hardware depenc into file hc_sl811-x86.c and hc_sl811-arm.c. + +20.11.2002 HNE +READ/WRITE_(INDEX_)DATA using for fast hardware access. + +02.09.2003 HNE +IO Region size only 2 (old 16) + +18.09.2003 HNE +Handle multi instances. For two controller on one board. +Portcheck in low level source (DEBUG) only. + +03.10.2003 HNE +Low level only for port io into hardware-include. + +*/ + +#ifdef MODULE + +#define MAX_CONTROLERS 2 /* Max number of SL811 controllers per module */ +static int io_base = 0x220; +static int irq = 12; + +MODULE_PARM(io_base,"i"); +MODULE_PARM_DESC(io_base,"sl811 base address 0x220"); +MODULE_PARM(irq,"i"); +MODULE_PARM_DESC(irq,"IRQ 12 (default)"); + +#endif // MODULE + +/* Define general IO Macros for our platform (hne) */ +#define SIZEOF_IO_REGION 2 /* Size for request/release region */ + +// #define sl811_write_index(hp,i) outb ((i), hp->hcport) +// #define sl811_write_data(hp,d) outb ((d), hp->hcport+1) +// #define sl811_write_index_data(hp,i,d) outw ((i)|(((__u16)(d)) << 8), hp->hcport) +// #define sl811_read_data(hp) ((__u8) inb (hp->hcport+1)) + +/* + * Low level: Read from Data port [x86] + */ +static __u8 inline sl811_read_data (hcipriv_t *hp) +{ + return ((__u8) inb (hp->hcport+1)); +} + +/* + * Low level: Write to index register [x86] + */ +static void inline sl811_write_index (hcipriv_t *hp, __u8 index) +{ + outb (index, hp->hcport); +} + +/* + * Low level: Write to Data port [x86] + */ +static void inline sl811_write_data (hcipriv_t *hp, __u8 data) +{ + outb (data, hp->hcport+1); +} + +/* + * Low level: Write to index register and data port [x86] + */ +static void inline sl811_write_index_data (hcipriv_t *hp, __u8 index, __u8 data) +{ + outw (index|(((__u16)data) << 8), hp->hcport); +} + +/***************************************************************** + * + * Function Name: init_irq [x86] + * + * This function is board specific. It sets up the interrupt to + * be an edge trigger and trigger on the rising edge + * + * Input: none + * + * Return value : none + * + *****************************************************************/ +static void inline init_irq (void) +{ + /* nothing */ +} + +/***************************************************************** + * + * Function Name: release_regions [x86] + * + * This function is board specific. It frees all io address. + * + * Input: hcipriv_t * + * + * Return value : none + * + *****************************************************************/ +static void inline sl811_release_regions (hcipriv_t *hp) +{ + DBGFUNC ("Enter release_regions\n"); + if (hp->hcport > 0) { + release_region (hp->hcport, SIZEOF_IO_REGION); + hp->hcport = 0; + } + + /* hcport2 unused for x86 */ +} + +/***************************************************************** + * + * Function Name: request_regions [x86] + * + * This function is board specific. It request all io address and + * maps into memory (if can). + * + * Input: hcipriv_t * + * + * Return value : 0 = OK + * + *****************************************************************/ +static int inline sl811_request_regions (hcipriv_t *hp, int base1, int base2) +{ + DBGFUNC ("Enter request_regions\n"); + if (!request_region (base1, SIZEOF_IO_REGION, "SL811")) { + DBGERR ("request address 0x%X %d failed\n", base1, SIZEOF_IO_REGION); + return -EBUSY; + } + hp->hcport = base1; + + /* hcport2 unused for x86 */ + return 0; +} + diff -urN linux-2.4.22-bk41/include/asm-i386/sl811-hw.h linux-2.4.22-bk42/include/asm-i386/sl811-hw.h --- linux-2.4.22-bk41/include/asm-i386/sl811-hw.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk42/include/asm-i386/sl811-hw.h 2003-10-26 02:49:55.000000000 -0800 @@ -0,0 +1,133 @@ +/* +File: include/asm-i386/sl811-hw.h + +19.09.2003 hne@ist1.de +Use Kernel 2.4.20 and this source from 2.4.22 +Splitt hardware depens into file sl811-x86.h and sl811-arm.h. +Functions are inline. + +Handle multi instances. For two controller on one board. +"hc->data_io" not used for x86 arch. + +22.09.2003 hne +Alternate IO-Base for second Controller (CF/USB1). + +23.09.2003 hne +Move Hardware depend header sl811-x86.h into include/asm-i386/sl811-hw.h. + +03.10.2003 hne +Low level only for port io into hardware-include. +*/ + +#ifndef __LINUX_SL811_HW_H +#define __LINUX_SL811_HW_H + +#define MAX_CONTROLERS 2 /* Max number of SL811 controllers per module */ + +static int io[MAX_CONTROLERS] = { 0x220, 0x222 }; /* IO ports for controllers */ +static int irq[MAX_CONTROLERS] = { 12, 12 }; /* Interrupt list */ + +MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CONTROLERS) "i"); +MODULE_PARM_DESC(io, "I/O base address(es), 0x220,0x222 default"); +MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CONTROLERS) "i"); +MODULE_PARM_DESC(irq, "IRQ number(s), 12,12 default"); + +#define OFFSET_DATA_REG 1 /* Offset from ADDR_IO to DATA_IO */ + /* Do not change this! */ +#define SIZEOF_IO_REGION 2 /* Size for request/release region */ + +/* + * Low level: Read from Data port [x86] + */ +static __u8 inline sl811_read_data (struct sl811_hc *hc) +{ + return ((__u8) inb (hc->addr_io+OFFSET_DATA_REG)); +} + +/* + * Low level: Write to index register [x86] + */ +static void inline sl811_write_index (struct sl811_hc *hc, __u8 index) +{ + outb (index, hc->addr_io); +} + +/* + * Low level: Write to Data port [x86] + */ +static void inline sl811_write_data (struct sl811_hc *hc, __u8 data) +{ + outb (data, hc->addr_io+OFFSET_DATA_REG); +} + +/* + * Low level: Write to index register and data port [x86] + */ +static void inline sl811_write_index_data (struct sl811_hc *hc, __u8 index, __u8 data) +{ + outw (index|(((__u16)data) << 8), hc->addr_io); +} + +/* + * This function is board specific. It sets up the interrupt to + * be an edge trigger and trigger on the rising edge + */ +static void inline sl811_init_irq(void) +{ + /* nothing for x86 */ +} + + +/***************************************************************** + * + * Function Name: release_regions [x86] + * + * This function is board specific. It release all io address + * from memory (if can). + * + * Input: struct sl811_hc * * + * + * Return value : 0 = OK + * + *****************************************************************/ +static void inline sl811_release_regions(struct sl811_hc *hc) +{ + if (hc->addr_io) + release_region(hc->addr_io, SIZEOF_IO_REGION); + hc->addr_io = 0; + + /* data_io unused for x86 */ + /* ... + if (hc->data_io) + release_region(hc->data_io, SIZEOF_IO_REGION); + hc->data_io = 0; + ... */ +} + +/***************************************************************** + * + * Function Name: request_regions [x86] + * + * This function is board specific. It request all io address and + * maps into memory (if can). + * + * Input: struct sl811_hc * + * + * Return value : 0 = OK + * + *****************************************************************/ +static int inline sl811_request_regions (struct sl811_hc *hc, int addr_io, int data_io, const char *name) +{ + if (!request_region(addr_io, SIZEOF_IO_REGION, name)) { + PDEBUG(3, "request address %d failed", addr_io); + return -EBUSY; + } + hc->addr_io = addr_io; + + /* data_io unused for x86 */ + /* hc->data_io = data_io; */ + + return 0; +} + +#endif // __LINUX_SL811_HW_H diff -urN linux-2.4.22-bk41/include/linux/i2c-id.h linux-2.4.22-bk42/include/linux/i2c-id.h --- linux-2.4.22-bk41/include/linux/i2c-id.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.22-bk42/include/linux/i2c-id.h 2003-10-26 02:49:55.000000000 -0800 @@ -199,6 +199,7 @@ #define I2C_HW_SMBUS_AMD756 0x05 #define I2C_HW_SMBUS_SIS5595 0x06 #define I2C_HW_SMBUS_ALI1535 0x07 +#define I2C_HW_SMBUS_W9968CF 0x08 /* --- ISA pseudo-adapter */ #define I2C_HW_ISA 0x00 diff -urN linux-2.4.22-bk41/include/linux/usb_ch9.h linux-2.4.22-bk42/include/linux/usb_ch9.h --- linux-2.4.22-bk41/include/linux/usb_ch9.h 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/include/linux/usb_ch9.h 2003-10-26 02:49:55.000000000 -0800 @@ -116,6 +116,17 @@ #define USB_DT_DEVICE_QUALIFIER 0x06 #define USB_DT_OTHER_SPEED_CONFIG 0x07 #define USB_DT_INTERFACE_POWER 0x08 +/* these are from a minor usb 2.0 revision (ECN) */ +#define USB_DT_OTG 0x09 +#define USB_DT_DEBUG 0x0a +#define USB_DT_INTERFACE_ASSOCIATION 0x0b + +/* conventional codes for class-specific descriptors */ +#define USB_DT_CS_DEVICE 0x21 +#define USB_DT_CS_CONFIG 0x22 +#define USB_DT_CS_STRING 0x23 +#define USB_DT_CS_INTERFACE 0x24 +#define USB_DT_CS_ENDPOINT 0x25 /* All standard descriptors have these 2 fields at the beginning */ struct usb_descriptor_header { @@ -165,6 +176,7 @@ #define USB_CLASS_CDC_DATA 0x0a #define USB_CLASS_CSCID 0x0b /* chip+ smart card */ #define USB_CLASS_CONTENT_SEC 0x0d /* content security */ +#define USB_CLASS_VIDEO 0x0e #define USB_CLASS_APP_SPEC 0xfe #define USB_CLASS_VENDOR_SPEC 0xff @@ -283,6 +295,36 @@ /*-------------------------------------------------------------------------*/ +/* USB_DT_OTG (from OTG 1.0a supplement) */ +struct usb_otg_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bmAttributes; /* support for HNP, SRP, etc */ +} __attribute__ ((packed)); + +/* from usb_otg_descriptor.bmAttributes */ +#define USB_OTG_SRP (1 << 0) +#define USB_OTG_HNP (1 << 1) /* swap host/device roles */ + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_INTERFACE_ASSOCIATION: groups interfaces */ +struct usb_interface_assoc_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bFirstInterface; + __u8 bInterfaceCount; + __u8 bFunctionClass; + __u8 bFunctionSubClass; + __u8 bFunctionProtocol; + __u8 iFunction; +} __attribute__ ((packed)); + + +/*-------------------------------------------------------------------------*/ + /* USB 2.0 defines three speeds, here's how Linux identifies them */ enum usb_device_speed { diff -urN linux-2.4.22-bk41/include/linux/videodev.h linux-2.4.22-bk42/include/linux/videodev.h --- linux-2.4.22-bk41/include/linux/videodev.h 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/include/linux/videodev.h 2003-10-26 02:49:55.000000000 -0800 @@ -420,6 +420,7 @@ #define VID_HARDWARE_CPIA2 33 #define VID_HARDWARE_VICAM 34 #define VID_HARDWARE_SF16FMR2 35 +#define VID_HARDWARE_W9968CF 36 #endif /* __LINUX_VIDEODEV_H */ /* diff -urN linux-2.4.22-bk41/kernel/sysctl.c linux-2.4.22-bk42/kernel/sysctl.c --- linux-2.4.22-bk41/kernel/sysctl.c 2003-10-26 02:49:50.000000000 -0800 +++ linux-2.4.22-bk42/kernel/sysctl.c 2003-10-26 02:49:55.000000000 -0800 @@ -180,7 +180,7 @@ 0644, NULL, &proc_dointvec}, {KERN_CORE_USES_PID, "core_uses_pid", &core_uses_pid, sizeof(int), 0644, NULL, &proc_dointvec}, - {KERN_CORE_USES_PID, "core_setuid_ok", &core_setuid_ok, sizeof(int), + {KERN_CORE_SETUID, "core_setuid_ok", &core_setuid_ok, sizeof(int), 0644, NULL, &proc_dointvec}, {KERN_CORE_PATTERN, "core_pattern", core_pattern, 64, 0644, NULL, &proc_dostring, &sysctl_string},