diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/Configure.help linux-2.2.17/Documentation/Configure.help --- linux-2.2.17-orig/Documentation/Configure.help Mon Sep 4 10:39:15 2000 +++ linux-2.2.17/Documentation/Configure.help Sun Sep 24 04:51:48 2000 @@ -12401,6 +12401,548 @@ If you do not have a CompactPCI model CP1400 or CP1500, or another UltraSPARC-IIi-cEngine boardset with digital display, you should say N to this option. + +CPiA USB Lowlevel Support +CONFIG_VIDEO_CPIA_USB + This is the lowlevel USB support for cameras based on Vision's CPiA + (Colour Processor Interface ASIC), such as the Creative Webcam II. + If you have the USB version of one of these cameras, say Y here, + otherwise say N. This will not work with the Creative Webcam III. + It is also available as a module (cpia_usb.o). + +Support for USB +CONFIG_USB + Universal Serial Bus (USB) is a specification for a serial bus + subsystem which offers higher speeds and more features than the + traditional PC serial port. The bus supplies power to peripherals + and allows for hot swapping. Up to 127 USB peripherals can be + connected to a single USB port in a tree structure. The USB port is + the root of the tree, the peripherals are the leaves and the inner + nodes are special USB devices called hubs. Many newer PC's have USB + ports and newer peripherals such as scanners, keyboards, mice, + modems, and printers support the USB protocol and can be connected + to the PC via those ports. + + Say Y here if your computer has a USB port and you want to use USB + devices. You then need to say Y to at least one of "UHCI support" or + "OHCI support" below (the type of interface that the USB hardware in + your computer provides to the operating system) and then choose from + among the drivers for USB peripherals. You may want to check out the + information provided in Documentation/usb/ and especially the links + given in Documentation/usb/usb-help.txt. + + 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 usbcore.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB verbose debug messages +CONFIG_USB_DEBUG + Say Y here if you want the USB core & hub drivers to produce a bunch + of debug messages to the system log. Select this if you are having a + problem with USB support and want to see more of what is going on. + +UHCI (intel PIIX4, VIA, ...) support? +CONFIG_USB_UHCI + The Universal Host Controller Interface is a standard by Intel for + accessing the USB hardware in the PC (which is also called the USB + host controller). If your USB host controller conforms to this + standard, you may want to say Y, but see below. All recent boards + with Intel PCI chipsets (like intel 430TX, 440FX, 440LX, 440BX, + i810, i820) conform to this standard. Also all VIA PCI chipsets + (like VIA VP2, VP3, MVP3, Apollo Pro, Apollo Pro II or Apollo Pro + 133). + + Currently there exist two drivers for UHCI host controllers: this + one and the so-called JE driver, which you can get from + "UHCI alternate (JE) support", below. You need only one. + + 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 usb-uhci.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +UHCI (intel PIIX4, VIA, ...) alternate (JE) support? +CONFIG_USB_UHCI_ALT + The Universal Host Controller Interface is a standard by Intel for + accessing the USB hardware in the PC (which is also called the USB + host controller). If your USB host controller conforms to this + standard, you may want to say Y, but see below. All recent boards + with Intel PCI chipsets (like intel 430TX, 440FX, 440LX, 440BX, + i810, i820) conform to this standard. Also all VIA PCI chipsets + (like VIA VP2, VP3, MVP3, Apollo Pro, Apollo Pro II or Apollo Pro + 133). If unsure, say Y. + + Currently there exist two drivers for UHCI host controllers: this + so-called JE driver, and the one you get from "UHCI support", above. + You need only one. + + 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 uhci.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +UHCI unlink optimizations (EXPERIMENTAL) +CONFIG_USB_UHCI_ALT_UNLINK_OPTIMIZE + This option currently does nothing. You may say Y or N. + +OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support +CONFIG_USB_OHCI + The Open Host Controller Interface is a standard by + Compaq/Microsoft/National for accessing the USB PC hardware (also + called USB host controller). If your USB host controller conforms to + this standard, say Y. The USB host controllers on most non-Intel + architectures and on several x86 compatibles with non-Intel chipsets + -- like SiS (aktual 610, 610 and so on) or ALi (ALi IV, ALi V, + Aladdin Pro..) -- conform to this standard. + + You may want to read the file Documentation/usb/ohci.txt. + + 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 usb-ohci.o. If you want to compile it + as a module, say M here and read Documentation/modules.txt. + +USB Human Interface Device (HID) support +CONFIG_USB_HID + Say Y here if you want to connect keyboards, mice, joysticks, + graphic tablets, or any other HID based devices to your + computer via USB. More information is available: + Documentation/usb/input.txt. + + If unsure, say Y. + + 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 hid.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB HIDBP Keyboard support +CONFIG_USB_KBD + Say Y here if you don't want to use the generic HID driver for your + USB keyboard and prefer to use the keyboard in its limited Boot + Protocol mode. This driver is much smaller than the HID one. + + 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 usbkbd.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + + If unsure, say N. + +USB HIDBP Mouse support +CONFIG_USB_MOUSE + Say Y here if you don't want to use the generic HID driver for your + USB mouse and prefer to use the mouse in its limited Boot Protocol + mode. This driver is much smaller than the HID one. + + 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 usbmouse.o. If you want to compile it as + a module, say M here and read Documentation/modules.txt. + + If unsure, say N. + +Wacom Intuos/Graphire tablet support +CONFIG_USB_WACOM + Say Y here if you want to use the USB version of the Wacom Intuos + or Graphire tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + This driver 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 wacom.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +Logitech WingMan Force joystick support +CONFIG_USB_WMFORCE + Say Y here if you want to use the Logitech WingMan Force with Linux + on the USB port. No force-feedback support yet, but other than that + it should work like a normal joystick. + + This driver 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 wmforce.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +Keyboard support +CONFIG_INPUT_KEYBDEV + Say Y here if you want your USB HID keyboard to be able to serve as + a system keyboard. + + This driver 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 keybdev.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +Mouse support +CONFIG_INPUT_MOUSEDEV + Say Y here if you want your USB HID mouse to be accessible as + char devices 13:32+ - /dev/input/mouseX and 13:63 - /dev/input/mice + as an emulated PS/2 mouse. That way, all user space programs will + be able to use your mouse. + + If unsure, say Y. + + This driver 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 mousedev.o. If you want to compile it as + a module, say M here and read Documentation/modules.txt. + +Horizontal screen resolution +CONFIG_INPUT_MOUSEDEV_SCREEN_X + If you're using a digitizer, or a graphic tablet, and want to use + it as a mouse then the mousedev driver needs to know the X window + screen resolution you are using to correctly scale the data. If + you're not using a digitizer, this value is ignored. + +Vertical screen resolution +CONFIG_INPUT_MOUSEDEV_SCREEN_Y + If you're using a digitizer, or a graphic tablet, and want to use + it as a mouse then the mousedev driver needs to know the X window + screen resolution you are using to correctly scale the data. If + you're not using a digitizer, this value is ignored. + +Joystick support +CONFIG_INPUT_JOYDEV + Say Y here if you want your USB HID joystick or gamepad to be + accessible as char device 13:0+ - /dev/input/jsX device. + + This driver 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 joydev.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +Event interface support +CONFIG_INPUT_EVDEV + Say Y here if you want your USB HID device events be accessible + under char device 13:64+ - /dev/inputX in a generic way. + This is the future ... + +USB Scanner support +CONFIG_USB_SCANNER + Say Y here if you want to connect a USB scanner to your computer's + USB port. Please read Documentation/usb/scanner.txt and + Documentation/usb/scanner-hp-sane.txt for more information. + + 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 scanner.o. If you want to compile it as + a module, say M here and read Documentation/modules.txt. + +USB Audio support +CONFIG_USB_AUDIO + Say Y here if you want to connect UAB audio equipment such as + speakers to your computer's USB port. + + 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 audio.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB Modem (CDC ACM) support +CONFIG_USB_ACM + This driver supports USB modems and ISDN adapters which support the + Communication Device Class Abstract Control Model interface. + Please read Documentation/usb/acm.txt for details. + + 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 acm.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB Serial converter support +CONFIG_USB_SERIAL + Say Y here if you have a USB device that provides normal serial + ports, and you want to connect it to your USB bus. Supported devices + are the Tech WhiteHEAT multi-port USB to serial converter, and the + FTDI or Keyspan single port USB to serial converter Handspring + Visor. In addition to saying Y here, you need to say Y to the driver + for your specific hardware below. Some other devices may also be + used if you say Y to "USB Generic Serial Driver", below. + + Please read Documentation/usb/usb-serial.txt for more information. + + 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 usb-serial.o. If you want to compile it + as a module, say M here and read Documentation/modules.txt. + +USB Generic Serial Driver +CONFIG_USB_SERIAL_GENERIC + Say Y here if you want to use the generic USB serial driver. Please + read Documentation/usb/usb-serial.txt for more information on using + this driver. It is recommended that the "USB Serial converter + support" be compiled as a module for this driver to be used + properly. + +USB ConnectTech WhiteHEAT Serial Driver +CONFIG_USB_SERIAL_WHITEHEAT + Say Y here if you want to use a ConnectTech WhiteHEAT 4 port + USB to serial converter device. + +USB Handspring Visor Driver +CONFIG_USB_SERIAL_VISOR + Say Y here if you want to connect to your HandSpring Visor through + its USB docking station. See http://usbvisor.sourceforge.net for + more information on using this driver. + +USB FTDI Single Port Serial Driver +CONFIG_USB_SERIAL_FTDI_SIO + Say Y here if you want to use a FTDI SIO single port USB to serial + converter device. The implementation I have is called the USC-1000. + + See http://reality.sgi.com/bryder_wellington/ftdi_sio for more + information on this driver and the device. + +USB Keyspan PDA Single Port Serial Driver +CONFIG_USB_SERIAL_KEYSPAN_PDA + Say Y here if you want to use a Keyspan PDA single port USB to + serial converter device. + +USB ZyXEL omni.net LCD Plus Driver +CONFIG_USB_SERIAL_OMNINET + Say Y here if you want to use a ZyXEL omni.net LCD ISDN TA. + +USB Digi International AccelePort USB Serial Driver +CONFIG_USB_SERIAL_DIGI_ACCELEPORT + Say Y here if you want to use a Digi AccelePort USB 4 device, + a 4 port USB serial converter. The Digi Acceleport USB 2 and + 8 are not yet supported by this driver. + +USB Printer support +CONFIG_USB_PRINTER + Say Y here if you want to connect a USB printer to your computer's + USB port. + + 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 printer.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB Serial Converter verbose debug +CONFIG_USB_SERIAL_DEBUG + Say Y here if you want verbose debug messages from the USB Serial + Converter. + +USB IBM (Xirlink) C-It Camera support +CONFIG_USB_IBMCAM + Say Y here if you want to connect a IBM "C-It" camera, also known as + "Xirlink PC Camera" to your computer's USB port. For more + information, read Documentation/usb/ibmcam.txt. + + This driver uses the Video For Linux API. You must enable + (Y or M in config) Video For Linux (under Character Devices) + to use this driver. Information on this API and pointers to + "v4l" programs may be found on the WWW at + http://roadrunner.swansea.uk.linux.org/v4l.shtml . + + 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 ibmcam.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. This camera + has several configuration options which can be specified when you + load the module. Read Documentation/usb/ibmcam.txt to learn more. + +USB OV511 Camera support +CONFIG_USB_OV511 + Say Y here if you want to connect this type of camera to your + computer's USB port. See Documentation/usb/ov511.txt for more + information and for a list of supported cameras. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" (under Character Devices) to use this driver. + Information on this API and pointers to "v4l" programs may be found + on the WWW at http://roadrunner.swansea.uk.linux.org/v4l.shtml . + + 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 ov511.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB ADMtek Pegasus-based ethernet device support +CONFIG_USB_PEGASUS + Say Y if you want to use your USB ethernet device. Supported + cards until now are: + Accton 10/100 + Billington USB-100 + Corega FEter USB-TX + MELCO/BUFFALO LUA-TX + D-Link DSB-650TX, DSB-650TX-PNA + Linksys USB100TX + SNC 202 + If you have devices with vendor IDs other than noted above + you should add them in the driver code and send a message + to me (petkan@spct.net) for update. + + 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 pegasus.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB Kodak DC-2xx Camera support +CONFIG_USB_DC2XX + Say Y here if you want to connect this type of still camera to + your computer's USB port. See Documentation/usb/dc2xx.txt for more + information; some non-Kodak cameras may also work with this + driver, given application support (such as www.gPhoto.org). + + 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 dc2xx.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB Mustek MDC800 Digital Camera Support +CONFIG_USB_MDC800 + Say Y here if you want to connect this type of still camera to + your computer's USB port. This driver can be used with gphoto 0.4.3 + and higher (look at http://www.gphoto.org ). + To use it create a device node with "mknod /dev/mustek c 180 32" and + configure it in your software. + + 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 mdc800.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB Mass Storage support +CONFIG_USB_STORAGE + Say Y here if you want to connect USB mass storage devices to your + computer's USB port. + + 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 usb-storage.o. If you want to compile it + as a module, say M here and read Documentation/modules.txt. + +USB Mass Storage verbose debug +CONFIG_USB_STORAGE_DEBUG + Say Y here in order to have the USB Mass Storage code generate + verbose debugging messages. + +USS720 parport driver +CONFIG_USB_USS720 + This driver is for USB parallel port adapters that use the Lucent + Technologies USS-720 chip. These cables are plugged into your USB + port and provide USB compatibility to peripherals designed with + parallel port interfaces. + + The chip has two modes: automatic mode and manual mode. In automatic + mode, it looks to the computer like a standard USB printer. Only + printers may be connected to the USS-720 in this mode. The generic + USB printer driver ("USB Printer support", above) may be used in + that mode, and you can say N here if you want to use the chip only + in this mode. + + Manual mode is not limited to printers, any parallel port + device should work. This driver utilizes manual mode. + Note however that some operations are three orders of magnitude + slower than on a PCI/ISA Parallel Port, so timing critical + applications might not work. + + Say Y here if you own an USS-720 USB->Parport cable and intend to + connect anything other than a printer to it. + + 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 uss720.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB device file system +CONFIG_USB_DEVICEFS + If you say Y here (and to "/proc file system support" below), you + will get a file /proc/usb/devices which lists the devices currently + connected to your USB busses, a file /proc/usb/drivers which lists + the USB kernel client drivers currently loaded, and for every + connected device a file named "/proc/usb/xxx/yyy", where xxx is the + bus number and yyy the device number; the latter files can be used + by user space programs to talk directly to the device. These files + are "virtual", meaning they are generated on the fly and not stored + on the hard drive. + + For the format of the /proc/usb/ files, please read + Documentation/usb/proc_usb_info.txt. + + Please note that this code is completely unrelated to devfs, the + "/dev file system support". + + Most users want to say Y here. + +USB Bandwidth allocation +CONFIG_USB_BANDWIDTH + If you say Y here, the USB subsystem enforces USB bandwidth + allocation and will prevent some device opens from succeeding + if they would cause USB bandwidth usage to go above 90% of + the bus bandwidth. + + If you say N here, these conditions will cause warning messages + about USB bandwidth usage to be logged and some devices or + drivers may not work correctly. + +DABUSB driver +CONFIG_USB_DABUSB + A Digital Audio Broadcasting (DAB) Receiver for USB and Linux + brought to you by the DAB-Team (http://dab.in.tum.de). This driver + can be taken as an example for URB-based bulk, control, and + isochronous transactions. URB's are explained in + Documentation/usb/URB.txt. + + 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 dabusb.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +PLUSB driver +CONFIG_USB_PLUSB + A driver for the Prolific PL-2302 USB-to-USB network device. This + 'USB cable' connects two hosts via a point-to-point network with + bandwidth of 5 Mbit/s. Configure this driver after connecting the + USB cable via ifconfig plusb0 10.0.0.1 pointopoint 10.0.0.2 (and + vice versa on the other host). + + 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 plusb.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB Diamond Rio500 support +CONFIG_USB_RIO500 + Say Y here if you want to connect a USB Rio500 mp3 player to your + computer's USB port. Please read Documentation/usb/rio.txt + for more information. + + 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 rio500.o. If you want to compile it as + a module, say M here and read Documentation/modules.txt. + +D-Link DSB-R100 FM radio support +CONFIG_USB_DSBR + Say Y here if you want to connect this type of radio to your + computer's USB port. Note that the audio is not digital, and + you must connect the line out connector to a sound card or a + set of speakers. + + This driver uses the Video For Linux API. You must enable + (Y or M in config) Video For Linux (under Character Devices) + to use this driver. Information on this API and pointers to + "v4l" programs may be found on the WWW at + http://roadrunner.swansea.uk.linux.org/v4l.shtml . + + 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 dsbr100.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +Microtek USB scanner support +CONFIG_USB_MICROTEK + Say Y here if you want support for the Microtek X6USB and possibly + some other scanners. The scanner will appear as a scsi device to the + rest of the system. A patched version of SANE is necessary to use the + scanner. It's available at + http://fachschaft.cup.uni-muenchen.de/~neukum/scanner.html + This driver can be compiled as a module. # # A couple of things I keep forgetting: diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/CREDITS linux-2.2.17/Documentation/usb/CREDITS --- linux-2.2.17-orig/Documentation/usb/CREDITS Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/CREDITS Sun Sep 24 04:26:21 2000 @@ -0,0 +1,169 @@ +Credits for the Simple Linux USB Driver: + +The following people have contributed to this code (in alphabetical +order by last name). I'm sure this list should be longer, its +difficult to maintain, add yourself with a patch if desired. + + Georg Acher + Alan Cox + Randy Dunlap + Johannes Erdfelt + Deti Fliegl + ham + Bradley M Keryan + Greg Kroah-Hartman + Pavel Machek + Paul Mackerras + David E. Nelson + Vojtech Pavlik + Bill Ryder + Thomas Sailer + Gregory P. Smith + Linus Torvalds + Roman Weissgaerber + + +Special thanks to: + + Inaky Perez Gonzalez for starting the + Linux USB driver effort and writing much of the larger uusbd driver. + Much has been learned from that effort. + + The NetBSD & FreeBSD USB developers. For being on the Linux USB list + and offering suggestions and sharing implementation experiences. + +Additional thanks to the following companies and people for donations +of hardware, support, time and development (this is from the original +THANKS file in Inaky's driver): + + The following corporations have helped us in the development + of Linux USB / UUSBD: + + - 3Com GmbH for donating a ISDN Pro TA and supporting me + in technical questions and with test equipment. I'd never + expect such a great help. + + - USAR Systems provided us with one of their excellent USB + Evaluation Kits. It allows us to test the Linux-USB driver + for compilance with the latest USB specification. USAR + Systems recognized the importance of an up-to-date open + Operating System and supports this project with + Hardware. Thanks!. + + - Thanks to Intel Corporation for their precious help. + + - We teamed up with Cherry to make Linux the first OS with + built-in USB support. Cherry is one of the biggest keyboard + makers in the world. + + - CMD Technology, Inc. sponsored us kindly donating a CSA-6700 + PCI-to-USB Controller Board to test the OHCI implementation. + + - Due to their support to us, Keytronic can be sure that they + will sell keyboards to some of the 3 million (at least) + Linux users. + + - Many thanks to ing büro h doran [http://www.ibhdoran.com]! + It was almost imposible to get a PC backplate USB connector + for the motherboard here at Europe (mine, home-made, was + quite lowsy :). Now I know where to adquire nice USB stuff! + + - Genius Germany donated a USB mouse to test the mouse boot + protocol. They've also donated a F-23 digital joystick and a + NetMouse Pro. Thanks! + + - AVM GmbH Berlin is supporting the development of the Linux + USB driver for the AVM ISDN Controller B1 USB. AVM is a + leading manufacturer for active and passive ISDN Controllers + and CAPI 2.0-based software. The active design of the AVM B1 + is open for all OS platforms, including Linux. + + - Thanks to Y-E Data, Inc. for donating their FlashBuster-U + USB Floppy Disk Drive, so we could test the bulk transfer + code. + + - Many thanks to Logitech for contributing a three axis USB + mouse. + + Logitech designs, manufactures and markets + Human Interface Devices, having a long history and + experience in making devices such as keyboards, mice, + trackballs, cameras, loudspeakers and control devices for + gaming and professional use. + + Being a recognized vendor and seller for all these devices, + they have donated USB mice, a joystick and a scanner, as a + way to acknowledge the importance of Linux and to allow + Logitech customers to enjoy support in their favorite + operating systems and all Linux users to use Logitech and + other USB hardware. + + Logitech is official sponsor of the Linux Conference on + Feb. 11th 1999 in Vienna, where we'll will present the + current state of the Linux USB effort. + + - CATC has provided means to uncover dark corners of the UHCI + inner workings with a USB Inspector. + + - Thanks to Entrega for providing PCI to USB cards, hubs and + converter products for development. + + - Thanks to ConnectTech for providing a WhiteHEAT usb to + serial converter, and the documentation for the device to + allow a driver to be written. + + And thanks go to (hey! in no particular order :) + + - Oren Tirosh , for standing so patiently + all my doubts'bout USB and giving lots of cool ideas. + + - Jochen Karrer , for + pointing out mortal bugs and giving advice. + + - Edmund Humemberger , for it's great work on + public relationships and general management stuff for the + Linux-USB effort. + + - Alberto Menegazzi is starting the + documentation for the UUSBD. Go for it! + + - Ric Klaren for doing nice + introductory documents (compiting with Alberto's :). + + - Christian Groessler , for it's help on those + itchy bits ... :) + + - Paul MacKerras for polishing OHCI and pushing me harder for + the iMac support, giving improvements and enhancements. + + - Fernando Herrera has taken + charge of composing, maintaining and feeding the + long-awaited, unique and marvelous UUSBD FAQ! Tadaaaa!!! + + - Rasca Gmelch has revived the raw driver and + pointed bugs, as well as started the uusbd-utils package. + + - Peter Dettori is unconvering bugs like + crazy, as well as making cool suggestions, great :) + + - All the Free Software and Linux community, the FSF & the GNU + project, the MIT X consortium, the TeX people ... everyone! + You know who you are! + + - Big thanks to Richard Stallman for creating Emacs! + + - The people at the linux-usb mailing list, for reading so + many messages :) Ok, no more kidding; for all your advices! + + - All the people at the USB Implementors Forum for their + help and assistance. + + - Nathan Myers , for his advice! (hope you + liked Cibeles' party). + + - Linus Torvalds, for starting, developing and managing Linux. + + - Mike Smith, Craig Keithley, Thierry Giron and Janet Schank + for convincing me USB Standard hubs are not that standard + and that's good to allow for vendor specific quirks on the + standard hub driver. diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/URB.txt linux-2.2.17/Documentation/usb/URB.txt --- linux-2.2.17-orig/Documentation/usb/URB.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/URB.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,196 @@ +1. Specification of the API + +1.1. Basic concept or 'What is an URB?' + +The basic idea of the new driver is message passing, the message itself is +called USB Request Block, or URB for short. + +- An URB consists of all relevant information to execute any USB transaction +and deliver the data and status back. + +- Execution of an URB is an inherently asynchronous operation, i.e. the +submit_urb(urb) call returns immediately after it has successfully queued +the requested action. + +- Ongoing transfers for one URB (e.g. ISO) can simply be canceled with +unlink_urb(urb) at any time. + +- Each URB has a completion handler, which is called after the action +has been successfully completed or canceled (INT transfers behave a bit +different, see below). The URB also contains a context-pointer for free +usage and information passing to the completion handler. + +- URBs can be linked. After completing one URB, the next one can be +automatically submitted. This is especially useful for ISO transfers: +You only have read/write the data from/to the buffers in the completion +handler, the continous streaming itself is transparently done by the +URB-machinery. + +1.2. The URB structure + +typedef struct urb +{ +// ignore, for host controller/URB machine internal use + void *hcpriv; // private data for host controller + struct list_head urb_list; // list pointer to all active urbs + +// This is used for urb linking + struct urb* next; // pointer to next URB + struct usb_device *dev; // pointer to associated USB device + +// pipe is assembled by the various well known pipe-macros in usb.h + unsigned int pipe; // pipe information + +// status after each completion + int status; // returned status + unsigned int transfer_flags; // ASAP, SP_OK, EARLY_COMPLETE + +// for data stage (CTRL), BULK, INT and ISO + void *transfer_buffer; // associated data buffer + +// expected length + int transfer_buffer_length; // data buffer length + int actual_length; // actual data buffer length + +// setup stage for CTRL (always 8 bytes!) + unsigned char* setup_packet; // setup packet (control only) + +// with ASAP, start_frame is set to the determined frame + int start_frame; // start frame (iso/irq) + int number_of_packets; // # of packets (iso/int) + int interval; // polling interval (irq only) + int error_count; // number of errors (iso only) + // + void *context; // context for completion routine + usb_complete_t complete; // pointer to completion routine + // +// specification of the requested data offsets and length for ISO + iso_packet_descriptor_t iso_frame_desc[0]; +} urb_t, *purb_t; + +1.3. How to get an URB? + +URBs are allocated with the following call + + purb_t alloc_urb(int isoframes) + +Return value is a pointer to the allocated URB, 0 if allocation failed. +The parameter isoframes specifies the number of isochronous transfer frames +you want to schedule. For CTRL/BULK/INT, use 0. + +To free an URB, use + + void free_urb(purb_t purb) + +This call also may free internal (host controller specific) memory in the +future. + +1.4. What has to be filled in? + +Depending on the type of transaction, there are some macros +(FILL_CONTROL_URB, FILL_BULK_URB, and FILL_INT_URB, defined in uhci.h) +that simplify the URB creation. In general, all macros need the usb +device pointer, the pipe (usual format), the transfer buffer, the +desired transfer length, the completion handler, and its context. +Take a look at the uhci_control_msg-function that convert the old API +into an URB. + +Flags: +For ISO there are two startup behaviors: Specified start_frame or ASAP. +For ASAP set USB_ISO_ASAP in transfer_flags. + +If short packets should NOT be tolerated, set USB_DISABLE_SPD in +transfer_flags. + +Usually, (to reduce restart time) the completion handler is called +AFTER the URB re-submission. You can get the other way by setting +USB_URB_EARLY_COMPLETE in transfer_flags. This is implicite for +INT transfers. + +1.5. How to submit an URB? + +Just call + + int submit_urb(purb_t purb) + +It immediately returns, either with status 0 (request queued) or some +error code, usually caused by the following: + +- Out of memory (-ENOMEM) +- Wrong pipe handle (-ENXIO) +- Unplugged device (-ENODEV) +- Stalled endpoint (-EPIPE) +- Too many queued ISO transfers (-EAGAIN) +- Too many requested ISO frames (-EFBIG) +- Invalid INT interval (-EINVAL) +- More than one packet for INT (-EINVAL) + +After submission, urb->status is USB_ST_URB_PENDING. + +For isochronous endpoints, subsequent submitting of URBs to the same endpoint +with the ASAP flag result in a seamless ISO streaming. Exception: The +execution cannot be scheduled later than 900 frames from the 'now'-time. +The same applies to INT transfers, but here the seamless continuation is +independent of the transfer flags (implicitely ASAP). + +1.6. How to cancel an already running URB? + +Call + int unlink_urb(purb_t purb) + +It removes the urb from the internal list and frees all allocated +HW descriptors. The status is changed to USB_ST_URB_KILLED. After +unlink_urb() returns, you can safely free the URB with free_urb(urb) +and all other possibly associated data (urb->context etc.) + +1.7. What about the completion handler? + +The completion handler is optional, but useful for fast data processing +or wakeup of a sleeping process (as shown in the compatibility wrapper's +completion handler). + +The handler is of the following type: + + typedef void (*usb_complete_t)(struct urb *); + +i.e. it gets just the URB that caused the completion call. +In the completion handler, you should have a look at urb->status to +detect any USB errors. Since the context parameter is included in the URB, +you can pass information to the completion handler. + + +1.8. How to do isochronous (ISO) transfers? + +For ISO transfers you have to append the iso_packet_descriptor_t structure +to the URB for each frame you want to schedule. When using alloc_urb(n) +(recommended), the isoframe-parameter n can be used to allocate the +structures for n frames. + +For each entry you have to specify the data offset for this frame (base is +transfer_buffer), and the length you want to write/expect to read. +After completion, actual_length contains the actual transfered length and +status contains the resulting USB-status for the ISO transfer for this frame. +It is allowed to specify a varying length from frame to frame (e.g. for +audio synchronisation/adaptive transfer rates). You can also use the length +0 to omit one or more frames (striping). + +As can be concluded from above, the UHCI-driver does not care for continous +data in case of short packet ISO reads! There's no fixup_isoc() like in the +old driver. There may be a common routine to do this in the future, but this +has nothing to do with the UHCI-driver! + +For scheduling you can choose your own start frame or ASAP. As written above, +queuing more than one ISO frame with ASAP to the same device&endpoint result +in seamless ISO streaming. For continous streaming you have to use URB +linking. + +1.9. How to start interrupt (INT) transfers? + +INT transfers are currently implemented with 8 different queues for intervals +for 1, 2, 4,... 128ms. Only one TD is allocated for each interrupt. After +calling the completion handler, the TD is recycled. +With the submission of one URB, the interrupt is scheduled until it is +canceled by unlink_urb. + +The submit_urb()-call modifies urb->interval to the rounded value. + diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/acm.txt linux-2.2.17/Documentation/usb/acm.txt --- linux-2.2.17-orig/Documentation/usb/acm.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/acm.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,138 @@ + Linux ACM driver v0.16 + (c) 1999 Vojtech Pavlik + Sponsored by SuSE +---------------------------------------------------------------------------- + +0. Disclaimer +~~~~~~~~~~~~~ + 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 + + Should you need to contact me, the author, you can do so either by e-mail +- mail your message to , or by paper mail: Vojtech Pavlik, +Ucitelska 1576, Prague 8, 182 00 Czech Republic + + For your convenience, the GNU General Public License version 2 is included +in the package: See the file COPYING. + +1. Usage +~~~~~~~~ + The drivers/usb/acm.c drivers works with USB modems and USB ISDN terminal +adapters that conform to the Universal Serial Bus Communication Device Class +Abstract Control Model (USB CDC ACM) specification. + + Many modems do, here is a list of those I know of: + + 3Com OfficeConnect 56k + 3Com Voice FaxModem Pro + 3Com Sportster + MultiTech MultiModem 56k + Zoom 2986L FaxModem + Compaq 56k FaxModem + ELSA Microlink 56k + + I know of one ISDN TA that does work with the acm driver: + + 3Com USR ISDN Pro TA + + Unfortunately many modems and most ISDN TAs use proprietary interfaces and +thus won't work with this drivers. Check for ACM compliance before buying. + + The driver (with devfs) creates these devices in /dev/usb/acm: + + crw-r--r-- 1 root root 166, 0 Apr 1 10:49 0 + crw-r--r-- 1 root root 166, 1 Apr 1 10:49 1 + crw-r--r-- 1 root root 166, 2 Apr 1 10:49 2 + + And so on, up to 31, with the limit being possible to change in acm.c to up +to 256, so you can use up to 256 USB modems with one computer (you'll need +three USB cards for that, though). + + If you don't use devfs, then you can create device nodes with the same +minor/major numbers anywhere you want, but either the above location or +/dev/usb/ttyACM0 is preferred. + + To use the modems you need these modules loaded: + + usbcore.o + usb-[uo]hci.o or uhci.o + acm.o + + After that, the modem[s] should be accessible. You should be able to use +minicom, ppp and mgetty with them. + +2. Verifying that it works +~~~~~~~~~~~~~~~~~~~~~~~~~~ + The first step would be to check /proc/bus/usb/devices, it should look +like this: + +T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 +B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0 +D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 +P: Vendor=0000 ProdID=0000 Rev= 0.00 +S: Product=USB UHCI Root Hub +S: SerialNumber=6800 +C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA +I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub +E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms +T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=12 MxCh= 0 +D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2 +P: Vendor=04c1 ProdID=008f Rev= 2.07 +S: Manufacturer=3Com Inc. +S: Product=3Com U.S. Robotics Pro ISDN TA +S: SerialNumber=UFT53A49BVT7 +C: #Ifs= 1 Cfg#= 1 Atr=60 MxPwr= 0mA +I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=acm +E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms +E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms +E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms +C:* #Ifs= 2 Cfg#= 2 Atr=60 MxPwr= 0mA +I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm +E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms +I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm +E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms +E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms + +The presence of these three lines (and the Cls= 'comm' and 'data' classes) +is important, it means it's an ACM device. The Driver=acm means the acm +driver is used for the device. If you see only Cls=ff(vend.) then you're out +of luck, you have a device with vendor specific-interface. + +D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2 +I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm +I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm + +In the system log you should see: + +usb.c: USB new device connect, assigned device number 2 +usb.c: kmalloc IF c7691fa0, numif 1 +usb.c: kmalloc IF c7b5f3e0, numif 2 +usb.c: skipped 4 class/vendor specific interface descriptors +usb.c: new device strings: Mfr=1, Product=2, SerialNumber=3 +usb.c: USB device number 2 default language ID 0x409 +Manufacturer: 3Com Inc. +Product: 3Com U.S. Robotics Pro ISDN TA +SerialNumber: UFT53A49BVT7 +acm.c: probing config 1 +acm.c: probing config 2 +ttyACM0: USB ACM device +acm.c: acm_control_msg: rq: 0x22 val: 0x0 len: 0x0 result: 0 +acm.c: acm_control_msg: rq: 0x20 val: 0x0 len: 0x7 result: 7 +usb.c: acm driver claimed interface c7b5f3e0 +usb.c: acm driver claimed interface c7b5f3f8 +usb.c: acm driver claimed interface c7691fa0 + +If all this seems to be OK, fire up minicom and set it to talk to the ttyACM +device and try typing 'at'. If it responds with 'OK', then everything is +working. diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/dc2xx.txt linux-2.2.17/Documentation/usb/dc2xx.txt --- linux-2.2.17-orig/Documentation/usb/dc2xx.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/dc2xx.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,111 @@ +14 April 2000 +david-b@pacbell.net + +This is an overview of how to use the "dc2xx" USB driver with certain +digital still cameras from Kodak and other vendors. + + +CAMERAS + +This driver will mostly be used with Kodak DC-2xx series digital still +cameras, but it should be trivial to tell it about several non-Kodak +USB-enabled cameras. + +You'll most likely want to hook it up to recent versions of "gPhoto" +(www.gphoto.org), since version 0.4 and later know how to use it to talk +to Kodak DC-240 and DC-280 cameras over USB. + +In addition the DC-220, DC-260, DC-265, and DC-290 are also recognized. +However, like other cameras using the "Digita OS" (from www.flashpoint.com) +there is no gPhoto support for this camera. There is a python script +for accessing these cameras (see archives of the linux-usb mailing list) +and a "Digita Services" library that can also use this driver. + +The HP PhotoSmart C500 should also work, since it's another Digita camera +with USB support. + + +USB HARDWARE + +Recent kernels have had no particular problems using this driver with +either OHCI or UHCI chipsets, and have worked on the PowerMac platform. + +Note that in some cases changes in BIOS settings may be needed before +your USB works. At least one user has reported a need for SMP-related +settings as well, and some old hardware may not handle USB correctly. + + +SETUP + +Configure in the DC2XX USB driver, and have it in your kernel. It works +as a module, or compiled in directly. + +Create at least one device, perhaps like this (both read and write): + + # mknod -m 0660 /dev/usb/dc2xx0 c 180 80 + # mknod -m 0660 /dev/usb/dc2xx1 c 180 81 + ... + +NOTE: you would normally configure PAM so that the user logged in at +the console is granted ownership of these devices. console.perms(5) +explains how to do this. + +The driver supports multiple device nodes. The USB framework supports +a maximum of sixteen device nodes (up to minor device number 96). + +When you plug in one camera, it will use the first device node (dc2xx0 +in the example above). A second camera will use the second device node, +and so on. + + +SANITY TESTING + +First: if you've got /proc support, make sure that the driver has hooked +itself up correctly. + + - You should see an entry in /proc/bus/usb/drivers for "dc2xx", + if you enabled USB /proc support and correctly mounted the + usbdevfs on /proc/bus/usb. + +Second: when you connect your camera to the computer, does it get recognized +by the driver? (Make sure the camera is powered on!) + + - if you've got /proc/bus/usb/devices, you should see an entry + something like this. The "ProdID" may be different if you didn't + plug in a DC-240, as may the strings presented, but "Driver=dc2xx" + had better be there. + + T: Lev=01 Prnt=00 Port=00 Cnt=01 Dev#= 1 Spd=12 MxCh= 0 + D: Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 + P: Vendor=040a ProdID=0120 Rev= 1.08 + S: Manufacturer=Eastman Kodak Company + S: Product=KODAK DC240 Zoom Digital Camera + C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr=100mA + I: If#= 0 Alt= 0 #EPs= 2 Cls=00(>ifc ) Sub=00 Prot=00 Driver=dc2xx + E: Ad=01(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms + E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms + + - see if "dmesg" output tells you that you plugged in your camera. + + Manufacturer: Eastman Kodak Company + Product: KODAK DC240 Zoom Digital Camera + dc2xx.c: USB Camera #0 connected + +Third: (optional) can you use gPhoto to talk to the camera? + + - When you configure your camera, tell it to use "/dev/usb/dc2xx0" + (or whatever name you used). Right now, gPhoto emits a diagnostic + message (non-GUI) saying that it since it didn't act like a TTY, + it's assuming it's got a USB connection. + + - With the camera turned on, get the "camera summary". It'll + talk to the camera -- and tell you you're using USB. + +If you got that far, you should be able to use everything fine. + + +ADDITIONAL INFORMATION + +You may find that you need more driver-specific information, which is +currently accessible through a link from http://www.linux-usb.org/ +along with other Linux USB resources. diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/error-codes.txt linux-2.2.17/Documentation/usb/error-codes.txt --- linux-2.2.17-orig/Documentation/usb/error-codes.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/error-codes.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,108 @@ +$Id: README.error-codes,v 1.1 1999/12/14 14:03:02 fliegl Exp $ + +This is the documentation of (hopefully) all possible error codes (and +their interpretation) that can be returned from the hostcontroller driver +and from usbcore. + +NOTE: +The USB_ST_* codes are deferred and are only listed for compatibility, new +software should use only -E* instead! + + + +************************************************************************** +* Error codes returned by usb_submit_urb * +************************************************************************** + +Non-USB-specific: + +USB_ST_NOERROR +0 URB submission went fine + +-ENOMEM no memory for allocation of internal structures + +USB-specific: + +-ENODEV specified USB-device or bus doesn't exist + +-ENXIO specified endpoint doesn't exist on the device + +USB_ST_URB_INVALID_ERROR +-EINVAL a) Invalid transfer type specified (or not supported) + b) Invalid interrupt interval (0<=n<256) + c) more than one interrupt packet requested + +-EAGAIN a) specified ISO start frame too early + b) (using ISO-ASAP) too much scheduled for the future + wait some time and try again. + +-EFBIG too much ISO frames requested (currently uhci>900) + +-EPIPE specified pipe-handle is already stalled + +-EMSGSIZE endpoint message size is zero, do interface/alternate setting + + +************************************************************************** +* Error codes returned by in urb->status * +* or in iso_frame_desc[n].status (for ISO) * +************************************************************************** + +USB_ST_NOERROR +0 Transfer completed successfully + +USB_ST_URB_KILLED +-ENOENT URB was canceled by unlink_urb + +USB_ST_URB_PENDING +-EINPROGRESS URB still pending, no results yet + (actually no error until now;-) + +USB_ST_BITSTUFF +USB_ST_INTERNALERROR +-EPROTO a) bitstuff error + b) unknown USB error + +USB_ST_CRC +-EILSEQ CRC mismatch + +-EPIPE a) babble detect + b) endpoint stalled + +USB_ST_BUFFERUNDERRUN +-ENOST buffer error + +USB_ST_NORESPONSE +USB_ST_TIMEOUT +-ETIMEDOUT transfer timed out, NAK + +USB_ST_REMOVED +-ENODEV device was removed + +USB_ST_SHORT_PACKET +-EREMOTEIO short packet detected + +USB_ST_PARTIAL_ERROR +-EXDEV ISO transfer only partially completed + look at individual frame status for details + +USB_ST_URB_INVALID_ERROR +-EINVAL ISO madness, if this happens: Log off and go home + +************************************************************************** +* Error codes returned by usbcore-functions * +* (expect also other submit and transfer status codes) * +************************************************************************** + +usb_register(): +USB_ST_NOTSUPPORTED +-EINVAL error during registering new driver + +usb_terminate_bulk(): +USB_ST_REMOVED +-ENODEV urb already removed + +usb_get_*/usb_set_*(): + All USB errors (submit/status) can occur + + diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/ibmcam.txt linux-2.2.17/Documentation/usb/ibmcam.txt --- linux-2.2.17-orig/Documentation/usb/ibmcam.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/ibmcam.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,288 @@ +README for Linux device driver for the IBM "C-It" USB video camera + +INTRODUCTION: + +This driver does not use all features known to exist in +the IBM camera. However most of needed features work well. + +This driver was developed using logs of observed USB traffic +which was produced by standard Windows driver (c-it98.sys). +I did not have data sheets from Xirlink. + +Video formats: + 128x96 [model 1] + 176x144 + 320x240 [model 2] + 352x240 [model 2] + 352x288 +Frame rate: 3 - 30 frames per second (FPS) +External interface: USB +Internal interface: Video For Linux (V4L) +Supported controls: +- by V4L: Contrast, Brightness, Color, Hue +- by driver options: frame rate, lighting conditions, video format, + default picture settings, sharpness. + +SUPPORTED CAMERAS: + +IBM "C-It" camera, also known as "Xirlink PC Camera" +The device uses proprietary ASIC (and compression method); +it is manufactured by Xirlink. See http://www.xirlink.com/ +http://www.ibmpccamera.com or http://www.c-itnow.com/ for +details and pictures. + +The Linux driver was developed with camera with following +model number (or FCC ID): KSX-XVP510. This camera has three +interfaces, each with one endpoint (control, iso, iso). This +type of cameras is referred to as "model 1". These cameras are +no longer manufactured. + +Xirlink now manufactures new cameras which are somewhat different. +In particular, following models [FCC ID] belong to that category: + +XVP300 [KSX-X9903] +XVP600 [KSX-X9902] +XVP610 [KSX-X9902] + +(see http://www.xirlink.com/ibmpccamera/ for updates, they refer +to these new cameras by Windows driver dated 12-27-99, v3005 BETA) +These cameras have two interfaces, one endpoint in each (iso, bulk). +Such type of cameras is referred to as "model 2". They are supported +(with exception of 352x288 native mode). + +Quirks of Model 2 cameras: +------------------------- + +Model 2 does not have hardware contrast control. Corresponding V4L +control is not used at the moment. It may be possible to implement +contrast control in software, at cost of extra processor cycles. + +This driver provides 352x288 mode by switching the camera into +quasi-352x288 RGB mode (800 Kbits per frame) essentially limiting +this mode to 10 frames per second or less, in ideal conditions on +the bus (USB is shared, after all). The frame rate +has to be programmed very conservatively. Additional concern is that +frame rate depends on brightness setting; therefore the picture can +be good at one brightness and broken at another! I did not want to fix +the frame rate at slowest setting, but I had to move it pretty much down +the scale (so that framerate option barely matters). I also noticed that +camera after first powering up produces frames slightly faster than during +consecutive uses. All this means that if you use videosize=2 (which is +default), be warned - you may encounter broken picture on first connect; +try to adjust brightness - brighter image is slower, so USB will be able +to send all data. However if you regularly use Model 2 cameras you may +prefer videosize=1 which makes perfectly good I420, with no scaling and +lesser demands on USB (300 Kbits per second, or 26 frames per second). + +The camera that I had also has a hardware quirk: if disconnected, +it needs few minutes to "relax" before it can be plugged in again +(poorly designed USB processor reset circuit?) + +Model 2 camera can be programmed for very high sensitivity (even starlight +may be enough), this makes it convenient for tinkering with. The driver +code has enough comments to help a programmer to tweak the camera +as s/he feels necessary. + +WHAT YOU NEED: + +- A supported IBM PC (C-it) camera (model 1 or 2) + +- A Linux box with USB support (2.3/2.4; 2.2 w/backport may work) + +- A Video4Linux compatible frame grabber program such as xawtv. + +HOW TO COMPILE THE DRIVER: + +You need to compile the driver only if you are a developer +or if you want to make changes to the code. Most distributions +precompile all modules, so you can go directly to the next +section "HOW TO USE THE DRIVER". + +The driver consists of two files in usb/ directory: +ibmcam.c and ibmcam.h These files are included into the +Linux kernel build process if you configure the kernel +for CONFIG_USB_IBMCAM. Run "make xconfig" and in USB section +you will find the IBM camera driver. Select it, save the +configuration and recompile. + +HOW TO USE THE DRIVER: + +I recommend to compile driver as a module. This gives you an +easier access to its configuration. The camera has many more +settings than V4L can operate, so some settings are done using +module options. + +Typically module is installed with command 'modprobe', like this: + +# modprobe ibmcam framerate=1 + +Alternatively you can use 'insmod' in similar fashion: + +# insmod /lib/modules/2.x.y/usb/ibmcam.o framerate=1 + +Module can be inserted with camera connected or disconnected. + +The driver can have options, though some defaults are provided. + +Driver options: (* indicates that option is model-dependent) + +Name Type Range [default] Example +-------------- -------------- -------------- ------------------ +debug Integer 0-9 [0] debug=1 +flags Integer 0-0xFF [0] flags=0x0d +framerate Integer 0-6 [2] framerate=1 +hue_correction Integer 0-255 [128] hue_correction=115 +init_brightness Integer 0-255 [128] init_brightness=100 +init_contrast Integer 0-255 [192] init_contrast=200 +init_color Integer 0-255 [128] init_color=130 +init_hue Integer 0-255 [128] init_hue=115 +lighting Integer 0-2* [1] lighting=2 +sharpness Integer 0-6* [4] sharpness=3 +videosize Integer 0-2* [2] videosize=1 + +Options for Model 2 only: + +Name Type Range [default] Example +-------------- -------------- -------------- ------------------ +init_model2_rg Integer 0..255 [0x70] init_model2_rg=128 +init_model2_rg2 Integer 0..255 [0x2f] init_model2_rg2=50 +init_model2_sat Integer 0..255 [0x34] init_model2_sat=65 +init_model2_yb Integer 0..255 [0xa0] init_model2_yb=200 + +debug You don't need this option unless you are a developer. + If you are a developer then you will see in the code + what values do what. 0=off. + +flags This is a bit mask, and you can combine any number of + bits to produce what you want. Usually you don't want + any of extra features this option provides: + + FLAGS_RETRY_VIDIOCSYNC 1 This bit allows to retry failed + VIDIOCSYNC ioctls without failing. + Will work with xawtv, will not + with xrealproducer. Default is + not set. + FLAGS_MONOCHROME 2 Activates monochrome (b/w) mode. + FLAGS_DISPLAY_HINTS 4 Shows colored pixels which have + magic meaning to developers. + FLAGS_OVERLAY_STATS 8 Shows tiny numbers on screen, + useful only for debugging. + FLAGS_FORCE_TESTPATTERN 16 Shows blue screen with numbers. + FLAGS_SEPARATE_FRAMES 32 Shows each frame separately, as + it was received from the camera. + Default (not set) is to mix the + preceding frame in to compensate + for occasional loss of Isoc data + on high frame rates. + FLAGS_CLEAN_FRAMES 64 Forces "cleanup" of each frame + prior to use; relevant only if + FLAGS_SEPARATE_FRAMES is set. + Default is not to clean frames, + this is a little faster but may + produce flicker if frame rate is + too high and Isoc data gets lost. + +framerate This setting controls frame rate of the camera. This is + an approximate setting (in terms of "worst" ... "best") + because camera changes frame rate depending on amount + of light available. Setting 0 is slowest, 6 is fastest. + Beware - fast settings are very demanding and may not + work well with all video sizes. Be conservative. + +hue_correction This highly optional setting allows to adjust the + hue of the image in a way slightly different from + what usual "hue" control does. Both controls affect + YUV colorspace: regular "hue" control adjusts only + U component, and this "hue_correction" option similarly + adjusts only V component. However usually it is enough + to tweak only U or V to compensate for colored light or + color temperature; this option simply allows more + complicated correction when and if it is necessary. + +init_brightness These settings specify _initial_ values which will be +init_contrast used to set up the camera. If your V4L application has +init_color its own controls to adjust the picture then these +init_hue controls will be used too. These options allow you to + preconfigure the camera when it gets connected, before + any V4L application connects to it. Good for webcams. + +init_model2_rg These initial settings alter color balance of the +init_model2_rg2 camera on hardware level. All four settings may be used +init_model2_sat to tune the camera to specific lighting conditions. These +init_model2_yb settings only apply to Model 2 cameras. + +lighting This option selects one of three hardware-defined + photosensitivity settings of the camera. 0=bright light, + 1=Medium (default), 2=Low light. This setting affects + frame rate: the dimmer the lighting the lower the frame + rate (because longer exposition time is needed). The + Model 2 cameras allow values more than 2 for this option, + thus enabling extremely high sensitivity at cost of frame + rate, color saturation and imaging sensor noise. + +sharpness This option controls smoothing (noise reduction) + made by camera. Setting 0 is most smooth, setting 6 + is most sharp. Be aware that CMOS sensor used in the + camera is pretty noisy, so if you choose 6 you will + be greeted with "snowy" image. Default is 4. Model 2 + cameras do not support this feature. + +videosize This setting chooses one if three image sizes that are + supported by this driver. Camera supports more, but + it's difficult to reverse-engineer all formats. + Following video sizes are supported: + + videosize=0 128x96 (Model 1 only) + videosize=1 176x144 + videosize=2 352x288 + videosize=3 320x240 (Model 2 only) + videosize=4 352x240 (Model 2 only) + + The last one (352x288) is the native size of the sensor + array, so it's the best resolution camera (Model 1) can + yield. The best resolution of Model 2 is 176x144, and + larger images are produced by stretching the bitmap. + Choose the image size you need. The smaller image can + support faster frame rate. Default is 352x288. + +WHAT NEEDS TO BE DONE: + +- The box freezes if camera is unplugged after being used (OHCI). + Workaround: remove usb-ohci module first. +- On occasion camera (model 1) does not start properly (xawtv reports + errors), or camera produces negative image (funny colors.) + Workaround: reload the driver module. Reason: [1]. +- The button on the camera is not used. I don't know how to get to it. + I know now how to read button on Model 2, but what to do with it? + +[1] +- Camera reports its status back to the driver; however I don't know + what returned data means. If camera fails at some initialization + stage then something should be done, and I don't do that because + I don't even know that some command failed. This is mostly Model 1 + concern because Model 2 uses different commands which do not return + status (and seem to complete successfully every time). + +VIDEO SIZE AND IMAGE SIZE + +Camera produces picture X by Y pixels. This is camera-specific and can be +altered by programming the camera accordingly. This image is placed onto +larger (or equal) area W by H, this is V4L image. At this time the driver +uses V4L image size (W by H) 352x288 pixels because many programs (such +as xawtv) expect quite specific sizes and don't want to deal with arbitrary, +camera-specific sizes. However this approach "hides" real image size, and +application always sees the camera as producing only 352x288 image. It is +possible to change the V4L image size to 128x96, and then if camera is +switched to 128x96 mode then xawtv will correctly accept this image size. But +many other popular sizes (such as 176x144) will not be welcomed. This is the +reason why all camera images are at this time placed onto 352x288 "canvas", +and size of that canvas (V4L) is reported to applications. It will be easy +to add options to control the canvas size, but it will be application- +specific because not all applications are ready to work with variety of +camera-specific sizes. + +CREDITS: + +The code is based in no small part on the CPiA driver by Johannes Erdfelt, +Randy Dunlap, and others. Big thanks to them for their pioneering work on that +and the USB stack. diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/input.txt linux-2.2.17/Documentation/usb/input.txt --- linux-2.2.17-orig/Documentation/usb/input.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/input.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,306 @@ + Linux Input drivers v1.0 + (c) 1999-2000 Vojtech Pavlik + Sponsored by SuSE + $Id: input.txt,v 1.4 2000/05/28 17:57:22 vojtech Exp $ +---------------------------------------------------------------------------- + +0. Disclaimer +~~~~~~~~~~~~~ + 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 + + Should you need to contact me, the author, you can do so either by e-mail +- mail your message to , or by paper mail: Vojtech Pavlik, +Ucitelska 1576, Prague 8, 182 00 Czech Republic + + For your convenience, the GNU General Public License version 2 is included +in the package: See the file COPYING. + +1. Introduction +~~~~~~~~~~~~~~~ + This is a collection of drivers that is designed to support all input +devices under Linux. However, in the current kernels, although it's +possibilities are much bigger, it's limited to USB devices only. This is +also why it resides in the drivers/usb subdirectory. + + The center of the input drivers is the input.o module, which must be +loaded before any other of the input modules - it serves as a way of +communication between two groups of modules: + +1.1 Device drivers +~~~~~~~~~~~~~~~~~~ + These modules talk to the hardware (for example via USB), and provide +events (keystrokes, mouse movements) to the input.o module. + +1.2 Event handlers +~~~~~~~~~~~~~~~~~~ + These modules get events from input.o and pass them where needed via +various interfaces - keystrokes to the kernel, mouse movements via a +simulated PS/2 interface to GPM and X and so on. + +2. Simple Usage +~~~~~~~~~~~~~~~ + For the most usual configuration, with one USB mouse and one USB keyboard, +you'll have to load the following modules (or have them built in to the +kernel): + + input.o + mousedev.o + keybdev.o + usbcore.o + usb-[uo]hci.o + hid.o + + After this, the USB keyboard will work straight away, and the USB mouse +will be available as a character device on major 13, minor 63: + + crw-r--r-- 1 root root 13, 63 Mar 28 22:45 mice + + This device, has to be created, unless you use devfs, in which case it's +created automatically. The commands to do that are: + + cd /dev + mkdir input + mknod input/mice c 13 63 + + After that you have to point GPM (the textmode mouse cut&paste tool) and +XFree to this device to use it - GPM should be called like: + + gpm -t ps2 -m /dev/input/mice + + And in X: + + Section "Pointer" + Protocol "ImPS/2" + Device "/dev/input/mice" + ZAxisMapping 4 5 + EndSection + + When you do all of the above, you can use your USB mouse and keyboard. + +3. Detailed Description +~~~~~~~~~~~~~~~~~~~~~~~ +3.1 Device drivers +~~~~~~~~~~~~~~~~~~ + Device drivers are the modules that generate events. The events are +however not useful without being handled, so you also will need to use some +of the modules from section 3.2. + +3.1.1 hid.c +~~~~~~~~~~~ + Hid.c is the largest and most complex driver of the whole suite. It +handles all HID devices, and because there is a very wide variety of them, +and because the USB HID specification isn't simple, it needs to be this big. + + Currently, it handles USB mice, joysticks, gamepads, steering wheels +keyboards, trackballs and digitizers. + + However, USB uses HID also for monitor controls, speaker controls, UPSs, +LCDs and many other purposes. + + The monitor and speaker controls should be easy to add to the hid/input +interface, but for the UPSs and LCDs it doesn't make much sense. The driver +doesn't support these yet, and a new, non-event interface should be designed +for them. If you have any of these devices (I don't), feel free to design +something and if it's good, it'll get into the driver. + + The usage of the hid.o module is very simple, it takes no parameters, +detects everything automatically and when a HID device is inserted, it +detects it appropriately. + + However, because the devices vary wildly, you might happen to have a +device that doesn't work well. In that case #define DEBUG at the beginning +of hid.c and send me the syslog traces. + +3.1.2 usbmouse.c +~~~~~~~~~~~~~~~~ + For embedded systems, for mice with broken HID descriptors and just any +other use when the big hid.c wouldn't be a good choice, there is the +usbmouse.c driver. It handles USB mice only. It uses a simpler HIDBP +protocol. This also means the mice must support this simpler protocol. Not +all do. If you don't have any strong reason to use this module, use hid.c +instead. + +3.1.3 usbkbd.c +~~~~~~~~~~~~~~ + Much like usbmouse.c, this module talks to keyboards with a simpplified +HIDBP protocol. It's smaller, but doesn't support any extra special keys. +Use hid.c instead if there isn't any special reason to use this. + +3.1.4 wacom.c +~~~~~~~~~~~~~ + This is a driver for Wacom Graphire and Intuos tablets. Not for Wacom +PenPartner, that one is handled by the HID driver. Although the Intuos and +Graphire tablets claim that they are HID tablets as well, they are not and +thus need this specific driver. + +3.1.5 wmforce.c +~~~~~~~~~~~~~~~ + A driver for the Logitech WingMan Force joysticks, when connected via the +USB port. It works quite well, but there is no force feedback support yet, +because the interface to do that is a trade secret of Immersion Corp, and +even Logitech can't disclose it. + + Support for Logitech WingMan Force Wheel isn't present in this driver, but +if someone has the device, and is willing to cooperate, it should be a +matter of a couple days to add it. + +3.2 Event handlers +~~~~~~~~~~~~~~~~~~ + Event handlers distrubite the events from the devices to userland and +kernel, as needed. + +3.2.1 keybdev.c +~~~~~~~~~~~~~~~ + Keybdev is currently a rather ugly hack that translates the input events +into architecture-specific keyboard raw mode (Xlated AT Set2 on x86), and +passes them into the handle_scancode function of the keyboard.c module. This +works well enough on all architectures that keybdev can generate rawmode on, +other architectures can be added to it. + + The right way would be to pass the events to keyboard.c directly, best if +keyboard.c would itself be an event handler. This is done in the input +patch, available on the webpage mentioned below. + +3.2.2 mousedev.c +~~~~~~~~~~~~~~~~ + Mousedev is also a hack to make programs that use mouse input work. It +takes events from either mice or digitizers/tablets and makes a PS/2-style +(a la /dev/psaux) mouse device available to the userland. Ideally, the +programs could use a more reasonable interface, for example evdev.c + + Mousedev devices in /dev/input (as shown above) are: + + crw-r--r-- 1 root root 13, 32 Mar 28 22:45 mouse0 + crw-r--r-- 1 root root 13, 33 Mar 29 00:41 mouse1 + crw-r--r-- 1 root root 13, 34 Mar 29 00:41 mouse2 + crw-r--r-- 1 root root 13, 35 Apr 1 10:50 mouse3 + ... + ... + crw-r--r-- 1 root root 13, 62 Apr 1 10:50 mouse30 + crw-r--r-- 1 root root 13, 63 Apr 1 10:50 mice + +Each 'mouse' device is assigned to a single mouse or digitizer, except the last +one - 'mice'. This single character device is shared by all mice and +digitizers, and even if none are connected, the device is present. This is +useful for hotplugging USB mice, so that programs can open the device even when +no mice are present. + + CONFIG_INPUT_MOUSEDEV_SCREEN_[XY] in the kernel configuration are the size +of your screen (in pixels) in XFree86. This is needed if you want to use +your digitizer in X, because it's movement is sent to X via a virtual PS/2 +mouse and thus needs to be scaled accordingly. These values won't be used if +you use a mouse only. + + Mousedev will generate either PS/2, ImPS/2 (Microsoft IntelliMouse) or +GenPS/2 (Genius NetMouse/NetScroll) protocols, depending on what the program +reading the data wishes. You can set GPM and X to any of these. You'll need +ImPS/2 if you want to make use of a wheel on a USB mouse and GenPS/2 if you +want to use extra (up to 5) buttons. I'm not sure how much is GenPS/2 supported +in X, though. + +3.2.3 joydev.c +~~~~~~~~~~~~~~ + Joydev implements v0.x and v1.x Linux joystick api, much like +drivers/char/joystick/joystick.c. See joystick-api.txt in the Documentation +subdirectory for details. Joydev does it on top of the input subsystem, +though. As soon as any USB joystick is connected, it can be accessed in +/dev/input on: + + crw-r--r-- 1 root root 13, 0 Apr 1 10:50 js0 + crw-r--r-- 1 root root 13, 1 Apr 1 10:50 js1 + crw-r--r-- 1 root root 13, 2 Apr 1 10:50 js2 + crw-r--r-- 1 root root 13, 3 Apr 1 10:50 js3 + ... + +And so on up to js31. + +3.2.4 evdev.c +~~~~~~~~~~~~~ + Evdev is the generic input event interface. It passes the events generated +in the kernel straight to the program, with timestamps. The API is still +evolving, but should be useable now. It's described in section 5. + + This should be the way for GPM and X to get keyboard and mouse mouse +events. It allows for multihead in X without any specific multihead kernel +support. The event codes are the same on all architectures and are hardware +independent. + + The devices are in /dev/input: + + crw-r--r-- 1 root root 13, 64 Apr 1 10:49 event0 + crw-r--r-- 1 root root 13, 65 Apr 1 10:50 event1 + crw-r--r-- 1 root root 13, 66 Apr 1 10:50 event2 + crw-r--r-- 1 root root 13, 67 Apr 1 10:50 event3 + ... + +3. Contacts +~~~~~~~~~~~ + This effort has it's home page at: + + http://www.suse.cz/development/input/ + +You'll find both the latest HID driver and the complete Input driver there +as well as information how to access the CVS repository for latest revisions +of the drivers. + + + There is also a mailing list for this: + + majordomo@atrey.karlin.mff.cuni.cz + +Send "subscribe linux-input" to subscribe to it. + +4. Verifying if it works +~~~~~~~~~~~~~~~~~~~~~~~~ + Typing a couple keys on the keyboard should be enough to check that a USB +keyboard works and is correctly connected to the kernel keyboard driver. + + Doing a cat /dev/input/mouse0 (c, 13, 32) will verify that a mouse is also +emulated, characters should appear if you move it. + + You can test the joystick emulation with the 'jstest' utility, available +in the joystick package (see Documentation/joystick.txt). + + You can test the event devics with the 'evtest' utitily available on the +input driver homepage (see the URL above). + +5. Event interface +~~~~~~~~~~~~~~~~~~ + Should you want to add event device support into any application (X, gpm, +svgalib ...) I will be happy to provide you any help I +can. Here goes a description of the current state of things, which is going +to be extended, but not changed incompatibly as time goes: + + You can use blocking and nonblocking reads, also select() on the +/dev/input/eventX devices, and you'll always get a whole number of input +events on a read. Their layout is: + +struct input_event { + struct timeval time; + unsigned short type; + unsigned short code; + unsigned int value; +}; + + 'time' is the timestamp, it returns the time at which the event happened. +Type is for example EV_REL for relative momement, REL_KEY for a keypress or +release. More types are defined in include/linux/input.h. + + 'code' is event code, for example REL_X or KEY_BACKSPACE, again a complete +list is in include/linux/input.h. + + 'value' is the value the event carries. Either a relative change for +EV_REL, absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for +release, 1 for keypress and 2 for autorepeat. diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/ohci.txt linux-2.2.17/Documentation/usb/ohci.txt --- linux-2.2.17-orig/Documentation/usb/ohci.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/ohci.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,98 @@ + +The OHCI HCD layer is a simple but nearly complete implementation of what the +USB people would call a HCD for the OHCI. + (ISO comming soon, Bulk, INT u. CTRL transfers enabled) +It is based on Linus Torvalds UHCI code and Gregory Smith OHCI fragments (0.03 source tree). +The layer (functions) on top of it, is for interfacing to the alternate-usb device-drivers. + +- Roman Weissgaerber + + * v4.0 1999/08/18 removed all dummy eds, unlink unused eds, code cleanup, bulk transfers + * v2.1 1999/05/09 ep_addr correction, code cleanup + * v0.2.0 1999/05/04 + * everything has been moved into 2 files (ohci-hcd.c, ohci-hub-root.c and headers) + * virtual root hub is now an option, + * memory allocation based on kmalloc and kfree now, simple Bus error handling, + * INT and CTRL transfers enabled, Bulk included but disabled, ISO needs completion + * + * from Linus Torvalds (uhci.c): APM (not tested); hub, usb_device, bus and related stuff + * from Greg Smith (ohci.c): better reset ohci-controller handling, hub + * + * v0.1.0 1999/04/27 initial release + +to remove the module try: +rmmod usb-ohci + +Features: +- virtual root hub, all basic hub descriptors and commands (state: complete) + this is an option now (v0.2.0) + #define CONFIG_USB_OHCI_VROOTHUB includes the virtual hub code, (VROOTHUB) + default is with. + (at the moment: the Virtual Root Hub is included automatically) + + files: ohci-root-hub.c, ohci-root-hub.h + + +- Endpoint Descriptor (ED) handling more static approach + (EDs should be allocated in parallel to the SET CONFIGURATION command and they live + as long as the function (device) is alive or another configuration is choosen. + In the HCD layer the EDs has to be allocated manually either by calling a subroutine + or by sending a USB root hub vendor specific command to the virtual root hub. + At the alternate linux usb stack EDs will be added (allocated) at their first use. + ED will be unlinked from the HC chains if they are not bussy. + + files: ohci-hcd.c ohci-hcd.h + routines: (do not use for drivers, use the top layer alternate usb commands instead) + + int usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr1, + int interval, int load, f_handler handler, int ep_size, int speed) + adds an endpoint, (if the endpoint already exists some parameters will be updated) + + int usb_ohci_rm_ep( ) + removes an endpoint and all pending TDs of that EP + + usb_ohci_rm_function( ) + removes all Endpoints of a function (device) + +- Transfer Descriptors (TD): handling and allocation of TDs is transparent to the upper layers + The HCD takes care of TDs and EDs memory allocation whereas the upper layers (UBSD ...) has + to take care of buffer allocation. + files: ohci-hcd.c ohci-hcd.h + + There is one basic command for all types of bus transfers (INT, BULK, ISO, CTRL): + + int ohci_trans_req(struct ohci * ohci, hcd_ed, int ctrl_len, void *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1) + + CTRL: ctrl, ctrl_len ... cmd buffer + data, data_len ... data buffer (in or out) + INT, BULK: ctrl = NULL, ctrl_len=0, + data, data_len ... data buffer (in or out) + ISO: tbd + + There is no buffer reinsertion done by the internal HCD function. + (The interface layer does this for a INT-pipe on request.) + If you want a transfer then you have to + provide buffers by sending ohci_trans_req requests. As they are queued as TDs on an ED + you can send as many as you like. They should come back by the callback f_handler in + the same order (for each endpoint, not globally) If an error occurs all + queued transfers of an endpoint will return unsent. They will be marked with an error status. + + e.g double-buffering for int transfers: + + ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0) + ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0) + + and when a data0 packet returns by the callback f_handler requeue it: + ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0) + and when a data1 packet returns by the callback f_handler requeue it: + ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0) + + lw0, lw1 are private fields for upper layers for ids or fine grained handlers. + The alternate usb uses them for dev_id and usb_device_irq handler. + + +- Done list handling: returns the requests (callback f_handler in ED) and does + some error handling, root-hub request dequeuing + (files: ohci-done-list.c in ohci-hcd.c now(v0.2.0)) + + diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/ov511.txt linux-2.2.17/Documentation/usb/ov511.txt --- linux-2.2.17-orig/Documentation/usb/ov511.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/ov511.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,225 @@ +------------------------------------------------------------------------------- +Readme for Linux device driver for the OmniVision OV511 USB to camera bridge IC +------------------------------------------------------------------------------- + +Author: Mark McClelland +Homepage: http://alpha.dyndns.org/ov511 + +NEW IN THIS VERSION: + o Race conditions and other bugs fixed + +INTRODUCTION: + +This is a driver for the OV511, a USB-only chip used in many "webcam" devices. +Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work.It +supports streaming and capture of color or monochrome video via the Video4Linux +API. Most V4L apps are compatible with it, but a few videoconferencing programs +do not work yet. The following resolutions are supported: 640x480, 448x336, +384x288, 352x288, and 320x240. + +WHAT YOU NEED: + +- If you want to help with the development, get the chip's specification docs at + http://www.ovt.com/omniusbp.html + +- A Video4Linux compatible frame grabber program (I recommend vidcat and xawtv) + vidcat is part of the w3cam package: http://www.hdk-berlin.de/~rasca/w3cam/ + xawtv is available at: http://www.in-berlin.de/User/kraxel/xawtv.html + +HOW TO USE IT: + +You must have first compiled USB support, support for your specific USB host +controller (UHCI or OHCI), and Video4Linux support for your kernel (I recommend +making them modules.) + +Next, (as root) from your appropriate modules directory (lib/modules/2.3.XX): + + insmod usb/usbcore.o + insmod usb/usb-uhci.o insmod usb/ohci-hcd.o + insmod misc/videodev.o + insmod usb/ov511.o + +If it is not already there (it usually is), create the video device: + + mknod /dev/video c 81 0 + +Sometimes /dev/video is a symlink to /dev/video0 + +You will have to set permissions on this device to allow you to read/write +from it: + + chmod 666 /dev/video + chmod 666 /dev/video0 (if necessary) + +Now you are ready to run a video app! Both vidcat and xawtv work well for me +at 640x480. + +[Using vidcat:] + + vidcat -s 640x480 > test.jpg + xview test.jpg + +[Using xawtv:] + +You must make some modifications to the source and compile it before you use it. +(Note: this may not be applicable to versions other than 3.06) + +In src/Xawtv.ad, change xawtv.tv.width to 640 and xawtv.tv.height to 480. Next, +in src/grab-v4l.c, change SYNC_TIMEOUT from 1 to 2. Then, from the main xawtv +directory: + + make clean + ./configure + make + make install + +Now you should be able to run xawtv. Right click for the options dialog. If +you get a scrambled image it is likely that you made a mistake in Xawtv.ad. +Try setting the size to 320x240 if all else fails. + +FAQ: +Q: "Why does the picture have noise and look grainy" +A: This is a problem at low light levels, and may be also due to subtle bugs in + the code. The cause is most likely the OV7610 settings we are currently + using. I am looking into this problem. + +Q: "The driver sometimes says `Failed to read OV7610 ID.' What is the deal?" +A: The I2C code that allows the OV511 to communicate with the camera chip is a + bit flaky right now. This message means that the I2C bus never got + initialized properly, and the camera will most likely not work even if you + disable this warning. Try unloading/reloading the driver or unplugging/re- + plugging the camera if this happens. Also try increasing the i2c_detect_tries + parameter (see below). + +Q: "Why do you bother with this phony camera detection crap? It doesn't do + anything useful!" +A: The main purpose of only supporting known camera models is to force people + with new camera models to tell me about them, so I can assemble the list + above, and so the code can know what CCD chip you have. Right now, nearly all + of the cameras use the OV7610 and consequently I have not put support for + other ones in, so the value of the detection code is questionable. Eventually + though, new CCDs might appear and we will be fortunate to have the detection. + +MODULE PARAMETERS: + + You can set these with: insmod ov511 NAME=VALUE + There is currently no way to set these on a per-camera basis. + + NAME: autoadjust + TYPE: integer (boolean) + DEFAULT: 1 + DESC: The camera normally adjusts exposure, gain, and hue automatically. This + can be set to 0 to disable this automatic adjustment. Note that there is + currently no way to set these parameters manually once autoadjust is + disabled. + + NAME: debug + TYPE: integer (0-6) + DEFAULT: 3 + DESC: Sets the threshold for printing debug messages. The higher the value, + the more is printed. The levels are cumulative, and are as follows: + 0=no debug messages + 1=init/detection/unload and other significant messages + 2=some warning messages + 3=config/control function calls + 4=most function calls and data parsing messages + 5=highly repetitive mesgs + + NAME: fix_rgb_offset + TYPE: integer (boolean) + DEFAULT: 0 + DESC: Some people have reported that the blue component of the image is one + or so lines higher than the red component. This is only apparent in + images with white objects on black backgrounds at 640x480. Setting this + to 1 will realign the color planes correctly. NOTE: This is still + experimental and very buggy. You will likely need a fast (500 Mhz) CPU. + + NAME: snapshot + TYPE: integer (boolean) + DEFAULT: 0 + DESC: Set to 1 to enable snapshot mode. read() will block until the snapshot + button is pressed. Note that this does not yet work with most apps, + including xawtv and vidcat. NOTE: See the section "TODO" for more info. + + NAME: sensor + TYPE: integer ([0, 1, 3]) + DEFAULT: [varies] + DESC: If you know that your camera sensor is not detected correctly, set this + parameter. This is a global option for all attached OV511 cameras. You + will probably never need to set this, but if you do, valid values are: + 0 for OV7620 + 1 for OV7620AE + 3 for OV7610 + + NAME: i2c_detect_tries + TYPE: integer (don't set it insanely high!) + DEFAULT: 5 + DESC: This is the number of times the driver will try to sync and detect the + internal i2c bus (which connects the OV511 and sensor). If you are + getting intermittant detection failures ("Failed to read sensor ID...") + you should increase this by a modest amount. If setting it to 20 or so + doesn't fix things, look elsewhere for the cause of the problem. + + NAME: aperture + TYPE: integer (0 - 15) + DEFAULT: [varies by sensor] + DESC: For legal values, see the OV7610/7620 specs under register Common F. + This setting affects the upper nybble of that reg (bits 4-7). This is + for if you want to play with the camera's pixel saturation. + + NAME: force_rgb + TYPE: integer (boolean) + DEFAULT: 0 + DESC: Force image to be read in RGB instead of BGR. This option allow + programs that expect RGB data (e.g. gqcam) to work with this driver. If + your colors look VERY wrong, you may want to change this. + +WORKING FEATURES: + o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240 + o YUV420 color + o Monochrome + o Setting/getting of saturation, contrast and brightness (no hue yet; only + works with OV7610, not the OV7620 or OV7620AE) + o proc status reporting + +EXPERIMENTAL FEATURES: + o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and + corrupted frames. If you have a very fast CPU, you can try it. + o Snapshot mode (only works with some read() based apps; see below for more) + o read() support + +TODO: + o Fix the noise / grainy image problem. + o Get compression working. It would be a nice addition as it improves + frame rate quite a bit. OmniVision wouldn't tell me how the algorithm works, + so we can't really work on that yet. Please kindly inform OmniVision that you + would like them to release their specifications to the Linux community. + o Get 160x120 working + o YUV422 (and other color modes) + o Get snapshot mode working with mmap(). + o Fix fixFrameRGBoffset(). It is not stable yet with streaming video. + o Get hue (red/blue channel balance) adjustment working (in ov511_get_picture() + and ov511_set_picture()) + o Get autoadjust disable working + o V4L2 support (Probably not until it goes into the kernel) + o Fix I2C initialization. Some people are reporting problems with reading the + 7610 registers. This could be due to timing differences, an excessive I2C + clock rate, or a problem with ov511_i2c_read(). + o Get rid of the memory management functions (put them in videodev.c??) + o Setting of contrast and brightness not working with 7620 + o Driver/camera state save/restore for when USB supports suspend/resume + o Multiple cameras reportedly do not work simultaneously + o Problems with OHCI + +HOW TO CONTACT ME: + +You can email me at mmcclelland@delphi.com . Please prefix the subject line +with "OV511: " so that I am certain to notice your message. + +CREDITS: + +The code is based in no small part on the CPiA driver by Johannes Erdfelt, +Randy Dunlap, and others. Big thanks to them for their pioneering work on that +and the USB stack. Thanks to Bret Wallach for getting camera reg IO, ISOC, and +image capture working. Thanks to Orion Sky Lawlor, Kevin Moore, and Claudio +Matsuoka for their work as well. diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/proc_usb_info.txt linux-2.2.17/Documentation/usb/proc_usb_info.txt --- linux-2.2.17-orig/Documentation/usb/proc_usb_info.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/proc_usb_info.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,256 @@ +/proc/bus/usb filesystem output +=============================== +(version 2000.03.24) + + +The /proc filesystem for USB devices generates +/proc/bus/usb/drivers and /proc/bus/usb/devices. + +/proc/bus/usb/drivers lists the registered drivers, +one per line, with each driver's USB minor dev node +number range if applicable. + +In /proc/bus/usb/devices, each device's output has multiple +lines of ASCII output. +I made it ASCII instead of binary on purpose, so that someone +can obtain some useful data from it without the use of an +auxiliary program. However, with an auxiliary program, the numbers +in the first 4 columns of each "T:" line (topology info: +Lev, Prnt, Port, Cnt) can be used to build a USB topology diagram. +(I think. I haven't proved this, but I have tested it with 3 +different topo/connections and it looked possible.) + +Each line is tagged with a one-character ID for that line: + +T = Topology (etc.) +B = Bandwidth (applies only to USB host controllers, which are + virtualized as root hubs) +D = Device descriptor info. +P = Product ID info. (from Device descriptor, but they won't fit + together on one line) +S = String descriptors. +C = Configuration descriptor info. (* = active configuration) +I = Interface descriptor info. +E = Endpoint descriptor info. + +======================================================================= + +/proc/bus/usb/devices output format: + +Legend: + d = decimal number (may have leading spaces or 0's) + x = hexadecimal number (may have leading spaces or 0's) + s = string + + +Topology info: + +T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd +| | | | | | | | |__MaxChildren +| | | | | | | |__Device Speed in Mbps +| | | | | | |__DeviceNumber +| | | | | |__Count of devices at this level +| | | | |__Connector/Port on Parent for this device +| | | |__Parent DeviceNumber +| | |__Level in topology for this bus +| |__Bus number +|__Topology info tag + + +Bandwidth info: +B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd +| | | |__Number if isochronous requests +| | |__Number of interrupt requests +| |__Total Bandwidth allocated to this bus +|__Bandwidth info tag + + +Device descriptor info & Product ID info: + +D: Ver=x.xx Cls=xx(s) Sub=xx Prot=xx MxPS=dd #Cfgs=dd +P: Vendor=xxxx ProdID=xxxx Rev=xx.xx + +where +D: Ver=x.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd +| | | | | | |__NumberConfigurations +| | | | | |__MaxPacketSize of Default Endpoint +| | | | |__DeviceProtocol +| | | |__DeviceSubClass +| | |__DeviceClass +| |__Device USB version +|__Device info tag #1 + +where +P: Vendor=xxxx ProdID=xxxx Rev=xx.xx +| | | |__Product revision number +| | |__Product ID code +| |__Vendor ID code +|__Device info tag #2 + + +String descriptor info: + +S: Manufacturer=ssss +| |__Manufacturer of this device as read from the device. +|__String info tag + +S: Product=ssss +| |__Product description of this device as read from the device, +| except that it is a generated string for USB host controllers +| (virtual root hubs), in the form "USB *HCI Root Hub". +|__String info tag + +S: SerialNumber=ssss +| |__Serial Number of this device as read from the device, +| except that it is a generated string for USB host controllers +| (virtual root hubs), and represent's the host controller's +| unique identification in the system (currently I/O or +| memory-mapped base address). +|__String info tag + + +Configuration descriptor info: + +C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA +| | | | |__MaxPower in mA +| | | |__Attributes +| | |__ConfiguratioNumber +| |__NumberOfInterfaces +|__Config info tag + + +Interface descriptor info (can be multiple per Config): + +I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss +| | | | | | | |__Driver name +| | | | | | |__InterfaceProtocol +| | | | | |__InterfaceSubClass +| | | | |__InterfaceClass +| | | |__NumberOfEndpoints +| | |__AlternateSettingNumber +| |__InterfaceNumber +|__Interface info tag + + +Endpoint descriptor info (can be multiple per Interface): + +E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms +E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms +| | | | |__Interval +| | | |__EndpointMaxPacketSize +| | |__Attributes(EndpointType) +| |__EndpointAddress(I=In,O=Out) +|__Endpoint info tag + +======================================================================= + + +If a user or script is interested only in Topology info, for +example, use something like "grep ^T: /proc/bus/usb/devices" +for only the Topology lines. A command like +"grep -i ^[tdp]: /proc/bus/usb/devices" can be used to list +only the lines that begin with the characters in square brackets, +where the valid characters are TDPCIE. With a slightly more able +script, it can display any selected lines (for example, only T, D, +and P lines) and change their output format. (The "procusb" +Perl script is the beginning of this idea. It will list only +selected lines [selected from TBDPSCIE] or "All" lines from +/proc/bus/usb/devices.) + +The Topology lines can be used to generate a graphic/pictorial +of the USB devices on a system's root hub. (See more below +on how to do this.) + +The Interface lines can be used to determine what driver is +being used for each device. + +The Configuration lines could be used to list maximum power +(in milliamps) that a system's USB devices are using. +For example, "grep ^C: /proc/bus/usb/devices". + + +Here's an example, from a system which has a UHCI root hub, +an external hub connected to the root hub, and a mouse and +a serial converter connected to the external hub. + +T: Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 +B: Alloc= 28/900 us ( 3%), #Int= 2, #Iso= 0 +D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 +P: Vendor=0000 ProdID=0000 Rev= 0.00 +S: Product=USB UHCI Root Hub +S: SerialNumber=dce0 +C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA +I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub +E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms +T: Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 4 +D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 +P: Vendor=0451 ProdID=1446 Rev= 1.00 +C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=100mA +I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub +E: Ad=81(I) Atr=03(Int.) MxPS= 1 Ivl=255ms +T: Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=1.5 MxCh= 0 +D: Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 +P: Vendor=04b4 ProdID=0001 Rev= 0.00 +C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA +I: If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=02 Driver=mouse +E: Ad=81(I) Atr=03(Int.) MxPS= 3 Ivl= 10ms +T: Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#= 4 Spd=12 MxCh= 0 +D: Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 +P: Vendor=0565 ProdID=0001 Rev= 1.08 +S: Manufacturer=Peracom Networks, Inc. +S: Product=Peracom USB to Serial Converter +C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=100mA +I: If#= 0 Alt= 0 #EPs= 3 Cls=00(>ifc ) Sub=00 Prot=00 Driver=serial +E: Ad=81(I) Atr=02(Bulk) MxPS= 64 Ivl= 16ms +E: Ad=01(O) Atr=02(Bulk) MxPS= 16 Ivl= 16ms +E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl= 8ms + + +Selecting only the "T:" and "I:" lines from this (for example, by using +"procusb ti"), we have: + +T: Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 +T: Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 4 +I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub +T: Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=1.5 MxCh= 0 +I: If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=02 Driver=mouse +T: Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#= 4 Spd=12 MxCh= 0 +I: If#= 0 Alt= 0 #EPs= 3 Cls=00(>ifc ) Sub=00 Prot=00 Driver=serial + + +Physically this looks like (or could be converted to): + + +------------------+ + | PC/root_hub (12)| Dev# = 1 + +------------------+ (nn) is Mbps. + Level 0 | CN.0 | CN.1 | [CN = connector/port #] + +------------------+ + / + / + +-----------------------+ + Level 1 | Dev#2: 4-port hub (12)| + +-----------------------+ + |CN.0 |CN.1 |CN.2 |CN.3 | + +-----------------------+ + \ \____________________ + \_____ \ + \ \ + +--------------------+ +--------------------+ + Level 2 | Dev# 3: mouse (1.5)| | Dev# 4: serial (12)| + +--------------------+ +--------------------+ + + + +Or, in a more tree-like structure (ports [Connectors] without +connections could be omitted): + +PC: Dev# 1, root hub, 2 ports, 12 Mbps +|_ CN.0: Dev# 2, hub, 4 ports, 12 Mbps + |_ CN.0: Dev #3, mouse, 1.5 Mbps + |_ CN.1: + |_ CN.2: Dev #4, serial, 12 Mbps + |_ CN.3: +|_ CN.1: + + + ### END ### diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/rio.txt linux-2.2.17/Documentation/usb/rio.txt --- linux-2.2.17-orig/Documentation/usb/rio.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/rio.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,138 @@ +Copyright (C) 1999, 2000 Bruce Tenison +Portions Copyright (C) 1999, 2000 David Nelson +Thanks to David Nelson for guidance and the usage of the scanner.txt +and scanner.c files to model our driver and this informative file. + +Mar. 2, 2000 + +CHANGES + +- Initial Revision + + +OVERVIEW + +This README will address issues regarding how to configure the kernel +to access a RIO 500 mp3 player. +Before I explain how to use this to access the Rio500 please be warned: + +W A R N I N G: +-------------- + +Please note that this software is still under development. The authors +are in no way responsible for any damage that may occur, no matter how +inconsequential. + +It seems that the Rio has a problem when sending .mp3 with low batteries. +I suggest when the batteries are low and want to transfer stuff that you +replace it with a fresh one. In my case, what happened is I lost two 16kb +blocks (they are no longer usable to store information to it). But I don't +know if thats normal or not. It could simply be a problem with the flash +memory. + +In an extreme case, I left my Rio playing overnight and the batteries wore +down to nothing and appear to have corrupted the flash memory. My RIO +needed to be replaced as a result. Diamond tech support is aware of the +problem. Do NOT allow your batteries to wear down to nothing before +changing them. It appears RIO 500 firmware does not handle low battery +power well at all. + +On systems with OHCI controllers, the kernel OHCI code appears to have +power on problems with some chipsets. If you are having problems +connecting to your RIO 500, try turning it on first and then plugging it +into the USB cable. + +Contact information: +-------------------- + + The main page for the project is hosted at sourceforge.net in the following + address: http://rio500.sourceforge.net You can also go to the sourceforge + project page at: http://sourceforge.net/project/?group_id=1944 There is + also a mailing list: rio500-users@lists.sourceforge.net + +Authors: +------- + +Most of the code was written by Cesar Miquel . Keith +Clayton is incharge of the PPC port and making sure +things work there. Bruce Tenison is adding support +for .fon files and also does testing. The program will mostly sure be +re-written and Pete Ikusz along with the rest will re-design it. I would +also like to thank Tri Nguyen who provided use +with some important information regarding the communication with the Rio. + +ADDITIONAL INFORMATION and Userspace tools + +http://rio500.sourceforge.net/ + + +REQUIREMENTS + +A host with a USB port. Ideally, either a UHCI (Intel) or OHCI +(Compaq and others) hardware port should work. + +A Linux development kernel (2.3.x) with USB support enabled or a +backported version to linux-2.2.x. See http://www.linux-usb.org for +more information on accomplishing this. + +A Linux kernel with RIO 500 support enabled. + +'lspci' which is only needed to determine the type of USB hardware +available in your machine. + +CONFIGURATION + +Using `lspci -v`, determine the type of USB hardware available. + + If you see something like: + + USB Controller: ...... + Flags: ..... + I/O ports at .... + + Then you have a UHCI based controller. + + If you see something like: + + USB Controller: ..... + Flags: .... + Memory at ..... + + Then you have a OHCI based controller. + +Using `make menuconfig` or your preferred method for configuring the +kernel, select 'Support for USB', 'OHCI/UHCI' depending on your +hardware (determined from the steps above), 'USB Diamond Rio500 support', and +'Preliminary USB device filesystem'. Compile and install the modules +(you may need to execute `depmod -a` to update the module +dependencies). + +Add a device for the USB rio500: + `mknod /dev/usb/rio500 c 180 64` + +Set appropriate permissions for /dev/usb/rio500 (don't forget about +group and world permissions). Both read and write permissions are +required for proper operation. + +Load the appropriate modules (if compiled as modules): + + OHCI: + modprobe usbcore + modprobe usb-ohci + modprobe rio500 + + UHCI: + modprobe usbcore + modprobe usb-uhci (or uhci) + modprobe rio500 + +That's it. The Rio500 Utils at: http://rio500.sourceforge.net should +be able to access the rio500. + +BUGS + +If you encounter any problems feel free to drop me an email. + +Bruce Tenison +btenison@dibbs.net + diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/scanner-hp-sane.txt linux-2.2.17/Documentation/usb/scanner-hp-sane.txt --- linux-2.2.17-orig/Documentation/usb/scanner-hp-sane.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/scanner-hp-sane.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,81 @@ +Copyright (C) 1999, 2000 David E. Nelson + +April 26, 2000 + +CHANGES + +- Amended for Linux-2.3.99-pre6-3 +- Updated for multiple scanner support + +INTRODUCTION + +This document will hopefully provide enough info on how to get SANE +working with a Hewlett Packard USB capable scanner using the USB +interface. The majority of HP Scanners support the Scanner Control +Language (SCL) which is both published by HP and supported by SANE. +The only HP Scanners that I'm aware of that do not support SCL are the +4200C ,3300C, and the PhotoSmart S20. All other HP scanners with USB +interfaces should work (4100C, 5200C, 6200C, and 6300C) as do models +that are derived from the models above. ie the 6350C which is a 6300C +with a transparency adaptor included with the scanner at time of +purchase. Of course as HP releases new scanners this information may +change. + + +REQUIREMENTS + +In order to get this running you'll need USB support in your kernel in +addition to USB Scanner support. Please refer to scanner.txt for +issues pertaining to Linux USB and USB Scanner support. + +An installed version of SANE which is available from +http://www.mostang.com/sane/. Testing has been performed using +version SANE-1.0.1. For instructions on building and installing SANE, +refer to the various README files within the SANE distribution. + +The latest SANE HP backend is available from http://www.kirchgessner.net. +At the time of this writing, version 0.83 was available. + + +OK, I'VE INSTALLED SANE. SO WHAT DO I DO NOW? + +NOTE: $INSTALL_DIR is the location where SANE is installed. It may be +/usr/local, /usr, /opt or somewhere else. If you don't know, ask your +system administrator. + +1) Make sure that you have the libsane-hp.* libraries under the +$INSTALL_DIR/lib/sane/ directory. If you don't, then the HP backend +was either not compiled or installed properly. + +2) Under the directory $INSTALL_DIR/etc/sane.d/ edit the following +files: dll.conf, hp.conf. + + dll.conf: Make sure that the 'hp' entry is present and uncommented. + + hp.conf: This should contain two lines: + + /dev/usbscanner + option connect-device + +NOTE: If you are using multiple scanners, make sure to have the correct +device, ie /dev/usbscanner0. See scanner.txt for more info. + +3) You should now be able to use SANE (xscanimage or scanimage). + +Don't forget to read any relevant man pages regarding the usage of +SANE. If you have other entries uncommented in 'dll.conf', you may +have to specify the device to (x)scanimage. Again, `man` is your +friend. The xscanimage (1) man page has info on how to get 'The Gimp' +to work with xscanimage. Note that Gimp support must be compiled into +SANE for it to work. If you are dealing with a RedHat system, this +means that you'll also need to install the gimp-devel rpm package +prior to compiling SANE. + +NOTE: The issues regarding core dumping by (x)scanimage have (or seem +to be thus far) been resolved with version 0.2+ of the USB scanner +driver which should be available in linux-2.3.23. If you notice +otherwise, please contact me. + +David /\/elson +dnelson@jump.net +http://www.jump.net/~dnelson diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/scanner.txt linux-2.2.17/Documentation/usb/scanner.txt --- linux-2.2.17-orig/Documentation/usb/scanner.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/scanner.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,362 @@ +Copyright (C) 1999, 2000 David E. Nelson + +April 26, 2000 + +CHANGES + +- Amended for linux-2.3.99-pre6-3 +- Appended hp_scan.c to end of this README +- Removed most references to HP +- Updated uhci/ohci host controller info +- Updated support for multiple scanner support +- Updated supported scanners list +- Updated usbdevfs info +- Spellcheck + +OVERVIEW + +This README addresses issues regarding how to configure the kernel +to access a USB scanner. Although the driver was originally conceived +for USB HP scanners, it's general enough so that it can be used with +other scanners. Also, one can now pass the USB Vendor and Product +ID's using module parameters for unknown scanners. Refer to the +document scanner-hp-sane.txt for guidance on how to configure SANE to +use a USB HP Scanner. + + +ADDITIONAL INFORMATION + +http://www.linux-usb.org/ + + +REQUIREMENTS + +A host with a USB port. Ideally, either a UHCI (Intel) or OHCI +(Compaq and others) hardware port should work. At the time of this +writing, there are two UHCI drivers and one OHCI. + +A Linux development kernel (2.3.x) with USB support enabled or a +backported version to linux-2.2.x. See http://www.linux-usb.org for +more information on accomplishing this. + +A Linux kernel with USB Scanner support enabled. + +'lspci' which is only needed to determine the type of USB hardware +available/installed in your machine. + +CONFIGURATION + +Using `lspci -v`, determine the type of USB hardware available/installed. + + If you see something like: + + USB Controller: ...... + Flags: ..... + I/O ports at .... + + Then you have a UHCI based controller. + + If you see something like: + + USB Controller: ..... + Flags: .... + Memory at ..... + + Then you have a OHCI based controller. + +Using `make menuconfig` or your preferred method for configuring the +kernel, select 'Support for USB', 'OHCI/UHCI' depending on your +hardware (determined from the steps above), 'USB Scanner support', and +'Preliminary USB device filesystem'. Compile and install the modules +(you may need to execute `depmod -a` to update the module +dependencies). If any of the USB sections were compiled into the +kernel, a reboot is necessary. NOTE: Updating the boot disk with +'lilo' may also be required. Testing was performed only as modules, +YMMV. + +Beginning with version 0.4 of the driver, up to 16 scanners can be +connected/used simultaneously. If you intend to use more than +one scanner at a time: + + Add a device for the USB scanner: + `mknod /dev/usbscanner0 c 180 48` + `mknod /dev/usbscanner1 c 180 49` + . + . + `mknod /dev/usb/scanner15 180 63` + + +If you foresee using only one scanner it is best to: + `mknod /dev/usbscanner0 c 180 48` + `ln -s /dev/usbscanner0 /dev/usbscanner` + + +Set appropriate permissions for /dev/usbscanner[0-15] (don't forget +about group and world permissions). Both read and write permissions +are required for proper operation. For example: + `chmod 666 /dev/usbscanner0` + +Load the appropriate modules (if compiled as modules): + + OHCI: + modprobe usb-ohci + modprobe scanner + + UHCI: + modprobe usb-uhci + modprobe scanner + +That's it. SANE should now be able to access the device. + +There is a small test program (hp_scan.c -- appended below) that can +be used to test the scanner device if it's an HP scanner that supports +SCL (Scanner Control Language). Known HP scanner that support SCL are +the 4100, 5200, 6200, the 6300 -- note that the 4200 is *not* +supported since it does not understand SCL; it's also strongly +suspected that the 3300 and the PhotoSmart S20 are not SCL compliant. +Hp_scan.c's purpose is to test the driver without having to +retrieve/configure SANE. Hp_scan.c will scan the entire bed and put +the output into a file called 'out.dat' in the current directory. The +data in the file is raw data so it's not very useful for imaging. + +MESSAGES + +On occassion the message 'usb_control/bulk_msg: timeout' or something +similar will appear in '/var/adm/messages' or on the console or both, +depending on how your system is configured. This is a side effect +that scanners are sometimes very slow at warming up and/or +initialiazing. In most cases, however, only several of these messages +should appear and is generally considered to be normal. If you see +a message of the type 'excessive NAK's received' then this should +be considered abnormal and generally indicates that the USB system is +unable to communicate with the scanner for some particular reason. + +SUPPORTED SCANNERS + +NOTE: Just because a product is listed here does not mean that +applications exist that support the product. It's in the hopes that +this will allow developers a means to produce applications that will +support the listed USB products. + +At the time of this writing, the following scanners were supported by +scanner.c: + + Acer + Prisa Acerscan 620U & 640U (!) + Prisa AcerScan 620U (!) + Agfa + SnapScan 1212U + Another SnapScan 1212U (?) + SnapScan Touch + Colorado -- See Primax/Colorado below + Epson -- See Seiko/Epson below + Genius + ColorPage-Vivid Pro + Hewlett Packard + 3300C + 4100C + 4200C + PhotoSmart S20 + 5200C + 6200C + 6300C + Microtek + ScanMaker X6 - X6U + Phantom 336CX - C3 + Phantom 336CX - C3 #2 + Phantom C6 + ScanMaker V6USL + ScanMaker V6USL #2 + ScanMaker V6UL - SpicyU + Mustek + 1200 CU + Primax/Colorado + G2-300 #1 + G2-600 #1 + G2E-300 #1 + ReadyScan 636i + G2-300 #2 + G2-600 #2 + G2E-300 #2 + G2E-600 + Colorado USB 9600 + Colorado USB 19200 + Colorado 600u + Colorado 1200u + Seiko/Epson Corp. + Perfection 636U and 636Photo + Perfection 610 + Perfection 1200U and 1200Photo + Umax + Astra 1220U + Astra 1236U + Astra 2000U + Astra 2200U + Visioneer + OneTouch 5300 + OneTouch 7600 duplicate ID (!) + 6100 + + +MODULE PARAMETERS + +If you have a device that you wish to experiment with or try using +this driver with, but the Vendor and Product ID's are not coded in, +don't despair. If the driver was compiled as a module, you can pass +options to the driver. Simply add + + options scanner vendor=0x#### product=0x**** + +to the /etc/modules.conf file replacing the #'s and the *'s with the +correct ID's. The ID's can be retrieved from the messages file or +using `cat /proc/bus/usb/devices`. Note that USB /proc support must be +enabled during kernel configuration. If the 'scanner' module is +already loaded into memory, it must be reloaded for the module +parameters to take effect. In essence, `rmmod scanner; modprobe +scanner` must be performed. + +**NOTE**: In later kernels (2.3.38+), a new filesystem was introduced, +usbdevfs. To mount the filesystem, issue the command (as root): + + mount -t usbdevfs /proc/bus/usb /proc/bus/usb + +An alternative and more permanent method would be to add + + none /proc/bus/usb usbdevfs defaults 0 0 + +to /etc/fstab. This will mount usbdevfs at each reboot. You can then +issue `cat /proc/bus/usb/devices` to extract USB device information. + + +BUGS + +Just look at the list of fixes in the source files. So, if you +encounter any problems feel free to drop me an email. + +David /\/elson +dnelson@jump.net +http://www.jump.net/~dnelson + +--------------- snip -- hp_scan.c -- snip --------------- +/* + +This is a really crude attempt at writing a short test program. It's +mostly only to be used to test connectivity with USB HP scanners that +understand SCL. Currently, the supported models are 4100C, 5200C, +6200C, and the 6300C. Note that the 4200C is *NOT* acceptable. + +Copyright (C) David E. Nelson , 1999 + +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. + +*/ + +#include +#include +#include +#include +#include + +/* + Gray Output produces about a 8945400 byte file. + Color Output produces a 26836200 byte file. + + To compile: gcc -o hp_scan hp_scan.c +*/ + +// #define COLOR /* Undef to scan GrayScale */ + +int send_cmd(int, const char *, int); +int read_cmd(int, char *, int); + +int +main(void) { + + ssize_t cnt = 0, total_cnt = 0; + + FILE *fpout; + + int fp; + int data_size = 32768; + + char *data; + + static char reset_cmd[] = {'\x1b','E'}; + +#ifdef COLOR + static char data_type_cmd[] = {'\x1b','*','a','5','T'}; /* Color */ + static char data_width_cmd[] = {'\x1b','*','a','2','4','G'}; /* 24 Bit Color */ +#else + static char data_type_cmd[] = {'\x1b','*','a','4','T'}; /* Gray */ + static char data_width_cmd[] = {'\x1b','*','a','8','G'}; /* 8 Bit Gray */ +#endif + + static char query_cmd[] = {'\x1b', '*', 's', '2', '5', '7', 'E'}; + static char start_scan_cmd[] = {'\x1b','*','f','0','S'}; + + if(!(data=malloc(data_size))) { + perror("malloc failed"); + exit (1); + } + + if((fp=open("/dev/usbscanner", O_RDWR)) < 0) { + perror("Unable to open scanner device"); + exit (1); + } + + if((fpout=fopen("out.dat", "w+")) == NULL) { + perror("Unable to open ouput file"); + exit(1); + } + + send_cmd(fp, reset_cmd, sizeof(reset_cmd)); + send_cmd(fp, data_type_cmd, sizeof(data_type_cmd)); + send_cmd(fp, data_width_cmd, sizeof(data_width_cmd)); + send_cmd(fp, start_scan_cmd, sizeof(start_scan_cmd)); + + while ((cnt = read(fp, data, data_size)) > 0) { + printf("Read: %u\n", cnt); + if(fwrite(data, sizeof(char), cnt, fpout) < 0) { + perror("Write to output file failed"); + exit (1); + } + total_cnt += cnt; + } + if (cnt < 0) { + perror("Read from scanner failed"); + exit (1); + } + + printf("\nRead %lu bytes.\n", total_cnt); + + send_cmd(fp, reset_cmd, sizeof(reset_cmd)); + + close(fp); + fclose(fpout); + return (0); +} + +int +send_cmd(int fp, const char * cmd, int length) { + + int result; + int x; + + if((result = write(fp, cmd, length)) != length) { + printf ("Write warning: %d bytes requested, %d written\n"); + } else if (result < 0) { + perror ("send_cmd failure"); + exit (1); + } + return (result); +} + +int +read_cmd(int fp, char * response, int length) { + + return read(fp, response, length); + +} diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/uhci.txt linux-2.2.17/Documentation/usb/uhci.txt --- linux-2.2.17-orig/Documentation/usb/uhci.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/uhci.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,165 @@ +Specification and Internals for the New UHCI Driver (Whitepaper...) + + brought to you by + + Georg Acher, acher@in.tum.de (executive slave) (base guitar) + Deti Fliegl, deti@fliegl.de (executive slave) (lead voice) + Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader) + + $Id: README.uhci,v 1.1 1999/12/14 14:03:02 fliegl Exp $ + +This document and the new uhci sources can be found on + http://hotswap.in.tum.de/usb + +1. General issues + +1.1 Why a new UHCI driver, we already have one?!? + +Correct, but its internal structure got more and more mixed up by the (still +ongoing) efforts to get isochronous transfers (ISO) to work. +Since there is an increasing need for reliable ISO-transfers (especially +for USB-audio needed by TS and for a DAB-USB-Receiver build by GA and DF), +this state was a bit unsatisfying in our opinion, so we've decided (based +on knowledge and experiences with the old UHCI driver) to start +from scratch with a new approach, much simpler but at the same time more +powerful. +It is inspired by the way Win98/Win2000 handles USB requests via URBs, +but it's definitely 100% free of MS-code and doesn't crash while +unplugging an used ISO-device like Win98 ;-) +Some code for HW setup and root hub management was taken from the +original UHCI driver, but heavily modified to fit into the new code. +The invention of the basic concept, and major coding were completed in two +days (and nights) on the 16th and 17th of October 1999, now known as the +great USB-October-Revolution started by GA, DF, and TS ;-) + +Since the concept is in no way UHCI dependant, we hope that it will also be +transfered to the OHCI-driver, so both drivers share a common API. + +1.2. Advantages and disadvantages + ++ All USB transfer types work now! ++ Asynchronous operation ++ Simple, but powerful interface (only two calls for start and cancel) ++ Easy migration to the new API, simplified by a compatibility API ++ Simple usage of ISO transfers ++ Automatic linking of requests ++ ISO transfers allow variable length for each frame and striping ++ No CPU dependent and non-portable atomic memory access, no asm()-inlines ++ Tested on x86 and Alpha + +- Rewriting for ISO transfers needed + +1.3. Is there some compatibility to the old API? + +Yes, but only for control, bulk and interrupt transfers. We've implemented +some wrapper calls for these transfer types. The usbcore works fine with +these wrappers. For ISO there's no compatibility, because the old ISO-API +and its semantics were unnecessary complicated in our opinion. + +1.4. What's really working? + +As said above, CTRL und BULK already work fine even with the wrappers, +so legacy code wouldn't notice the change. +Regarding to Thomas, ISO transfers now run stable with USB audio. +INT transfers (e.g. mouse driver) work fine, too. + +1.5. Are there any bugs? + +No ;-) +Hm... +Well, of course this implementation needs extensive testing on all available +hardware, but we believe that any fixes shouldn't harm the overall concept. + +1.6. What should be done next? + +A large part of the request handling seems to be identical for UHCI and +OHCI, so it would be a good idea to extract the common parts and have only +the HW specific stuff in uhci.c. Furthermore, all other USB device drivers +should need URBification, if they use isochronous or interrupt transfers. +One thing missing in the current implementation (and the old UHCI driver) +is fair queueing for BULK transfers. Since this would need (in principle) +the alteration of already constructed TD chains (to switch from depth to +breadth execution), another way has to be found. Maybe some simple +heuristics work with the same effect. + +--------------------------------------------------------------------------- + +2. Internal structure and mechanisms + +To get quickly familiar with the internal structures, here's a short +description how the new UHCI driver works. However, the ultimate source of +truth is only uhci.c! + +2.1. Descriptor structure (QHs and TDs) + +During initialization, the following skeleton is allocated in init_skel: + + framespecific | common chain + +framelist[] +[ 0 ]-----> TD --> TD -------\ +[ 1 ]-----> TD --> TD --------> TD ----> QH -------> QH -------> QH ---> NULL + ... TD --> TD -------/ +[1023]-----> TD --> TD ------/ + + ^^ ^^ ^^ ^^ ^^ ^^ + 1024 TDs for 7 TDs for 1 TD for Start of Start of End Chain + ISO INT (2-128ms) 1ms-INT CTRL Chain BULK Chain + +For each CTRL or BULK transfer a new QH is allocated and the containing data +transfers are appended as (vertical) TDs. After building the whole QH with its +dangling TDs, the QH is inserted before the BULK Chain QH (for CTRL) or +before the End Chain QH (for BULK). Since only the QH->next pointers are +affected, no atomic memory operation is required. The three QHs in the +common chain are never equipped with TDs! + +For ISO or INT, the TD for each frame is simply inserted into the apropriate +ISO/INT-TD-chain for the desired frame. The 7 skeleton INT-TDs are scattered +among the 1024 frames similar to the old UHCI driver. + +For CTRL/BULK/ISO, the last TD in the transfer has the IOC-bit set. For INT, +every TD (there is only one...) has the IOC-bit set. + +Besides the data for the UHCI controller (2 or 4 32bit words), the descriptors +are double-linked through the .vertical and .horizontal elements in the +SW data of the descriptor (using the double-linked list structures and +operations), but SW-linking occurs only in closed domains, i.e. for each of +the 1024 ISO-chains and the 8 INT-chains there is a closed cycle. This +simplifies all insertions and unlinking operations and avoids costly +bus_to_virt()-calls. + +2.2. URB structure and linking to QH/TDs + +During assembly of the QH and TDs of the requested action, these descriptors +are stored in urb->urb_list, so the allocated QH/TD descriptors are bound to +this URB. +If the assembly was successful and the descriptors were added to the HW chain, +the corresponding URB is inserted into a global URB list for this controller. +This list stores all pending URBs. + +2.3. Interrupt processing + +Since UHCI provides no means to directly detect completed transactions, the +following is done in each UHCI interrupt (uhci_interrupt()): + +For each URB in the pending queue (process_urb()), the ACTIVE-flag of the +associated TDs are processed (depending on the transfer type +process_{transfer|interrupt|iso}()). If the TDs are not active anymore, +they indicate the completion of the transaction and the status is calculated. +Inactive QH/TDs are removed from the HW chain (since the host controller +already removed the TDs from the QH, no atomic access is needed) and +eventually the URB is marked as completed (OK or errors) and removed from the +pending queue. Then the next linked URB is submitted. After (or immediately +before) that, the completion handler is called. + +2.4. Unlinking URBs + +First, all QH/TDs stored in the URB are unlinked from the HW chain. +To ensure that the host controller really left a vertical TD chain, we +wait for one frame. After that, the TDs are physically destroyed. + +2.5. URB linking and the consequences + +Since URBs can be linked and the corresponding submit_urb is called in +the UHCI-interrupt, all work associated with URB/QH/TD assembly has to be +interrupt save. This forces kmalloc to use GFP_ATOMIC in the interrupt. diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/usb-help.txt linux-2.2.17/Documentation/usb/usb-help.txt --- linux-2.2.17-orig/Documentation/usb/usb-help.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/usb-help.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,16 @@ +usb-help.txt +2000-March-24 + +For USB help other than the readme files that are located in +linux/Documentation/usb/*, see the following: + +Linux-USB project: http://www.linux-usb.org + mirrors at http://www.suse.cz/development/linux-usb/ + and http://usb.in.tum.de/linux-usb/ +Linux USB Guide: http://linuxusbguide.sourceforge.net +Linux-USB device overview (working devices and drivers): + http://www.qbik.ch/usb/devices/ + +The Linux-USB mailing list is linux-usb@suse.com . + +### diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Documentation/usb/usb-serial.txt linux-2.2.17/Documentation/usb/usb-serial.txt --- linux-2.2.17-orig/Documentation/usb/usb-serial.txt Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/Documentation/usb/usb-serial.txt Sun Sep 24 04:26:21 2000 @@ -0,0 +1,164 @@ +INTRODUCTION + + The USB serial driver currently supports a number of different USB to + serial converter products, as well as some devices that use a serial + interface from userspace to talk to the device. + + See the individual product section below for specific information about + the different devices. + + +CONFIGURATION + + Currently the driver can handle up to 256 different serial interfaces at + one time. + + If you are not using devfs: + The major number that the driver uses is 188 so to use the driver, + create the following nodes: + mknod /dev/ttyUSB0 c 188 0 + mknod /dev/ttyUSB1 c 188 1 + mknod /dev/ttyUSB2 c 188 2 + mknod /dev/ttyUSB3 c 188 3 + . + . + . + mknod /dev/ttyUSB254 c 188 254 + mknod /dev/ttyUSB255 c 188 255 + + If you are using devfs: + The devices supported by this driver will show up as + /dev/usb/tts/{0,1,...} + + When the device is connected and recognized by the driver, the driver + will print to the system log, which node(s) the device has been bound + to. + + +SPECIFIC DEVICES SUPPORTED + + +ConnectTech WhiteHEAT 4 port converter + + ConnectTech has been very forthcoming with information about their + device, including providing a unit to test with. This driver will end up + being fully supported. + +Current status: + The device's firmware is downloaded on connection, the new firmware + runs properly and all four ports are successfuly recognized and connected. + Now data flow needs to be implemented properly. + This driver is not fully operational. + + +HandSpring Visor USB docking station + +Current status: + Only when the Visor tries to connect to the host, does the docking + station show up as a valid USB device. When this happens, the device is + properly enumerated, assigned a port, and then communication _should_ be + possible. The driver cleans up properly when the device is removed, or + the connection is canceled on the Visor. + + NOTE: + This means that in order to talk to the Visor, the sync button must be + pressed BEFORE trying to get any program to communicate to the Visor. + This goes against the current documentation for pilot-xfer and other + packages, but is the only way that it will work due to the hardware + in the Visor. + + When the device is connected, try talking to it on the second port + (this is usually /dev/ttyUSB1 if you do not have any other usb-serial + devices in the system.) The system log should tell you which port is + the port to use for the HotSync transfer. The "Generic" port can be used + for other device communication, such as a PPP link. + + There is a webpage and mailing lists for this portion of the driver at: + http://usbvisor.sourceforge.net/ + + +Keyspan PDA Serial Adapter + + Single port DB-9 serial adapter, pushed as a PDA adapter for iMacs (mostly + sold in Macintosh catalogs, comes in a translucent white/green dongle). + Fairly simple device. Firmware is homebrew. + +Current status: + Things that work: + basic input/output (tested with 'cu') + blocking write when serial line can't keep up + changing baud rates (up to 115200) + getting/setting modem control pins (TIOCM{GET,SET,BIS,BIC}) + sending break (although duration looks suspect) + Things that don't: + device strings (as logged by kernel) have trailing binary garbage + device ID isn't right, might collide with other Keyspan products + changing baud rates ought to flush tx/rx to avoid mangled half characters + Big Things on the todo list: + parity, 7 vs 8 bits per char, 1 or 2 stop bits + HW flow control + not all of the standard USB descriptors are handled: Get_Status, Set_Feature + O_NONBLOCK, select() + + +FTDI Single Port Serial Driver + + This is a single port DB-25 serial adapter. More information about this + device and the Linux driver can be found at: + http://reality.sgi.com/bryder_wellington/ftdi_sio/ + + +ZyXEL omni.net lcd plus ISDN TA + + This is an ISDN TA. Please report both successes and troubles to the + author at omninet@kroah.com + + +Digi AccelePort Driver + + This driver supports the Digi AccelePort USB 4 device, a 4 port + USB serial converter. The driver does NOT yet support the Digi + AccelePort USB 2 or 8. + + The driver supports open, close, read, write, termios settings (baud + rate, word size, parity, stop bits, hardware/software flow control, + CREAD), DTR/RTS, and TIOCMGET/SET/BIS/BIC ioctls. It has not been + thoroughly tested, but it seems to be working reasonable well. There + is more work to do, including flow control, ioctls, and support for + the Digi AccelePort USB 2 and 8. + + Please contact Peter Berger (pberger@brimson.com) or Al Borchers + (alborchers@steinerpoint.com) for questions or problems with this + driver. + + +Generic Serial driver + + If your device is not one of the above listed devices, compatible with + the above models, you can try out the "generic" interface. This + interface does not provide any type of control messages sent to the + device, and does not support any kind of device flow control. All that + is required of your device is that it has at least one bulk in endpoint, + or one bulk out endpoint. + + To enable the generic driver to recognize your device, build the driver + as a module and load it by the following invocation: + insmod usb-serial vendor=0x#### product=0x#### + where the #### is replaced with the hex representation of your device's + vendor id and product id. + + This driver has been successfully used to connect to the NetChip USB + development board, providing a way to develop USB firmware without + having to write a custom driver. + + +CONTACT: + + If anyone has any problems using this driver, with any of the above + specified products, please contact me, or join the Linux-USB mailing + list (information on joining the mailing list, as well as a link to its + searchable archive is at http://www.linux-usb.org/ ) + + +Greg Kroah-Hartman +greg@kroah.com diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/Makefile linux-2.2.17/Makefile --- linux-2.2.17-orig/Makefile Mon Sep 4 10:47:54 2000 +++ linux-2.2.17/Makefile Sun Sep 24 04:26:21 2000 @@ -191,7 +191,7 @@ endif ifeq ($(CONFIG_USB),y) -DRIVERS := $(DRIVERS) drivers/usb/usb.a +DRIVERS := $(DRIVERS) drivers/usb/usbdrv.o endif ifeq ($(CONFIG_I2O),y) @@ -348,6 +348,8 @@ if [ -f VIDEO_MODULES ]; then inst_mod VIDEO_MODULES video; fi; \ if [ -f FC4_MODULES ]; then inst_mod FC4_MODULES fc4; fi; \ if [ -f IRDA_MODULES ]; then inst_mod IRDA_MODULES net; fi; \ + if [ -f USB_MODULES ]; then inst_mod USB_MODULES usb; fi; \ + if [ -f USB_SERIAL_MODULES ]; then inst_mod USB_SERIAL_MODULES usb; fi; \ if [ -f SK98LIN_MODULES ]; then inst_mod SK98LIN_MODULES net; fi; \ \ for f in *.o; do [ -r $$f ] && echo $$f; done | sort > $$MODLIB/.allmods; \ diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/arch/alpha/config.in linux-2.2.17/arch/alpha/config.in --- linux-2.2.17-orig/arch/alpha/config.in Mon Sep 4 10:39:16 2000 +++ linux-2.2.17/arch/alpha/config.in Sun Sep 24 04:26:21 2000 @@ -268,7 +268,7 @@ endmenu source drivers/char/Config.in - +source drivers/usb/Config.in source fs/Config.in if [ "$CONFIG_VT" = "y" ]; then diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/arch/i386/config.in linux-2.2.17/arch/i386/config.in --- linux-2.2.17-orig/arch/i386/config.in Mon Sep 4 10:39:16 2000 +++ linux-2.2.17/arch/i386/config.in Sun Sep 24 04:26:21 2000 @@ -176,7 +176,7 @@ source drivers/char/Config.in -# source drivers/usb/Config.in +source drivers/usb/Config.in source fs/Config.in diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/char/Config.in linux-2.2.17/drivers/char/Config.in --- linux-2.2.17-orig/drivers/char/Config.in Wed Jun 7 14:26:42 2000 +++ linux-2.2.17/drivers/char/Config.in Sun Sep 24 04:26:21 2000 @@ -160,13 +160,13 @@ dep_tristate 'Colour QuickCam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV $CONFIG_PARPORT fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - dep_tristate 'CPiA Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV + dep_tristate 'CPiA Video For Linux' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV if [ "$CONFIG_VIDEO_CPIA" != "n" ]; then - if [ "$CONFIG_PARPORT" != "n" ]; then - dep_tristate 'CPiA Parallel Port Lowlevel Support (EXPERIMENTAL)' CONFIG_VIDEO_CPIA_PP $CONFIG_VIDEO_CPIA $CONFIG_PARPORT - if [ "$CONFIG_VIDEO_CPIA_PP" != "n" ]; then - bool 'CPiA Parallel Port DMA Support (EXPERIMENTAL)' CONFIG_VIDEO_CPIA_PP_DMA + if [ "CONFIG_PARPORT_1284" != "n" ]; then + dep_tristate ' CPiA Parallel Port Lowlevel Support' CONFIG_VIDEO_CPIA_PP $CONFIG_VIDEO_CPIA $CONFIG_PARPORT fi + if [ "$CONFIG_USB" != "n" ]; then + dep_tristate ' CPiA USB Lowlevel Support' CONFIG_VIDEO_CPIA_USB $CONFIG_VIDEO_CPIA $CONFIG_USB fi fi fi diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/char/Makefile linux-2.2.17/drivers/char/Makefile --- linux-2.2.17-orig/drivers/char/Makefile Mon Sep 4 10:39:17 2000 +++ linux-2.2.17/drivers/char/Makefile Sun Sep 24 04:26:21 2000 @@ -499,6 +499,14 @@ endif endif +ifeq ($(CONFIG_VIDEO_CPIA_USB),y) +L_OBJS += cpia_usb.o +else + ifeq ($(CONFIG_VIDEO_CPIA_USB),m) + M_OBJS += cpia_usb.o + endif +endif + ifeq ($(CONFIG_RADIO_AZTECH),y) L_OBJS += radio-aztech.o else diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/char/cpia.c linux-2.2.17/drivers/char/cpia.c --- linux-2.2.17-orig/drivers/char/cpia.c Wed Jun 7 14:26:42 2000 +++ linux-2.2.17/drivers/char/cpia.c Sun Sep 24 04:26:21 2000 @@ -3,9 +3,9 @@ * * Supports CPiA based Video Camera's. * - * (C) 1999 Peter Pregler, - * Scott J. Bertin, - * Johannes Erdfelt + * (C) Copyright 1999-2000 Peter Pregler, + * (C) Copyright 1999-2000 Scott J. Bertin, + * (C) Copyright 1999-2000 Johannes Erdfelt, jerdfelt@valinux.com * * 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 @@ -22,28 +22,28 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* #define _CPIA_DEBUG_ define for verbose debug output */ #include #include +#include #include -#include #include #include -#include #include -#include +#include #include #include -#include +#include #include #include +#include #ifdef CONFIG_KMOD #include #endif -#undef _CPIA_DEBUG_ /* define for verbose debug output */ -#include +#include "cpia.h" #ifdef CONFIG_VIDEO_CPIA_PP extern int cpia_pp_init(void); @@ -52,8 +52,12 @@ extern int cpia_usb_init(void); #endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) +#define init_MUTEX(x) (*(x) = MUTEX) +#endif + #ifdef MODULE -MODULE_AUTHOR("Scott J. Bertin & Peter Pregler "); +MODULE_AUTHOR("Scott J. Bertin & Peter Pregler & Johannes Erdfelt "); MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras"); MODULE_SUPPORTED_DEVICE("video"); #endif @@ -97,6 +101,7 @@ #define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13) #define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1) +#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2) #define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3) #define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4) #define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6) @@ -127,6 +132,7 @@ #define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12) #define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13) #define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14) +#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15) #define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1) #define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4) @@ -135,117 +141,7 @@ #define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8) #define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9) #define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10) - -struct cam_params { - struct { - u8 firmwareVersion; - u8 firmwareRevision; - u8 vcVersion; - u8 vcRevision; - } version; - struct { - u16 vendor; - u16 product; - u16 deviceRevision; - } pnpID; - struct { - u8 vpVersion; - u8 vpRevision; - u16 cameraHeadID; - } vpVersion; - struct { - u8 systemState; - u8 grabState; - u8 streamState; - u8 fatalError; - u8 cmdError; - u8 debugFlags; - u8 vpStatus; - u8 errorCode; - } status; - struct { - u8 brightness; - u8 contrast; - u8 saturation; - } colourParams; - struct { - u8 gainMode; - u8 expMode; - u8 compMode; - u8 centreWeight; - u8 gain; - u8 fineExp; - u8 coarseExpLo; - u8 coarseExpHi; - u8 redComp; - u8 green1Comp; - u8 green2Comp; - u8 blueComp; - } exposure; - struct { - u8 balanceMode; - u8 redGain; - u8 greenGain; - u8 blueGain; - } colourBalance; - struct { - u8 divisor; - u8 baserate; - } sensorFps; - struct { - u8 gain1; - u8 gain2; - u8 gain4; - u8 gain8; - } apcor; - struct { - u8 flickerMode; - u8 coarseJump; - u8 allowableOverExposure; - } flickerControl; - struct { - u8 gain1; - u8 gain2; - u8 gain4; - u8 gain8; - } vlOffset; - struct { - u8 mode; - u8 decimation; - } compression; - struct { - u8 frTargeting; - u8 targetFR; - u8 targetQ; - } compressionTarget; - struct { - u8 yThreshold; - u8 uvThreshold; - } yuvThreshold; - struct { - u8 hysteresis; - u8 threshMax; - u8 smallStep; - u8 largeStep; - u8 decimationHysteresis; - u8 frDiffStepThresh; - u8 qDiffStepThresh; - u8 decimationThreshMod; - } compressionParams; - struct { - u8 videoSize; /* CIF/QCIF */ - u8 subSample; - u8 yuvOrder; - } format; - struct { - u8 colStart; /* skip first 8*colStart pixels */ - u8 colEnd; /* finish at 8*colEnd pixels */ - u8 rowStart; /* skip first 4*rowStart lines */ - u8 rowEnd; /* finish at 4*rowEnd lines */ - } roi; - u8 ecpTiming; - u8 streamStartLine; -}; +#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11) enum { FRAME_READY, /* Ready to grab into */ @@ -254,24 +150,6 @@ FRAME_UNUSED, /* Unused (no MCAPTURE) */ }; -#define FRAME_NUM 2 /* double buffering for now */ -struct cpia_frame { - u8 *data; - int count; - int width; - int height; - volatile int state; -}; - -enum v4l_camstates { - CPIA_V4L_IDLE = 0, - CPIA_V4L_ERROR, - CPIA_V4L_COMMAND, - CPIA_V4L_GRABBING, - CPIA_V4L_STREAMING, - CPIA_V4L_STREAMING_PAUSED, -}; - #define COMMAND_NONE 0x0000 #define COMMAND_SETCOMPRESSION 0x0001 #define COMMAND_SETCOMPRESSIONTARGET 0x0002 @@ -288,45 +166,7 @@ #define COMMAND_SETAPCOR 0x1000 #define COMMAND_SETFLICKERCTRL 0x2000 #define COMMAND_SETVLOFFSET 0x4000 - -struct cam_data { - int index; /* which camera is this */ - struct semaphore busy_lock; /* guard against SMP multithreading */ - struct cpia_camera_ops *ops; /* lowlevel driver operations */ - void *lowlevel_data; /* private data for lowlevel driver */ - u8 *raw_image; /* buffer for raw image data */ - struct cpia_frame decompressed_frame; - /* buffer to hold decompressed frame */ - int image_size; /* sizeof last decompressed image */ - int open_count; /* # of process that have camera open */ - /* camera status */ - int fps; /* actual fps reported by the camera */ - int transfer_rate; /* transfer rate from camera in kB/s */ - u8 mainsFreq; /* for flicker control */ - - /* proc interface */ - struct semaphore param_lock; /* params lock for this camera */ - struct cam_params params; /* camera settings */ - struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */ - - /* v4l */ - int video_size; /* VIDEO_SIZE_ */ - volatile enum v4l_camstates camstate; /* v4l layer status */ - struct video_device vdev; /* v4l videodev */ - struct video_picture vp; /* v4l camera settings */ - struct video_window vw; /* v4l capture area */ - - /* mmap interface */ - int curframe; /* the current frame to grab into */ - u8 *frame_buf; /* frame buffer data */ - struct cpia_frame frame[FRAME_NUM]; - /* FRAME_NUM-buffering, so we need a array */ - - int first_frame; - volatile u32 cmd_queue; /* queued commands */ -}; - -static struct cam_data *camera[CPIA_MAXCAMS] ={ [0 ... CPIA_MAXCAMS-1] = NULL }; +#define COMMAND_SETLIGHTS 0x8000 /* GA 04/14/00 */ /* Developer's Guide Table 5 p 3-34 * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/ @@ -338,6 +178,7 @@ /* forward declaration of local function */ static void reset_camera_struct(struct cam_data *cam); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) /********************************************************************** * * Memory management @@ -357,64 +198,134 @@ pte_t *ptep, pte; pgd = pgd_offset(current->mm, adr); - if (pgd_none(*pgd)) return 0; + if (pgd_none(*pgd)) + return 0; pmd = pmd_offset(pgd, adr); - if (pmd_none(*pmd)) return 0; + if (pmd_none(*pmd)) + return 0; ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/); pte = *ptep; - if(pte_present(pte)) - return - virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1)))); + if (pte_present(pte)) + return virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1)))); return 0; } -static inline unsigned long kvirt_to_phys(unsigned long adr) +static inline unsigned long kvirt_to_pa(unsigned long adr) { return uvirt_to_phys(VMALLOC_VMADDR(adr)); } -static void * rvmalloc(unsigned long size) +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */ + +/********************************************************************** + * + * Memory management + * + * This is a shameless copy from the USB-cpia driver (linux kernel + * version 2.3.29 or so, I have no idea what this code actually does ;). + * Actually it seems to be a copy of a shameless copy of the bttv-driver. + * Or that is a copy of a shameless copy of ... (To the powers: is there + * no generic kernel-function to do this sort of stuff?) + * + * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says + * there will be one, but apparentely not yet - jerdfelt + * + **********************************************************************/ + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) { - void * mem; + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if (pte_present(pte)) + ret = page_address(pte_page(pte)) | + (adr & (PAGE_SIZE-1)); + } + } + return ret; +} + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + return ret; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */ + +static void *rvmalloc(unsigned long size) +{ + void *mem; unsigned long adr, page; + /* Round it off to PAGE_SIZE */ size += (PAGE_SIZE - 1); size &= ~(PAGE_SIZE - 1); - mem=vmalloc(size); - if (mem) { - /* Clear the ram out, no junk to the user */ - memset(mem, 0, size); - adr=(unsigned long) mem; + mem = vmalloc(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) { - page = kvirt_to_phys(adr); + page = kvirt_to_pa(adr); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) mem_map_reserve(MAP_NR(phys_to_virt(page))); - adr+=PAGE_SIZE; - if (size > PAGE_SIZE) size-=PAGE_SIZE; - else size=0; - } +#else + mem_map_reserve(MAP_NR(__va(page))); +#endif + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; } + return mem; } -static void rvfree(void * mem, unsigned long size) +static void rvfree(void *mem, unsigned long size) { unsigned long adr, page; + if (!mem) + return; + size += (PAGE_SIZE - 1); size &= ~(PAGE_SIZE - 1); - if (mem) { - adr=(unsigned long) mem; + adr = (unsigned long) mem; while (size > 0) { - page = kvirt_to_phys(adr); + page = kvirt_to_pa(adr); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) mem_map_unreserve(MAP_NR(phys_to_virt(page))); - adr+=PAGE_SIZE; - if (size > PAGE_SIZE) size-=PAGE_SIZE; - else size=0; +#else + mem_map_unreserve(MAP_NR(__va(page))); +#endif + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; } vfree(mem); - } } /********************************************************************** @@ -422,6 +333,7 @@ * /proc interface * **********************************************************************/ +#ifdef CONFIG_PROC_FS static struct proc_dir_entry *cpia_proc_root=NULL; static int cpia_read_proc(char *page, char **start, off_t off, @@ -432,7 +344,7 @@ struct cam_data *cam = data; char tmpstr[20]; - /* IMPORTANT: This output MUST be kept under 4k (FIXME: PAGE_SIZE?) + /* IMPORTANT: This output MUST be kept under PAGE_SIZE * or we need to get more sophisticated. */ out += sprintf(out, "read-only\n-----------------------\n"); @@ -467,22 +379,23 @@ cam->params.status.vpStatus); out += sprintf(out, "error_code: %#04x\n", cam->params.status.errorCode); + /* GA 04/14/00 - QX3 specific entries */ + if (cam->params.qx3.qx3_detected) { + out += sprintf(out, "button: %4d\n", + cam->params.qx3.button); + out += sprintf(out, "cradled: %4d\n", + cam->params.qx3.cradled); + } out += sprintf(out, "video_size: %s\n", cam->params.format.videoSize == VIDEOSIZE_CIF ? "CIF " : "QCIF"); - out += sprintf(out, "sub_sample: %s\n", - cam->params.format.subSample == SUBSAMPLE_420 ? - "420" : "422"); - out += sprintf(out, "yuv_order: %s\n", - cam->params.format.yuvOrder == YUVORDER_YUYV ? - "YUYV" : "UYVY"); out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n", cam->params.roi.colStart*8, cam->params.roi.rowStart*4, cam->params.roi.colEnd*8, cam->params.roi.rowEnd*4); - out += sprintf(out, "actual_fps: %d\n", cam->fps); - out += sprintf(out, "transfer_rate: %dkB/s\n", + out += sprintf(out, "actual_fps: %3d\n", cam->fps); + out += sprintf(out, "transfer_rate: %4dkB/s\n", cam->transfer_rate); out += sprintf(out, "\nread-write\n"); @@ -490,13 +403,13 @@ " max default comment\n"); out += sprintf(out, "brightness: %8d %8d %8d %8d\n", cam->params.colourParams.brightness, 0, 100, 50); - if(cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) { + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) /* 1-02 firmware limits contrast to 80 */ tmp = 80; - } else { + else tmp = 96; - } + out += sprintf(out, "contrast: %8d %8d %8d %8d" " steps of 8\n", cam->params.colourParams.contrast, 0, tmp, 48); @@ -510,21 +423,20 @@ 2*cam->params.streamStartLine, 0, cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144, cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120); + out += sprintf(out, "sub_sample: %8s %8s %8s %8s\n", + cam->params.format.subSample == SUBSAMPLE_420 ? + "420" : "422", "420", "422", "422"); + out += sprintf(out, "yuv_order: %8s %8s %8s %8s\n", + cam->params.format.yuvOrder == YUVORDER_YUYV ? + "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV"); out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n", cam->params.ecpTiming ? "slow" : "normal", "slow", "normal", "normal"); - switch(cam->params.colourBalance.balanceMode) { - case 1: - // FIXME case 3: - sprintf(tmpstr, "manual"); - break; - case 2: + if (cam->params.colourBalance.balanceModeIsAuto) { sprintf(tmpstr, "auto"); - break; - default: - sprintf(tmpstr, "unknown"); - break; + } else { + sprintf(tmpstr, "manual"); } out += sprintf(out, "color_balance_mode: %8s %8s %8s" " %8s\n", tmpstr, "manual", "auto", "auto"); @@ -535,20 +447,20 @@ out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n", cam->params.colourBalance.blueGain, 0, 212, 92); - if(cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) { + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) /* 1-02 firmware limits gain to 2 */ sprintf(tmpstr, "%8d %8d", 1, 2); - } else { + else sprintf(tmpstr, "%8d %8d", 1, 8); - } - if(cam->params.exposure.gainMode == 0) { + + if (cam->params.exposure.gainMode == 0) out += sprintf(out, "max_gain: unknown %18s" " %8d powers of 2\n", tmpstr, 2); - } else { + else out += sprintf(out, "max_gain: %8d %18s %8d powers of 2\n", 1<<(cam->params.exposure.gainMode-1), tmpstr, 2); - } + switch(cam->params.exposure.expMode) { case 1: case 3: @@ -568,22 +480,22 @@ "off", "on", "on"); out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n", 1<params.exposure.gain, 1, 1); - if(cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) { + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) /* 1-02 firmware limits fineExp to 127 */ tmp = 255; - } else { + else tmp = 511; - } + out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n", cam->params.exposure.fineExp*2, 0, tmp, 0); - if(cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) { + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) /* 1-02 firmware limits coarseExpHi to 0 */ tmp = 255; - } else { + else tmp = 65535; - } + out += sprintf(out, "coarse_exp: %8d %8d %8d" " %8d\n", cam->params.exposure.coarseExpLo+ 256*cam->params.exposure.coarseExpHi, 0, tmp, 185); @@ -637,9 +549,9 @@ break; } out += sprintf(out, " none,auto,manual auto\n"); - out += sprintf(out, "decimation: %8s %8s %8s %8s\n", + out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n", cam->params.compression.decimation == - DECIMATION_ENAB ? "on":"off", "off", "off", + DECIMATION_ENAB ? "on":"off", "off", "on", "off"); out += sprintf(out, "compression_target: %9s %9s %9s %9s\n", cam->params.compressionTarget.frTargeting == @@ -647,13 +559,13 @@ "framerate":"quality", "framerate", "quality", "quality"); out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n", - cam->params.compressionTarget.targetFR, 0, 30, 7); + cam->params.compressionTarget.targetFR, 1, 30, 15); out += sprintf(out, "target_quality: %8d %8d %8d %8d\n", - cam->params.compressionTarget.targetQ, 0, 255, 10); + cam->params.compressionTarget.targetQ, 1, 64, 5); out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n", - cam->params.yuvThreshold.yThreshold, 0, 31, 15); + cam->params.yuvThreshold.yThreshold, 0, 31, 6); out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n", - cam->params.yuvThreshold.uvThreshold, 0, 31, 15); + cam->params.yuvThreshold.uvThreshold, 0, 31, 6); out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n", cam->params.compressionParams.hysteresis, 0, 255, 3); out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n", @@ -675,14 +587,24 @@ cam->params.compressionParams.decimationThreshMod, 0, 255, 2); + /* GA 04/14/00 - QX3 specific entries */ + if (cam->params.qx3.qx3_detected) { + out += sprintf(out, "toplight: %8s %8s %8s %8s\n", + cam->params.qx3.toplight ? "on" : "off", + "off", "on", "off"); + out += sprintf(out, "bottomlight: %8s %8s %8s %8s\n", + cam->params.qx3.bottomlight ? "on" : "off", + "off", "on", "off"); + } + len = out - page; len -= off; if (len < count) { *eof = 1; if (len <= 0) return 0; - } else { + } else len = count; - } + *start = page + off; return len; } @@ -692,16 +614,14 @@ { struct cam_data *cam = data; struct cam_params new_params; - int retval, len, colon_found; + int retval, find_colon; int size = count; - char *p; unsigned long val; u32 command_flags = 0; u8 new_mains; - if(down_interruptible(&cam->param_lock)) { + if (down_interruptible(&cam->param_lock)) return -ERESTARTSYS; - } /* * Skip over leading whitespace @@ -714,106 +634,112 @@ memcpy(&new_params, &cam->params, sizeof(struct cam_params)); new_mains = cam->mainsFreq; -#define MATCH(x) (len=strlen(x), len <= count && strncmp(buffer, x, len) == 0) -#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \ - new_params.version.firmwareRevision == (y)) -#define VALUE \ - simple_strtoul(buffer, &p, 0); \ - if(p == buffer) { \ - retval = -EINVAL; \ - } else { \ - count -= p-buffer; \ - buffer = p; \ - } -#define FIND_VALUE \ - buffer += len;\ - count -= len; \ - colon_found=0; \ - while(count && (*buffer == ' ' || *buffer == '\t' || \ - (!colon_found && *buffer == ':'))) { \ - if(*buffer == ':') colon_found = 1; \ +#define MATCH(x) \ + ({ \ + int _len = strlen(x), _ret, _colon_found; \ + _ret = (_len <= count && strncmp(buffer, x, _len) == 0); \ + if (_ret) { \ + buffer += _len; \ + count -= _len; \ + if (find_colon) { \ + _colon_found = 0; \ + while (count && (*buffer == ' ' || *buffer == '\t' || \ + (!_colon_found && *buffer == ':'))) { \ + if (*buffer == ':') \ + _colon_found = 1; \ --count; \ ++buffer; \ } \ - if(!count || !colon_found) { \ + if (!count || !_colon_found) \ retval = -EINVAL; \ - } -#define FIND_END \ - if(retval == 0) { \ - while(count && isspace(*buffer) && *buffer != '\n'){ \ - --count; \ - ++buffer; \ + find_colon = 0; \ } \ - if(count) { \ - if(*buffer != '\n' && *buffer != ';') { \ - retval = -EINVAL; \ - } else { \ - --count; \ - ++buffer; \ } \ + _ret; \ + }) +#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \ + new_params.version.firmwareRevision == (y)) +#define VALUE \ + ({ \ + char *_p; \ + unsigned long int _ret; \ + _ret = simple_strtoul(buffer, &_p, 0); \ + if (_p == buffer) \ + retval = -EINVAL; \ + else { \ + count -= _p - buffer; \ + buffer = _p; \ } \ - } + _ret; \ + }) + retval = 0; - while(count && !retval) { - if(MATCH("brightness")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 100) - new_params.colourParams.brightness=val; - else retval = -EINVAL; + while (count && !retval) { + find_colon = 1; + if (MATCH("brightness")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) + new_params.colourParams.brightness = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOLOURPARAMS; - FIND_END - } else if(MATCH("contrast")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 100) { + } else if (MATCH("contrast")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) { /* contrast is in steps of 8, so round*/ val = ((val + 3) / 8) * 8; /* 1-02 firmware limits contrast to 80*/ - if(FIRMWARE_VERSION(1,2) && val > 80) + if (FIRMWARE_VERSION(1,2) && val > 80) val = 80; + new_params.colourParams.contrast = val; - } else retval = -EINVAL; + } else + retval = -EINVAL; } command_flags |= COMMAND_SETCOLOURPARAMS; - FIND_END - } else if(MATCH("saturation")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 100) - new_params.colourParams.saturation=val; - else retval = -EINVAL; + } else if (MATCH("saturation")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) + new_params.colourParams.saturation = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOLOURPARAMS; - FIND_END - } else if(MATCH("sensor_fps")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("sensor_fps")) { + if (!retval) + val = VALUE; + + if (!retval) { /* find values so that sensorFPS is minimized, * but >= val */ - if(val > 30) { + if (val > 30) retval = -EINVAL; - } else if(val > 25) { + else if (val > 25) { new_params.sensorFps.divisor = 0; new_params.sensorFps.baserate = 1; - } else if(val > 15) { + } else if (val > 15) { new_params.sensorFps.divisor = 0; new_params.sensorFps.baserate = 0; - } else if(val > 12) { + } else if (val > 12) { new_params.sensorFps.divisor = 1; new_params.sensorFps.baserate = 1; - } else if(val > 7) { + } else if (val > 7) { new_params.sensorFps.divisor = 1; new_params.sensorFps.baserate = 0; - } else if(val > 6) { + } else if (val > 6) { new_params.sensorFps.divisor = 2; new_params.sensorFps.baserate = 1; - } else if(val > 3) { + } else if (val > 3) { new_params.sensorFps.divisor = 2; new_params.sensorFps.baserate = 0; } else { @@ -825,89 +751,100 @@ flicker_jumps[new_mains] [new_params.sensorFps.baserate] [new_params.sensorFps.divisor]; - if(new_params.flickerControl.flickerMode) + if (new_params.flickerControl.flickerMode) command_flags |= COMMAND_SETFLICKERCTRL; } command_flags |= COMMAND_SETSENSORFPS; - FIND_END - } else if(MATCH("stream_start_line")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("stream_start_line")) { + if (!retval) + val = VALUE; + + if (!retval) { int max_line = 288; - if(new_params.format.videoSize==VIDEOSIZE_QCIF) + + if (new_params.format.videoSize == VIDEOSIZE_QCIF) max_line = 144; - if(val <= max_line) + if (val <= max_line) new_params.streamStartLine = val/2; - else retval = -EINVAL; + else + retval = -EINVAL; } - FIND_END - } else if(MATCH("ecp_timing")) { - FIND_VALUE - if(!retval && MATCH("normal")) { - buffer += len; - count -= len; + } else if (MATCH("sub_sample")) { + if (!retval && MATCH("420")) + new_params.format.subSample = SUBSAMPLE_420; + else if (!retval && MATCH("422")) + new_params.format.subSample = SUBSAMPLE_422; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETFORMAT; + } else if (MATCH("yuv_order")) { + if (!retval && MATCH("YUYV")) + new_params.format.yuvOrder = YUVORDER_YUYV; + else if (!retval && MATCH("UYVY")) + new_params.format.yuvOrder = YUVORDER_UYVY; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETFORMAT; + } else if (MATCH("ecp_timing")) { + if (!retval && MATCH("normal")) new_params.ecpTiming = 0; - } else if(!retval && MATCH("slow")) { - buffer += len; - count -= len; + else if (!retval && MATCH("slow")) new_params.ecpTiming = 1; - } else { + else retval = -EINVAL; - } + command_flags |= COMMAND_SETECPTIMING; - FIND_END - } else if(MATCH("color_balance_mode")) { - FIND_VALUE - if(!retval && MATCH("manual")) { - buffer += len; - count -= len; - new_params.colourBalance.balanceMode=1; - } else if(!retval && MATCH("auto")) { - buffer += len; - count -= len; - new_params.colourBalance.balanceMode=2; - } else { + } else if (MATCH("color_balance_mode")) { + if (!retval && MATCH("manual")) + new_params.colourBalance.balanceModeIsAuto = 0; + else if (!retval && MATCH("auto")) + new_params.colourBalance.balanceModeIsAuto = 1; + else retval = -EINVAL; - } + command_flags |= COMMAND_SETCOLOURBALANCE; - FIND_END - } else if(MATCH("red_gain")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 212) + } else if (MATCH("red_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) new_params.colourBalance.redGain = val; - else retval = -EINVAL; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOLOURBALANCE; - FIND_END - } else if(MATCH("green_gain")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 212) - new_params.colourBalance.greenGain=val; - else retval = -EINVAL; + } else if (MATCH("green_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) + new_params.colourBalance.greenGain = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOLOURBALANCE; - FIND_END - } else if(MATCH("blue_gain")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 212) + } else if (MATCH("blue_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) new_params.colourBalance.blueGain = val; - else retval = -EINVAL; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOLOURBALANCE; - FIND_END - } else if(MATCH("max_gain")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("max_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { /* 1-02 firmware limits gain to 2 */ - if(FIRMWARE_VERSION(1,2) && val > 2) + if (FIRMWARE_VERSION(1,2) && val > 2) val = 2; switch(val) { case 1: @@ -928,44 +865,32 @@ } } command_flags |= COMMAND_SETEXPOSURE; - FIND_END - } else if(MATCH("exposure_mode")) { - FIND_VALUE - if(!retval && MATCH("auto")) { - buffer += len; - count -= len; + } else if (MATCH("exposure_mode")) { + if (!retval && MATCH("auto")) new_params.exposure.expMode = 2; - } else if(!retval && MATCH("manual")) { - buffer += len; - count -= len; - if(new_params.exposure.expMode == 2) + else if (!retval && MATCH("manual")) { + if (new_params.exposure.expMode == 2) new_params.exposure.expMode = 3; new_params.flickerControl.flickerMode = 0; command_flags |= COMMAND_SETFLICKERCTRL; - } else { + } else retval = -EINVAL; - } + command_flags |= COMMAND_SETEXPOSURE; - FIND_END - } else if(MATCH("centre_weight")) { - FIND_VALUE - if(!retval && MATCH("on")) { - buffer += len; - count -= len; + } else if (MATCH("centre_weight")) { + if (!retval && MATCH("on")) new_params.exposure.centreWeight = 1; - } else if(!retval && MATCH("off")) { - buffer += len; - count -= len; + else if (!retval && MATCH("off")) new_params.exposure.centreWeight = 2; - } else { + else retval = -EINVAL; - } + command_flags |= COMMAND_SETEXPOSURE; - FIND_END - } else if(MATCH("gain")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("gain")) { + if (!retval) + val = VALUE; + + if (!retval) { switch(val) { case 1: new_params.exposure.gain = 0; @@ -996,35 +921,36 @@ break; } command_flags |= COMMAND_SETEXPOSURE; - if(new_params.exposure.gain > + if (new_params.exposure.gain > new_params.exposure.gainMode-1) retval = -EINVAL; } - FIND_END - } else if(MATCH("fine_exp")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val < 256) { + } else if (MATCH("fine_exp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 256) { /* 1-02 firmware limits fineExp to 127*/ - if(FIRMWARE_VERSION(1,2) && val > 127) + if (FIRMWARE_VERSION(1,2) && val > 127) val = 127; new_params.exposure.fineExp = val; new_params.exposure.expMode = 1; command_flags |= COMMAND_SETEXPOSURE; new_params.flickerControl.flickerMode = 0; command_flags |= COMMAND_SETFLICKERCTRL; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("coarse_exp")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val < 65536) { + } else if (MATCH("coarse_exp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 65536) { /* 1-02 firmware limits * coarseExp to 255 */ - if(FIRMWARE_VERSION(1,2) && val > 255) + if (FIRMWARE_VERSION(1,2) && val > 255) val = 255; new_params.exposure.coarseExpLo = val & 0xff; @@ -1034,353 +960,398 @@ command_flags |= COMMAND_SETEXPOSURE; new_params.flickerControl.flickerMode = 0; command_flags |= COMMAND_SETFLICKERCTRL; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("red_comp")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val >= 220 && val <= 255) { + } else if (MATCH("red_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= 220 && val <= 255) { new_params.exposure.redComp = val; command_flags |= COMMAND_SETEXPOSURE; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("green1_comp")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val >= 214 && val <= 255) { + } else if (MATCH("green1_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= 214 && val <= 255) { new_params.exposure.green1Comp = val; command_flags |= COMMAND_SETEXPOSURE; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("green2_comp")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val >= 214 && val <= 255) { + } else if (MATCH("green2_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= 214 && val <= 255) { new_params.exposure.green2Comp = val; command_flags |= COMMAND_SETEXPOSURE; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("blue_comp")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val >= 230 && val <= 255) { + } else if (MATCH("blue_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= 230 && val <= 255) { new_params.exposure.blueComp = val; command_flags |= COMMAND_SETEXPOSURE; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("apcor_gain1")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("apcor_gain1")) { + if (!retval) + val = VALUE; + + if (!retval) { command_flags |= COMMAND_SETAPCOR; - if(val <= 0xff) new_params.apcor.gain1 = val; - else retval = -EINVAL; + if (val <= 0xff) + new_params.apcor.gain1 = val; + else + retval = -EINVAL; } - FIND_END - } else if(MATCH("apcor_gain2")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("apcor_gain2")) { + if (!retval) + val = VALUE; + + if (!retval) { command_flags |= COMMAND_SETAPCOR; - if(val <= 0xff) new_params.apcor.gain2 = val; - else retval = -EINVAL; + if (val <= 0xff) + new_params.apcor.gain2 = val; + else + retval = -EINVAL; } - FIND_END - } else if(MATCH("apcor_gain4")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("apcor_gain4")) { + if (!retval) + val = VALUE; + + if (!retval) { command_flags |= COMMAND_SETAPCOR; - if(val <= 0xff) new_params.apcor.gain4 = val; - else retval = -EINVAL; + if (val <= 0xff) + new_params.apcor.gain4 = val; + else + retval = -EINVAL; } - FIND_END - } else if(MATCH("apcor_gain8")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("apcor_gain8")) { + if (!retval) + val = VALUE; + + if (!retval) { command_flags |= COMMAND_SETAPCOR; - if(val <= 0xff) new_params.apcor.gain8 = val; - else retval = -EINVAL; + if (val <= 0xff) + new_params.apcor.gain8 = val; + else + retval = -EINVAL; } - FIND_END - } else if(MATCH("vl_offset_gain1")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 0xff) new_params.vlOffset.gain1 = val; - else retval = -EINVAL; + } else if (MATCH("vl_offset_gain1")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain1 = val; + else + retval = -EINVAL; } - FIND_END command_flags |= COMMAND_SETVLOFFSET; - } else if(MATCH("vl_offset_gain2")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 0xff) new_params.vlOffset.gain2 = val; - else retval = -EINVAL; + } else if (MATCH("vl_offset_gain2")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain2 = val; + else + retval = -EINVAL; } - FIND_END command_flags |= COMMAND_SETVLOFFSET; - } else if(MATCH("vl_offset_gain4")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 0xff) new_params.vlOffset.gain4 = val; - else retval = -EINVAL; + } else if (MATCH("vl_offset_gain4")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain4 = val; + else + retval = -EINVAL; } - FIND_END command_flags |= COMMAND_SETVLOFFSET; - } else if(MATCH("vl_offset_gain8")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 0xff) new_params.vlOffset.gain8 = val; - else retval = -EINVAL; + } else if (MATCH("vl_offset_gain8")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain8 = val; + else + retval = -EINVAL; } - FIND_END command_flags |= COMMAND_SETVLOFFSET; - } else if(MATCH("flicker_control")) { - FIND_VALUE - if(!retval && MATCH("on")) { - buffer += len; - count -= len; + } else if (MATCH("flicker_control")) { + if (!retval && MATCH("on")) { new_params.flickerControl.flickerMode = 1; new_params.exposure.expMode = 2; command_flags |= COMMAND_SETEXPOSURE; - } else if(!retval && MATCH("off")) { - buffer += len; - count -= len; + } else if (!retval && MATCH("off")) new_params.flickerControl.flickerMode = 0; - } else { + else retval = -EINVAL; - } + command_flags |= COMMAND_SETFLICKERCTRL; - FIND_END - } else if(MATCH("mains_frequency")) { - FIND_VALUE - if(!retval && MATCH("50")) { - buffer += len; - count -= len; + } else if (MATCH("mains_frequency")) { + if (!retval && MATCH("50")) { new_mains = 0; new_params.flickerControl.coarseJump = flicker_jumps[new_mains] [new_params.sensorFps.baserate] [new_params.sensorFps.divisor]; - if(new_params.flickerControl.flickerMode) + if (new_params.flickerControl.flickerMode) command_flags |= COMMAND_SETFLICKERCTRL; - } else if(!retval && MATCH("60")) { - buffer += len; - count -= len; + } else if (!retval && MATCH("60")) { new_mains = 1; new_params.flickerControl.coarseJump = flicker_jumps[new_mains] [new_params.sensorFps.baserate] [new_params.sensorFps.divisor]; - if(new_params.flickerControl.flickerMode) + if (new_params.flickerControl.flickerMode) command_flags |= COMMAND_SETFLICKERCTRL; - } else { + } else retval = -EINVAL; - } - FIND_END - } else if(MATCH("allowable_overexposure")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 0xff) { + } else if (MATCH("allowable_overexposure")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) { new_params.flickerControl. allowableOverExposure = val; command_flags |= COMMAND_SETFLICKERCTRL; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("compression_mode")) { - FIND_VALUE - if(!retval && MATCH("none")) { - buffer += len; - count -= len; + } else if (MATCH("compression_mode")) { + if (!retval && MATCH("none")) new_params.compression.mode = CPIA_COMPRESSION_NONE; - } else if(!retval && MATCH("auto")) { - buffer += len; - count -= len; + else if (!retval && MATCH("auto")) new_params.compression.mode = CPIA_COMPRESSION_AUTO; - } else if(!retval && MATCH("manual")) { - buffer += len; - count -= len; + else if (!retval && MATCH("manual")) new_params.compression.mode = CPIA_COMPRESSION_MANUAL; - } else { + else retval = -EINVAL; - } + command_flags |= COMMAND_SETCOMPRESSION; - FIND_END - } else if(MATCH("decimation")) { - FIND_VALUE - if(!retval && MATCH("off")) { - buffer += len; - count -= len; + } else if (MATCH("decimation_enable")) { + if (!retval && MATCH("off")) new_params.compression.decimation = 0; - } else { + else if (!retval && MATCH("on")) + new_params.compression.decimation = 1; + else retval = -EINVAL; - } + command_flags |= COMMAND_SETCOMPRESSION; - FIND_END - } else if(MATCH("compression_target")) { - FIND_VALUE - if(!retval && MATCH("quality")) { - buffer += len; - count -= len; + } else if (MATCH("compression_target")) { + if (!retval && MATCH("quality")) new_params.compressionTarget.frTargeting = CPIA_COMPRESSION_TARGET_QUALITY; - } else if(!retval && MATCH("framerate")) { - buffer += len; - count -= len; + else if (!retval && MATCH("framerate")) new_params.compressionTarget.frTargeting = CPIA_COMPRESSION_TARGET_FRAMERATE; - } else { + else retval = -EINVAL; - } + command_flags |= COMMAND_SETCOMPRESSIONTARGET; - FIND_END - } else if(MATCH("target_framerate")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) + } else if (MATCH("target_framerate")) { + if (!retval) + val = VALUE; + + if (!retval) { + if(val > 0 && val <= 30) new_params.compressionTarget.targetFR = val; + else + retval = -EINVAL; + } command_flags |= COMMAND_SETCOMPRESSIONTARGET; - FIND_END - } else if(MATCH("target_quality")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) new_params.compressionTarget.targetQ = val; + } else if (MATCH("target_quality")) { + if (!retval) + val = VALUE; + + if (!retval) { + if(val > 0 && val <= 64) + new_params.compressionTarget.targetQ = val; + else + retval = -EINVAL; + } command_flags |= COMMAND_SETCOMPRESSIONTARGET; - FIND_END - } else if(MATCH("y_threshold")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val < 32) - new_params.yuvThreshold.yThreshold=val; - else retval = -EINVAL; + } else if (MATCH("y_threshold")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 32) + new_params.yuvThreshold.yThreshold = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETYUVTHRESH; - FIND_END - } else if(MATCH("uv_threshold")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val < 32) - new_params.yuvThreshold.uvThreshold=val; - else retval = -EINVAL; + } else if (MATCH("uv_threshold")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 32) + new_params.yuvThreshold.uvThreshold = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETYUVTHRESH; - FIND_END - } else if(MATCH("hysteresis")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams.hysteresis=val; - else retval = -EINVAL; + } else if (MATCH("hysteresis")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.hysteresis = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("threshold_max")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams.threshMax=val; - else retval = -EINVAL; + } else if (MATCH("threshold_max")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.threshMax = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("small_step")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams.smallStep=val; - else retval = -EINVAL; + } else if (MATCH("small_step")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.smallStep = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("large_step")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams.largeStep=val; - else retval = -EINVAL; + } else if (MATCH("large_step")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.largeStep = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("decimation_hysteresis")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams. - decimationHysteresis = val; - else retval = -EINVAL; + } else if (MATCH("decimation_hysteresis")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.decimationHysteresis = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("fr_diff_step_thresh")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams. - frDiffStepThresh = val; - else retval = -EINVAL; + } else if (MATCH("fr_diff_step_thresh")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.frDiffStepThresh = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("q_diff_step_thresh")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams. - qDiffStepThresh = val; - else retval = -EINVAL; - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("decimation_thresh_mod")) { + } else if (MATCH("q_diff_step_thresh")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.qDiffStepThresh = val; + else + retval = -EINVAL; } - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams. - decimationThreshMod = val; - else retval = -EINVAL; + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("decimation_thresh_mod")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.decimationThreshMod = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END + } else if (MATCH("toplight")) { /* GA 4/14/00 */ + if (!retval && MATCH("on")) + new_params.qx3.toplight = 1; + + else if (!retval && MATCH("off")) + new_params.qx3.toplight = 0; + + else + retval = -EINVAL; + command_flags |= COMMAND_SETLIGHTS; + + } else if (MATCH("bottomlight")) { /* GA 4/14/00 */ + if (!retval && MATCH("on")) + new_params.qx3.bottomlight = 1; + + else if (!retval && MATCH("off")) + new_params.qx3.bottomlight = 0; + + else + retval = -EINVAL; + command_flags |= COMMAND_SETLIGHTS; + } else { + DBG("No match found\n"); retval = -EINVAL; } + + if (!retval) { + while (count && isspace(*buffer) && *buffer != '\n') { + --count; + ++buffer; + } + if (count) { + if (*buffer != '\n' && *buffer != ';') + retval = -EINVAL; + else { + --count; + ++buffer; + } + } + } } #undef MATCH #undef FIRMWARE_VERSION #undef VALUE #undef FIND_VALUE #undef FIND_END - if(retval == 0) { - if(command_flags & COMMAND_SETCOLOURPARAMS) { + if (!retval) { + if (command_flags & COMMAND_SETCOLOURPARAMS) { /* Adjust cam->vp to reflect these changes */ cam->vp.brightness = new_params.colourParams.brightness*65535/100; @@ -1394,9 +1365,8 @@ cam->mainsFreq = new_mains; cam->cmd_queue |= command_flags; retval = size; - } else { + } else DBG("error: %d\n", retval); - } up(&cam->param_lock); @@ -1408,17 +1378,21 @@ char name[7]; struct proc_dir_entry *ent; - if(cpia_proc_root == NULL || cam == NULL) { + if (!cpia_proc_root || !cam) return; - } + sprintf(name, "video%d", cam->vdev.minor); ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root); - if (!ent) return; + if (!ent) + return; + ent->data = cam; ent->read_proc = cpia_read_proc; ent->write_proc = cpia_write_proc; - ent->size = 3623; + ent->size = 3736; + if (cam->params.qx3.qx3_detected) + ent->size += 188; cam->proc_entry = ent; } @@ -1426,16 +1400,43 @@ { char name[7]; - if(cam == NULL || cam->proc_entry == NULL) return; + if (!cam || !cam->proc_entry) + return; sprintf(name, "video%d", cam->vdev.minor); remove_proc_entry(name, cpia_proc_root); cam->proc_entry = NULL; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) +/* + * This is called as the fill_inode function when an inode + * is going into (fill = 1) or out of service (fill = 0). + * We use it here to manage the module use counts. + */ +static void proc_cpia_modcount(struct inode *inode, int fill) +{ +#ifdef MODULE + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +#endif +} +#endif + static void proc_cpia_create(void) { cpia_proc_root = create_proc_entry("cpia", S_IFDIR, 0); + + if (cpia_proc_root) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) + cpia_proc_root->fill_inode = &proc_cpia_modcount; +#else + cpia_proc_root->owner = THIS_MODULE; +#endif + } else + LOG("Unable to initialise /proc/cpia\n"); } #ifdef MODULE @@ -1444,6 +1445,7 @@ remove_proc_entry("cpia", 0); } #endif +#endif /* CONFIG_PROC_FS */ /* ----------------------- debug functions ---------------------- */ @@ -1473,39 +1475,41 @@ { /* return the best match, where 'best' is as always * the largest that is not bigger than what is requested. */ - if(width>=352 && height>=288) { + if (width>=352 && height>=288) return VIDEOSIZE_352_288; /* CIF */ - } - if(width>=320 && height>=240) { + + if (width>=320 && height>=240) return VIDEOSIZE_320_240; /* SIF */ - } - if(width>=288 && height>=216) { + + if (width>=288 && height>=216) return VIDEOSIZE_288_216; - } - if(width>=256 && height>=192) { + + if (width>=256 && height>=192) return VIDEOSIZE_256_192; - } - if(width>=224 && height>=168) { + + if (width>=224 && height>=168) return VIDEOSIZE_224_168; - } - if(width>=192 && height>=144) { + + if (width>=192 && height>=144) return VIDEOSIZE_192_144; - } - if(width>=176 && height>=144) { + + if (width>=176 && height>=144) return VIDEOSIZE_176_144; /* QCIF */ - } - if(width>=160 && height>=120) { + + if (width>=160 && height>=120) return VIDEOSIZE_160_120; /* QSIF */ - } - if(width>=128 && height>=96) { + + if (width>=128 && height>=96) return VIDEOSIZE_128_96; - } - if(width>=64 && height>=48) { + + if (width>=88 && height>=72) + return VIDEOSIZE_88_72; + + if (width>=64 && height>=48) return VIDEOSIZE_64_48; - } - if(width>=48 && height>=48) { + + if (width>=48 && height>=48) return VIDEOSIZE_48_48; - } return -1; } @@ -1608,6 +1612,16 @@ cam->params.roi.rowEnd=30; cam->params.streamStartLine = 60; break; + case VIDEOSIZE_88_72: + cam->vw.width = 88; + cam->vw.height = 72; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=5; + cam->params.roi.colEnd=16; + cam->params.roi.rowStart=9; + cam->params.roi.rowEnd=27; + cam->params.streamStartLine = 60; + break; case VIDEOSIZE_64_48: cam->vw.width = 64; cam->vw.height = 48; @@ -1635,27 +1649,29 @@ return; } -static int allocate_frame_buf(struct cam_data *cam) { +static int allocate_frame_buf(struct cam_data *cam) +{ int i; - cam->frame_buf = rvmalloc(FRAME_NUM*CPIA_MAX_FRAME_SIZE); - if (!cam->frame_buf) { + cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE); + if (!cam->frame_buf) return -ENOBUFS; - } - for( i=0; iframe[i].data = cam->frame_buf + i*CPIA_MAX_FRAME_SIZE; - } + + for (i = 0; i < FRAME_NUM; i++) + cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE; + return 0; } -static int free_frame_buf(struct cam_data *cam) { +static int free_frame_buf(struct cam_data *cam) +{ int i; rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE); - cam->frame_buf=0; - for( i=0; iframe_buf = 0; + for (i=0; i < FRAME_NUM; i++) cam->frame[i].data = NULL; - } + return 0; } @@ -1663,9 +1679,9 @@ static void inline free_frames(struct cpia_frame frame[FRAME_NUM]) { int i; - for( i=0; iparam_lock); datasize=8; break; + case CPIA_COMMAND_ReadMCPorts: /* GA 4/14/00 */ + case CPIA_COMMAND_ReadVCRegs: + datasize = 4; + break; default: datasize=0; break; @@ -1708,8 +1728,8 @@ cmd[7] = 0; retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if(retval) { - LOG("%x - failed\n", command); + if (retval) { + DBG("%x - failed, retval=%d\n", command, retval); if (command == CPIA_COMMAND_GetColourParams || command == CPIA_COMMAND_GetColourBalance || command == CPIA_COMMAND_GetExposure) @@ -1765,8 +1785,47 @@ cam->params.exposure.green1Comp = data[5]; cam->params.exposure.green2Comp = data[6]; cam->params.exposure.blueComp = data[7]; + /* If the *Comp parameters are wacko, generate + * a warning, and reset them back to default + * values. - rich@annexia.org + */ + if (cam->params.exposure.redComp < 220 || + cam->params.exposure.redComp > 255 || + cam->params.exposure.green1Comp < 214 || + cam->params.exposure.green1Comp > 255 || + cam->params.exposure.green2Comp < 214 || + cam->params.exposure.green2Comp > 255 || + cam->params.exposure.blueComp < 230 || + cam->params.exposure.blueComp > 255) + { + printk (KERN_WARNING "*_comp parameters have gone AWOL (%d/%d/%d/%d) - reseting them\n", + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + cam->params.exposure.redComp = 220; + cam->params.exposure.green1Comp = 214; + cam->params.exposure.green2Comp = 214; + cam->params.exposure.blueComp = 230; + } up(&cam->param_lock); break; + + case CPIA_COMMAND_ReadMCPorts: /* GA 04/14/00 */ + if (!cam->params.qx3.qx3_detected) break; + + /* test button press */ + cam->params.qx3.button = ((data[1] & 0x02) == 0); + if (cam->params.qx3.button) { + /* button pressed - unlock the latch */ + do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0); + do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0); + } + + /* test whether microscope is cradled */ + cam->params.qx3.cradled = ((data[2] & 0x40) == 0); + break; + default: break; } @@ -1782,6 +1841,7 @@ { int retval; u8 cmd[8], data[8]; + cmd[0] = command>>8; cmd[1] = command&0xff; cmd[2] = a; @@ -1800,9 +1860,9 @@ data[7] = l; retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if(retval) { + if (retval) LOG("%x - failed\n", command); - } + return retval; } @@ -1813,16 +1873,173 @@ **********************************************************************/ #define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) +static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt, + int linesize, int mmap_kludge) +{ + int y, u, v, r, g, b, y1; + + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + r = ((*(rgb+1-linesize)) & 0x7c) << 1; + g = ((*(rgb-linesize)) & 0xe0) >> 4 | + ((*(rgb+1-linesize)) & 0x03) << 6; + b = ((*(rgb-linesize)) & 0x1f) << 3; + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u - 53294 * v; + b = 132278 * u; + break; + case VIDEO_PALETTE_RGB565: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + r = (*(rgb+1-linesize)) & 0xf8; + g = ((*(rgb-linesize)) & 0xe0) >> 3 | + ((*(rgb+1-linesize)) & 0x07) << 5; + b = ((*(rgb-linesize)) & 0x1f) << 3; + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u - 53294 * v; + b = 132278 * u; + break; + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + if (mmap_kludge) { + r = *(rgb+2-linesize); + g = *(rgb+1-linesize); + b = *(rgb-linesize); + } else { + r = *(rgb-linesize); + g = *(rgb+1-linesize); + b = *(rgb+2-linesize); + } + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + break; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + y = *yuv++; + u = *(rgb+1-linesize); + y1 = *yuv; + v = *(rgb+3-linesize); + /* Just to avoid compiler warnings */ + r = 0; + g = 0; + b = 0; + break; + case VIDEO_PALETTE_UYVY: + u = *(rgb-linesize); + y = *yuv++; + v = *(rgb+2-linesize); + y1 = *yuv; + /* Just to avoid compiler warnings */ + r = 0; + g = 0; + b = 0; + break; + case VIDEO_PALETTE_GREY: + default: + y = *yuv++; + y1 = *yuv; + /* Just to avoid compiler warnings */ + u = 0; + v = 0; + r = 0; + g = 0; + b = 0; + break; + } + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); + *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); + *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); + *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); + return 4; + case VIDEO_PALETTE_RGB565: + *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); + *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); + *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); + *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); + return 4; + case VIDEO_PALETTE_RGB24: + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + return 6; + case VIDEO_PALETTE_RGB32: + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + rgb++; + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + rgb++; + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + return 8; + case VIDEO_PALETTE_GREY: + *rgb++ = y; + *rgb = y1; + return 2; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + *rgb++ = y; + *rgb++ = u; + *rgb++ = y1; + *rgb = v; + return 4; + case VIDEO_PALETTE_UYVY: + *rgb++ = u; + *rgb++ = y; + *rgb++ = v; + *rgb = y1; + return 4; + default: + DBG("Empty: %d\n", out_fmt); + return 0; + } +} + static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt, - int in_uyvy) + int in_uyvy, int mmap_kludge) { int y, u, v, r, g, b, y1; + switch(out_fmt) { case VIDEO_PALETTE_RGB555: case VIDEO_PALETTE_RGB565: case VIDEO_PALETTE_RGB24: case VIDEO_PALETTE_RGB32: - if(in_uyvy) { + if (in_uyvy) { u = *yuv++ - 128; y = (*yuv++ - 16) * 76310; v = *yuv++ - 128; @@ -1862,14 +2079,24 @@ *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); return 4; case VIDEO_PALETTE_RGB24: + if (mmap_kludge) { *rgb++ = LIMIT(b+y); *rgb++ = LIMIT(g+y); *rgb++ = LIMIT(r+y); *rgb++ = LIMIT(b+y1); *rgb++ = LIMIT(g+y1); *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } return 6; case VIDEO_PALETTE_RGB32: + if (mmap_kludge) { *rgb++ = LIMIT(b+y); *rgb++ = LIMIT(g+y); *rgb++ = LIMIT(r+y); @@ -1877,6 +2104,15 @@ *rgb++ = LIMIT(b+y1); *rgb++ = LIMIT(g+y1); *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + rgb++; + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } return 8; case VIDEO_PALETTE_GREY: *rgb++ = y; @@ -1896,11 +2132,13 @@ *rgb = y1; return 4; default: + DBG("Empty: %d\n", out_fmt); return 0; } } -static int skipcount(int count, int fmt) { +static int skipcount(int count, int fmt) +{ switch(fmt) { case VIDEO_PALETTE_GREY: return count; @@ -1922,7 +2160,8 @@ static int parse_picture(struct cam_data *cam, int size) { u8 *obuf, *ibuf, *end_obuf; - int ll, in_uyvy, compressed, origsize, out_fmt; + int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt; + int rows, cols, linesize, subsample_422; /* make sure params don't change while we are decoding */ down(&cam->param_lock); @@ -1933,34 +2172,33 @@ origsize = size; out_fmt = cam->vp.palette; - if((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) { + if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) { LOG("header not found\n"); up(&cam->param_lock); return -1; } - if((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { + if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { LOG("wrong video size\n"); up(&cam->param_lock); return -1; } - if(ibuf[17] != SUBSAMPLE_422) { + if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) { LOG("illegal subtype %d\n",ibuf[17]); up(&cam->param_lock); return -1; } + subsample_422 = ibuf[17] == SUBSAMPLE_422; - if(ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { + if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { LOG("illegal yuvorder %d\n",ibuf[18]); up(&cam->param_lock); return -1; } in_uyvy = ibuf[18] == YUVORDER_UYVY; -#if 0 - /* FIXME: ROI mismatch occurs when switching capture sizes */ - if((ibuf[24] != cam->params.roi.colStart) || + if ((ibuf[24] != cam->params.roi.colStart) || (ibuf[25] != cam->params.roi.colEnd) || (ibuf[26] != cam->params.roi.rowStart) || (ibuf[27] != cam->params.roi.rowEnd)) { @@ -1968,20 +2206,22 @@ up(&cam->param_lock); return -1; } -#endif + cols = 8*(ibuf[25] - ibuf[24]); + rows = 4*(ibuf[27] - ibuf[26]); - if((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { + if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { LOG("illegal compression %d\n",ibuf[28]); up(&cam->param_lock); return -1; } compressed = (ibuf[28] == COMPRESSED); - if(ibuf[29] != NO_DECIMATION) { - LOG("decimation not supported\n"); + if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) { + LOG("illegal decimation %d\n",ibuf[29]); up(&cam->param_lock); return -1; } + decimation = (ibuf[29] == DECIMATION_ENAB); cam->params.yuvThreshold.yThreshold = ibuf[30]; cam->params.yuvThreshold.uvThreshold = ibuf[31]; @@ -1994,98 +2234,135 @@ cam->params.status.vpStatus = ibuf[38]; cam->params.status.errorCode = ibuf[39]; cam->fps = ibuf[41]; + up(&cam->param_lock); - ibuf += 64; - size -= 64; + linesize = skipcount(cols, out_fmt); + ibuf += FRAME_HEADER_SIZE; + size -= FRAME_HEADER_SIZE; ll = ibuf[0] | (ibuf[1] << 8); ibuf += 2; - while(size > 0) { + even_line = 1; + + while (size > 0) { size -= (ll+2); - if(size < 0) { + if (size < 0) { LOG("Insufficient data in buffer\n"); - up(&cam->param_lock); return -1; } - while(ll > 1) { - if(!compressed || (compressed && !(*ibuf & 1))) { - obuf += yuvconvert(ibuf,obuf,out_fmt,in_uyvy); + + while (ll > 1) { + if (!compressed || (compressed && !(*ibuf & 1))) { + if(subsample_422 || even_line) { + obuf += yuvconvert(ibuf, obuf, out_fmt, + in_uyvy, cam->mmap_kludge); ibuf += 4; ll -= 4; } else { + /* SUBSAMPLE_420 on an odd line */ + obuf += convert420(ibuf, obuf, + out_fmt, linesize, + cam->mmap_kludge); + ibuf += 2; + ll -= 2; + } + } else { /*skip compressed interval from previous frame*/ - int skipsize = skipcount(*ibuf >> 1, out_fmt); - obuf += skipsize; - if( obuf > end_obuf ) { - LOG("Insufficient data in buffer\n"); - up(&cam->param_lock); + obuf += skipcount(*ibuf >> 1, out_fmt); + if (obuf > end_obuf) { + LOG("Insufficient buffer size\n"); return -1; } ++ibuf; ll--; } } - if(ll == 1) { - if(*ibuf != EOL) { + if (ll == 1) { + if (*ibuf != EOL) { LOG("EOL not found giving up after %d/%d" " bytes\n", origsize-size, origsize); - up(&cam->param_lock); return -1; } - ibuf++; /* skip over EOL */ + ++ibuf; /* skip over EOL */ - if((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) && + if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) && (ibuf[2] == EOI) && (ibuf[3] == EOI)) { size -= 4; break; } - if(size > 1) { + if(decimation) { + /* skip the odd lines for now */ + obuf += linesize; + } + + if (size > 1) { ll = ibuf[0] | (ibuf[1] << 8); ibuf += 2; /* skip over line length */ } + + if(!decimation) + even_line = !even_line; } else { LOG("line length was not 1 but %d after %d/%d bytes\n", ll, origsize-size, origsize); - up(&cam->param_lock); return -1; } } - cam->decompressed_frame.count = obuf-cam->decompressed_frame.data; + if(decimation) { + /* interpolate odd rows */ + int i, j; + u8 *prev, *next; + prev = cam->decompressed_frame.data; + obuf = prev+linesize; + next = obuf+linesize; + for(i=1; iparam_lock); + cam->decompressed_frame.count = obuf-cam->decompressed_frame.data; return cam->decompressed_frame.count; } /* InitStreamCap wrapper to select correct start line */ -static inline int init_stream_cap(struct cam_data *cam) { +static inline int init_stream_cap(struct cam_data *cam) +{ return do_command(cam, CPIA_COMMAND_InitStreamCap, 0, cam->params.streamStartLine, 0, 0); } /* update various camera modes and settings */ -static void dispatch_commands(struct cam_data *cam ) { +static void dispatch_commands(struct cam_data *cam) +{ down(&cam->param_lock); - if( cam->cmd_queue==COMMAND_NONE ) { + if (cam->cmd_queue==COMMAND_NONE) { up(&cam->param_lock); return; } DEB_BYTE(cam->cmd_queue); DEB_BYTE(cam->cmd_queue>>8); - if( cam->cmd_queue & COMMAND_SETCOLOURPARAMS ) { + if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS) do_command(cam, CPIA_COMMAND_SetColourParams, cam->params.colourParams.brightness, cam->params.colourParams.contrast, cam->params.colourParams.saturation, 0); - } - if( cam->cmd_queue & COMMAND_SETCOMPRESSION ) { + + if (cam->cmd_queue & COMMAND_SETCOMPRESSION) do_command(cam, CPIA_COMMAND_SetCompression, cam->params.compression.mode, cam->params.compression.decimation, 0, 0); - } - if( cam->cmd_queue & COMMAND_SETFORMAT ) { + + if (cam->cmd_queue & COMMAND_SETFORMAT) { do_command(cam, CPIA_COMMAND_SetFormat, cam->params.format.videoSize, cam->params.format.subSample, @@ -2095,22 +2372,23 @@ cam->params.roi.rowStart, cam->params.roi.rowEnd); cam->first_frame = 1; } - if( cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET ) { + + if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET) do_command(cam, CPIA_COMMAND_SetCompressionTarget, cam->params.compressionTarget.frTargeting, cam->params.compressionTarget.targetFR, cam->params.compressionTarget.targetQ, 0); - } - if( cam->cmd_queue & COMMAND_SETYUVTHRESH ) { + + if (cam->cmd_queue & COMMAND_SETYUVTHRESH) do_command(cam, CPIA_COMMAND_SetYUVThresh, cam->params.yuvThreshold.yThreshold, cam->params.yuvThreshold.uvThreshold, 0, 0); - } - if( cam->cmd_queue & COMMAND_SETECPTIMING ) { + + if (cam->cmd_queue & COMMAND_SETECPTIMING) do_command(cam, CPIA_COMMAND_SetECPTiming, cam->params.ecpTiming, 0, 0, 0); - } - if( cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS ) { + + if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS) do_command_extended(cam, CPIA_COMMAND_SetCompressionParams, 0, 0, 0, 0, cam->params.compressionParams.hysteresis, @@ -2121,8 +2399,8 @@ cam->params.compressionParams.frDiffStepThresh, cam->params.compressionParams.qDiffStepThresh, cam->params.compressionParams.decimationThreshMod); - } - if( cam->cmd_queue & COMMAND_SETEXPOSURE ) { + + if (cam->cmd_queue & COMMAND_SETEXPOSURE) do_command_extended(cam, CPIA_COMMAND_SetExposure, cam->params.exposure.gainMode, cam->params.exposure.expMode, @@ -2136,176 +2414,222 @@ cam->params.exposure.green1Comp, cam->params.exposure.green2Comp, cam->params.exposure.blueComp); - } - if( cam->cmd_queue & COMMAND_SETCOLOURBALANCE ) { + + if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) { + if (cam->params.colourBalance.balanceModeIsAuto) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 2, 0, 0, 0); + } else { do_command(cam, CPIA_COMMAND_SetColourBalance, - cam->params.colourBalance.balanceMode, + 1, cam->params.colourBalance.redGain, cam->params.colourBalance.greenGain, cam->params.colourBalance.blueGain); + do_command(cam, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); } - if( cam->cmd_queue & COMMAND_SETSENSORFPS ) { + } + + if (cam->cmd_queue & COMMAND_SETSENSORFPS) do_command(cam, CPIA_COMMAND_SetSensorFPS, cam->params.sensorFps.divisor, cam->params.sensorFps.baserate, 0, 0); - } - if( cam->cmd_queue & COMMAND_SETAPCOR ) { + + if (cam->cmd_queue & COMMAND_SETAPCOR) do_command(cam, CPIA_COMMAND_SetApcor, cam->params.apcor.gain1, cam->params.apcor.gain2, cam->params.apcor.gain4, cam->params.apcor.gain8); - } - if( cam->cmd_queue & COMMAND_SETFLICKERCTRL ) { + + if (cam->cmd_queue & COMMAND_SETFLICKERCTRL) do_command(cam, CPIA_COMMAND_SetFlickerCtrl, cam->params.flickerControl.flickerMode, cam->params.flickerControl.coarseJump, cam->params.flickerControl.allowableOverExposure, 0); - } - if( cam->cmd_queue & COMMAND_SETVLOFFSET ) { + + if (cam->cmd_queue & COMMAND_SETVLOFFSET) do_command(cam, CPIA_COMMAND_SetVLOffset, cam->params.vlOffset.gain1, cam->params.vlOffset.gain2, cam->params.vlOffset.gain4, cam->params.vlOffset.gain8); - } - if( cam->cmd_queue & COMMAND_PAUSE ) { + + if (cam->cmd_queue & COMMAND_PAUSE) do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); + + if (cam->cmd_queue & COMMAND_RESUME) + init_stream_cap(cam); + + /* GA 04/14/00 */ + if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected) + { + int p1 = (cam->params.qx3.bottomlight == 0) << 1; + int p2 = (cam->params.qx3.toplight == 0) << 3; + do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0); + do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0); } - if( cam->cmd_queue & COMMAND_RESUME ) { - init_stream_cap( cam ); - } + up(&cam->param_lock); - cam->cmd_queue=COMMAND_NONE; + cam->cmd_queue = COMMAND_NONE; return; } /* kernel thread function to read image from camera */ static void fetch_frame(void *data) { - int image_size; + int image_size, retry; struct cam_data *cam = (struct cam_data *)data; unsigned long oldjif, rate, diff; + /* Allow up to two bad images in a row to be read and + * ignored before an error is reported */ + for (retry = 0; retry < 3; ++retry) { + if (retry) + DBG("retry=%d\n", retry); + + if (!cam->ops) + continue; + /* load first frame always uncompressed */ - if( cam->first_frame && - cam->params.compression.mode != CPIA_COMPRESSION_NONE ) { + if (cam->first_frame && + cam->params.compression.mode != CPIA_COMPRESSION_NONE) do_command(cam, CPIA_COMMAND_SetCompression, CPIA_COMPRESSION_NONE, NO_DECIMATION, 0, 0); - } /* init camera upload */ - if(do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_CONTINUOUS, - 0, 0, 0)) goto end; - if(do_command(cam, CPIA_COMMAND_GrabFrame, 0, - cam->params.streamStartLine, 0, 0)) goto end; + if (do_command(cam, CPIA_COMMAND_SetGrabMode, + CPIA_GRAB_CONTINUOUS, 0, 0, 0)) + continue; + + if (do_command(cam, CPIA_COMMAND_GrabFrame, 0, + cam->params.streamStartLine, 0, 0)) + continue; + if (cam->ops->wait_for_stream_ready) { /* loop until image ready */ - do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); - while(cam->params.status.streamState != STREAM_READY) { - if(current->need_resched) {schedule();} - current->state=TASK_INTERRUPTIBLE; - schedule_timeout(10*HZ/1000); /* 10 ms, hopefully ;) */ - if(signal_pending(current)) { - goto end; + do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); + while (cam->params.status.streamState != STREAM_READY) { + if (current->need_resched) + schedule(); + + current->state = TASK_INTERRUPTIBLE; + + /* sleep for 10 ms, hopefully ;) */ + schedule_timeout(10*HZ/1000); + if (signal_pending(current)) + return; + + do_command(cam, CPIA_COMMAND_GetCameraStatus, + 0, 0, 0, 0); } - do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); } /* grab image from camera */ - if(current->need_resched) {schedule();} + if (current->need_resched) + schedule(); + oldjif = jiffies; - image_size=cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0); - if(image_size<=0) { - DBG("read frame failed\n"); - goto end; + image_size = cam->ops->streamRead(cam->lowlevel_data, + cam->raw_image, 0); + if (image_size <= 0) { + DBG("streamRead failed: %d\n", image_size); + continue; } + rate = image_size * HZ / 1024; diff = jiffies-oldjif; - rate = diff==0 ? rate : rate/diff; /* unlikely but possible */ - /* Keep track of transfer_rate as a runnung average over 3 frames - * to smooth out any inconsistencies */ - cam->transfer_rate = (2*cam->transfer_rate + rate) / 3; + cam->transfer_rate = diff==0 ? rate : rate/diff; + /* diff==0 ? unlikely but possible */ /* camera idle now so dispatch queued commands */ - dispatch_commands( cam ); + dispatch_commands(cam); /* Update our knowledge of the camera state - FIXME: necessary? */ do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); /* GA */ + /* decompress and convert image to by copying it from * raw_image to decompressed_frame */ - if(current->need_resched) {schedule();} + if (current->need_resched) + schedule(); + cam->image_size = parse_picture(cam, image_size); - if( cam->image_size <= 0 ) { - DBG("parse frame failed\n"); - goto end; + if (cam->image_size <= 0) + DBG("parse_picture failed %d\n", cam->image_size); + else + break; } + if (retry < 3) { /* FIXME: this only works for double buffering */ - if( cam->frame[cam->curframe].state == FRAME_READY ) { + if (cam->frame[cam->curframe].state == FRAME_READY) { memcpy(cam->frame[cam->curframe].data, cam->decompressed_frame.data, cam->decompressed_frame.count); cam->frame[cam->curframe].state = FRAME_DONE; - } else { + } else cam->decompressed_frame.state = FRAME_DONE; - } #if 0 - if( cam->first_frame && - cam->params.compression.mode != CPIA_COMPRESSION_NONE ) { + if (cam->first_frame && + cam->params.compression.mode != CPIA_COMPRESSION_NONE) { cam->first_frame = 0; cam->cmd_queue |= COMMAND_SETCOMPRESSION; } #else - if( cam->first_frame ) { + if (cam->first_frame) { cam->first_frame = 0; cam->cmd_queue |= COMMAND_SETCOMPRESSION; cam->cmd_queue |= COMMAND_SETEXPOSURE; } #endif -end: - return; + } } static int capture_frame(struct cam_data *cam, struct video_mmap *vm) { int retval = 0; - if( cam->frame_buf == NULL ) { /* we do lazy allocation */ - if( (retval = allocate_frame_buf(cam)) ) { + if (!cam->frame_buf) { + /* we do lazy allocation */ + if ((retval = allocate_frame_buf(cam))) return retval; } - } /* FIXME: the first frame seems to be captured by the camera without regards to any initial settings, so we throw away that one, the next one is generated with our settings (exposure, color balance, ...) */ - if(cam->first_frame) { + if (cam->first_frame) { cam->curframe = vm->frame; cam->frame[cam->curframe].state = FRAME_READY; fetch_frame(cam); - if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO; + if (cam->frame[cam->curframe].state != FRAME_DONE) + retval = -EIO; } cam->curframe = vm->frame; cam->frame[cam->curframe].state = FRAME_READY; fetch_frame(cam); - if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO; + if (cam->frame[cam->curframe].state != FRAME_DONE) + retval=-EIO; return retval; } static int goto_high_power(struct cam_data *cam) { - if(do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) return -1; - if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1; - if(cam->params.status.systemState == HI_POWER_STATE) { + if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) + return -1; + mdelay(100); /* windows driver does it too */ + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -1; + if (cam->params.status.systemState == HI_POWER_STATE) { DBG("camera now in HIGH power state\n"); return 0; } @@ -2315,14 +2639,15 @@ static int goto_low_power(struct cam_data *cam) { - if(do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) return -1; - if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1; - if(cam->params.status.systemState == LO_POWER_STATE) { + if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) + return -1; + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -1; + if (cam->params.status.systemState == LO_POWER_STATE) { DBG("camera now in LOW power state\n"); return 0; } printstatus(cam); - mdelay(100); /* windows driver does it too */ return -1; } @@ -2344,21 +2669,23 @@ cam->params.colourBalance.redGain, cam->params.colourBalance.greenGain, cam->params.colourBalance.blueGain); - - return; } static void set_camera_state(struct cam_data *cam) { - if(cam->params.colourBalance.balanceMode != 1) { + if(cam->params.colourBalance.balanceModeIsAuto) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 2, 0, 0, 0); + } else { do_command(cam, CPIA_COMMAND_SetColourBalance, 1, cam->params.colourBalance.redGain, cam->params.colourBalance.greenGain, cam->params.colourBalance.blueGain); + do_command(cam, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); } - if(cam->params.colourBalance.balanceMode == 0) - cam->params.colourBalance.balanceMode = 2; + do_command_extended(cam, CPIA_COMMAND_SetExposure, cam->params.exposure.gainMode, 1, 1, @@ -2375,11 +2702,11 @@ 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - if(cam->params.exposure.gainMode == 0) + if (!cam->params.exposure.gainMode) cam->params.exposure.gainMode = 2; - if(cam->params.exposure.expMode == 0) + if (!cam->params.exposure.expMode) cam->params.exposure.expMode = 2; - if(cam->params.exposure.centreWeight == 0) + if (!cam->params.exposure.centreWeight) cam->params.exposure.centreWeight = 1; cam->cmd_queue = COMMAND_SETCOMPRESSION | @@ -2407,32 +2734,25 @@ { /* GetCPIAVersion */ do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0); - printk(KERN_INFO " CPIA Version: %d.%02d (%d.%d)\n", - cam->params.version.firmwareVersion, - cam->params.version.firmwareRevision, - cam->params.version.vcVersion, - cam->params.version.vcRevision); /* GetPnPID */ do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0); - printk(KERN_INFO " CPIA PnP-ID: %04x:%04x:%04x\n", - cam->params.pnpID.vendor, cam->params.pnpID.product, - cam->params.pnpID.deviceRevision); } /* initialize camera */ static int reset_camera(struct cam_data *cam) { /* Start the camera in low power mode */ - if(goto_low_power(cam)) { - if( cam->params.status.systemState != 0x4 ) { + if (goto_low_power(cam)) { + if (cam->params.status.systemState != WARM_BOOT_STATE) return -ENODEV; - } + /* FIXME: this is just dirty trial and error */ reset_camera_struct(cam); goto_high_power(cam); do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); - if(goto_low_power(cam)) return -NODEV; + if (goto_low_power(cam)) + return -NODEV; } /* procedure described in developer's guide p3-28 */ @@ -2440,11 +2760,15 @@ /* Check the firmware version FIXME: should we check PNPID? */ cam->params.version.firmwareVersion = 0; get_version_information(cam); - if(cam->params.version.firmwareVersion != 1) { + if (cam->params.version.firmwareVersion != 1) return -ENODEV; - } - /* The fatal error checking should be done after the camera powers up (developer's guide p 3-38) */ + /* GA 04/14/00 - set QX3 detected flag */ + cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 && + cam->params.pnpID.product == 0x0001); + + /* The fatal error checking should be done after + * the camera powers up (developer's guide p 3-38) */ /* Set streamState before transition to high power to avoid bug * in firmware 1-02 */ @@ -2452,20 +2776,22 @@ STREAM_NOT_READY, 0); /* GotoHiPower */ - if(goto_high_power(cam)) + if (goto_high_power(cam)) return -ENODEV; - /* Check the camera status */ - if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -EIO; - if(cam->params.status.fatalError) { - DBG("fatal_error: %#04x\n",cam->params.status.fatalError); - DBG("vp_status: %#04x\n",cam->params.status.vpStatus); - if(cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) { + + if (cam->params.status.fatalError) { + DBG("fatal_error: %#04x\n", + cam->params.status.fatalError); + DBG("vp_status: %#04x\n", + cam->params.status.vpStatus); + if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) { /* Fatal error in camera */ return -EIO; - } else if(cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)){ + } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) { /* Firmware 1-02 may do this for parallel port cameras, * just clear the flags (developer's guide p 3-38) */ do_command(cam, CPIA_COMMAND_ModifyCameraStatus, @@ -2474,18 +2800,14 @@ } /* Check the camera status again */ - if(cam->params.status.fatalError) { - if(cam->params.status.fatalError) + if (cam->params.status.fatalError) { + if (cam->params.status.fatalError) return -EIO; } /* VPVersion can't be retrieved before the camera is in HiPower, * so get it here instead of in get_version_information. */ do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0); - printk(KERN_INFO " VP-Version: %d.%d %04x\n", - cam->params.vpVersion.vpVersion, - cam->params.vpVersion.vpRevision, - cam->params.vpVersion.cameraHeadID); /* set camera to a known state */ set_camera_state(cam); @@ -2499,53 +2821,59 @@ int i; struct cam_data *cam = dev->priv; - DBG("cpia_open\n"); + if (!cam) { + DBG("Internal error, cam_data not found!\n"); + return -EBUSY; + } - if(cam->open_count > 0) { - DBG("Camera[%d] already open\n",cam->index); + if (cam->open_count > 0) { + DBG("Camera already open\n"); return -EBUSY; } - if(cam->raw_image == NULL) { - if((cam->raw_image=rvmalloc(CPIA_MAX_IMAGE_SIZE)) == NULL){ + if (!cam->raw_image) { + cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE); + if (!cam->raw_image) return -ENOMEM; } - } - if(cam->decompressed_frame.data == NULL) { - cam->decompressed_frame.data=rvmalloc(CPIA_MAX_FRAME_SIZE); - if(cam->decompressed_frame.data == NULL){ + + if (!cam->decompressed_frame.data) { + cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE); + if (!cam->decompressed_frame.data) { rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image=NULL; + cam->raw_image = NULL; return -ENOMEM; } } /* open cpia */ - if (cam->ops->open(cam->index, &cam->lowlevel_data)) { + if (cam->ops->open(cam->lowlevel_data)) { rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data=NULL; + cam->decompressed_frame.data = NULL; rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image=NULL; + cam->raw_image = NULL; return -ENODEV; } /* reset the camera */ - if((i = reset_camera(cam)) != 0) { + if ((i = reset_camera(cam)) != 0) { cam->ops->close(cam->lowlevel_data); rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data=NULL; + cam->decompressed_frame.data = NULL; rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image=NULL; + cam->raw_image = NULL; return i; } /* Set ownership of /proc/cpia/videoX to current user */ - if(cam->proc_entry != NULL) + if(cam->proc_entry) cam->proc_entry->uid = current->uid; /* set mark for loading first frame uncompressed */ cam->first_frame = 1; - cam->busy_lock = MUTEX; + + /* init it to something */ + cam->mmap_kludge = 0; ++cam->open_count; #ifdef MODULE @@ -2554,49 +2882,15 @@ return 0; } -/* FIXME */ static void cpia_close(struct video_device *dev) { struct cam_data *cam; - DBG("cpia_close\n"); cam = dev->priv; - if(cam->ops == NULL) { - if(--cam->open_count == 0) { - int i; - video_unregister_device(dev); - /* Need a lock when adding/removing cameras */ - lock_kernel(); - if(cam->raw_image) { - rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image=0; - } - if(cam->decompressed_frame.data) { - rvfree(cam->decompressed_frame.data, - CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data=0; - } - if(cam->frame_buf) { - free_frame_buf(cam); - } - for(i=0; iops) { /* Return ownership of /proc/cpia/videoX to root */ - if(cam->proc_entry != NULL) + if(cam->proc_entry) cam->proc_entry->uid = 0; /* save camera state for later open (developers guide ch 3.5.3) */ @@ -2605,27 +2899,34 @@ /* GotoLoPower */ goto_low_power(cam); - /* cleanup internal state stuff */ - free_frames(cam->frame); - /* Update the camera ststus */ do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + /* cleanup internal state stuff */ + free_frames(cam->frame); + /* close cpia */ cam->ops->close(cam->lowlevel_data); + } - if(--cam->open_count == 0) { + if (--cam->open_count == 0) { /* clean up capture-buffers */ - if(cam->raw_image) { + if (cam->raw_image) { rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image = 0; + cam->raw_image = NULL; } - if(cam->decompressed_frame.data) { + + if (cam->decompressed_frame.data) { rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data = 0; + cam->decompressed_frame.data = NULL; } - if(cam->frame_buf) { + + if (cam->frame_buf) free_frame_buf(cam); + + if (!cam->ops) { + video_unregister_device(dev); + kfree(cam); } } @@ -2642,21 +2943,22 @@ struct cam_data *cam = dev->priv; /* make this _really_ smp and multithredi-safe */ - if( down_interruptible(&cam->busy_lock) ) { + if (down_interruptible(&cam->busy_lock)) return -EINTR; - } if (!buf) { DBG("buf NULL\n"); up(&cam->busy_lock); return -EINVAL; } - if(count == 0) { + + if (!count) { DBG("count 0\n"); up(&cam->busy_lock); return 0; } - if(!cam->ops) { + + if (!cam->ops) { DBG("ops NULL\n"); up(&cam->busy_lock); return -ENODEV; @@ -2664,8 +2966,9 @@ /* upload frame */ cam->decompressed_frame.state = FRAME_READY; + cam->mmap_kludge=0; fetch_frame(cam); - if( cam->decompressed_frame.state != FRAME_DONE ) { + if (cam->decompressed_frame.state != FRAME_DONE) { DBG("upload failed %d/%d\n", cam->decompressed_frame.count, cam->decompressed_frame.state); up(&cam->busy_lock); @@ -2674,12 +2977,13 @@ cam->decompressed_frame.state = FRAME_UNUSED; /* copy data to user space */ - if(cam->decompressed_frame.count > count) { - DBG("count wrong\n"); + if (cam->decompressed_frame.count > count) { + DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count, + count); up(&cam->busy_lock); return -EFAULT; } - if(copy_to_user(buf, cam->decompressed_frame.data, + if (copy_to_user(buf, cam->decompressed_frame.data, cam->decompressed_frame.count)) { DBG("copy_to_user failed\n"); up(&cam->busy_lock); @@ -2695,21 +2999,23 @@ struct cam_data *cam = dev->priv; int retval = 0; + if (!cam || !cam->ops) + return -ENODEV; + /* make this _really_ smp-safe */ - if( down_interruptible(&cam->busy_lock) ) { + if (down_interruptible(&cam->busy_lock)) return -EINTR; - } + //DBG("cpia_ioctl: %u\n", ioctlnr); switch (ioctlnr) { - /* query capabilites */ case VIDIOCGCAP: { struct video_capability b; DBG("VIDIOCGCAP\n"); - strcpy(b.name, "CPiA Parport Camera"); + strcpy(b.name, "CPiA Camera"); b.type = VID_TYPE_CAPTURE; b.channels = 1; b.audios = 0; @@ -2720,6 +3026,7 @@ if (copy_to_user(arg, &b, sizeof(b))) retval = -EFAULT; + break; } @@ -2782,13 +3089,15 @@ retval = -EFAULT; break; } + /* check validity */ DBG("palette: %d\n", vp.palette); DBG("depth: %d\n", vp.depth); - if ( !valid_mode(vp.palette, vp.depth) ) { + if (!valid_mode(vp.palette, vp.depth)) { retval = -EINVAL; break; } + down(&cam->param_lock); /* brightness, colour, contrast need no check 0-65535 */ memcpy( &cam->vp, &vp, sizeof(vp) ); @@ -2799,12 +3108,13 @@ /* contrast is in steps of 8, so round */ cam->params.colourParams.contrast = ((cam->params.colourParams.contrast + 3) / 8) * 8; - if(cam->params.version.firmwareVersion == 1 && + if (cam->params.version.firmwareVersion == 1 && cam->params.version.firmwareRevision == 2 && cam->params.colourParams.contrast > 80) { /* 1-02 firmware limits contrast to 80 */ cam->params.colourParams.contrast = 80; } + /* queue command to update camera */ cam->cmd_queue |= COMMAND_SETCOLOURPARAMS; up(&cam->param_lock); @@ -2845,9 +3155,10 @@ * is requested by the user??? */ down(&cam->param_lock); - if(vw.width!=cam->vw.width || vw.height!=cam->vw.height) { + if (vw.width != cam->vw.width || vw.height != cam->vw.height) { int video_size = match_videosize(vw.width, vw.height); - if(video_size < 0) { + + if (video_size < 0) { retval = -EINVAL; up(&cam->param_lock); break; @@ -2863,7 +3174,7 @@ /* setformat ignored by camera during streaming, * so stop/dispatch/start */ - if(cam->cmd_queue & COMMAND_SETFORMAT) { + if (cam->cmd_queue & COMMAND_SETFORMAT) { DBG("\n"); dispatch_commands(cam); } @@ -2882,10 +3193,10 @@ memset(&vm, 0, sizeof(vm)); vm.size = CPIA_MAX_FRAME_SIZE*FRAME_NUM; vm.frames = FRAME_NUM; - for( i=0; ivp.palette = vm.format; switch(vm.format) { case VIDEO_PALETTE_GREY: + cam->vp.depth = 8; + break; case VIDEO_PALETTE_RGB555: case VIDEO_PALETTE_RGB565: case VIDEO_PALETTE_YUV422: @@ -2930,15 +3243,16 @@ retval = -EINVAL; break; } - if(retval != 0) break; + if (retval) + break; /* set video size */ video_size = match_videosize(vm.width, vm.height); - if(cam->video_size<0) { + if (cam->video_size < 0) { retval = -EINVAL; break; } - if( video_size != cam->video_size ) { + if (video_size != cam->video_size) { cam->video_size = video_size; set_vw_size(cam); cam->cmd_queue |= COMMAND_SETFORMAT; @@ -2949,6 +3263,7 @@ cam->vw.width, cam->vw.height); #endif /* according to v4l-spec we must start streaming here */ + cam->mmap_kludge = 1; retval = capture_frame(cam, &vm); break; @@ -2964,7 +3279,7 @@ } //DBG("VIDIOCSYNC: %d\n", frame); - if (frame<0||frame>=FRAME_NUM) { + if (frame<0 || frame >= FRAME_NUM) { retval = -EINVAL; break; } @@ -2982,7 +3297,7 @@ //DBG("VIDIOCSYNC: %d synced\n", frame); break; } - if(retval == -EINTR) { + if (retval == -EINTR) { /* FIXME - xawtv does not handle this nice */ retval = 0; } @@ -3040,49 +3355,57 @@ { unsigned long start = (unsigned long)adr; unsigned long page, pos; - struct cam_data *cam; + struct cam_data *cam = dev->priv; int retval; + if (!cam || !cam->ops) + return -ENODEV; + DBG("cpia_mmap: %ld\n", size); - if(size > FRAME_NUM*CPIA_MAX_FRAME_SIZE){ + if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE) return -EINVAL; - } - cam = dev->priv; + if (!cam || !cam->ops) + return -ENODEV; /* make this _really_ smp-safe */ - if( down_interruptible(&cam->busy_lock) ) { + if (down_interruptible(&cam->busy_lock)) return -EINTR; - } - if( cam->frame_buf == NULL ) { /* we do lazy allocation */ - if( (retval = allocate_frame_buf(cam)) ) { + if (!cam->frame_buf) { /* we do lazy allocation */ + if ((retval = allocate_frame_buf(cam))) { up(&cam->busy_lock); return retval; } } + pos = (unsigned long)(cam->frame_buf); while (size > 0) { - page = kvirt_to_phys(pos); + page = kvirt_to_pa(pos); if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) { up(&cam->busy_lock); return -EAGAIN; } - start+=PAGE_SIZE; - pos+=PAGE_SIZE; - if (size > PAGE_SIZE) size-=PAGE_SIZE; - else size=0; + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; } DBG("cpia_mmap: %ld\n", size); up(&cam->busy_lock); + return 0; } int cpia_video_init(struct video_device *vdev) { +#ifdef CONFIG_PROC_FS create_proc_cpia_cam(vdev->priv); +#endif return 0; } @@ -3124,7 +3447,7 @@ cam->params.exposure.green1Comp = 214; cam->params.exposure.green2Comp = 214; cam->params.exposure.blueComp = 230; - cam->params.colourBalance.balanceMode = 2; /* auto color balance */ + cam->params.colourBalance.balanceModeIsAuto = 1; cam->params.colourBalance.redGain = 32; cam->params.colourBalance.greenGain = 6; cam->params.colourBalance.blueGain = 92; @@ -3151,16 +3474,15 @@ cam->params.compressionParams.decimationThreshMod = 2; /* End of default values from Software Developer's Guide */ - /* Start with a reasonable transfer rate */ - cam->transfer_rate = 500; + cam->transfer_rate = 0; /* Set Sensor FPS to 15fps. This seems better than 30fps * for indoor lighting. */ cam->params.sensorFps.divisor = 1; cam->params.sensorFps.baserate = 1; - cam->params.yuvThreshold.yThreshold = 15; /* FIXME? */ - cam->params.yuvThreshold.uvThreshold = 15; /* FIXME? */ + cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */ + cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */ cam->params.format.subSample = SUBSAMPLE_422; cam->params.format.yuvOrder = YUVORDER_YUYV; @@ -3168,8 +3490,14 @@ cam->params.compression.mode = CPIA_COMPRESSION_AUTO; cam->params.compressionTarget.frTargeting = CPIA_COMPRESSION_TARGET_QUALITY; - cam->params.compressionTarget.targetFR = 7; /* FIXME? */ - cam->params.compressionTarget.targetQ = 10; /* FIXME? */ + cam->params.compressionTarget.targetFR = 15; /* From windows driver */ + cam->params.compressionTarget.targetQ = 5; /* From windows driver */ + + cam->params.qx3.qx3_detected = 0; + cam->params.qx3.toplight = 0; + cam->params.qx3.bottomlight = 0; + cam->params.qx3.button = 0; + cam->params.qx3.cradled = 0; cam->video_size = VIDEOSIZE_CIF; @@ -3179,7 +3507,7 @@ cam->vp.contrast = 32768; /* 50% */ cam->vp.whiteness = 0; /* not used -> grayscale only */ cam->vp.depth = 0; /* FIXME: to be set by user? */ - cam->vp.palette = 0; /* FIXME: to be set by user? */ + cam->vp.palette = VIDEO_PALETTE_RGB24; /* FIXME: to be set by user? */ cam->vw.x = 0; cam->vw.y = 0; @@ -3201,11 +3529,13 @@ struct cpia_camera_ops *ops ) { int i; + /* Default everything to 0 */ memset(cam, 0, sizeof(struct cam_data)); cam->ops = ops; - cam->param_lock = MUTEX; + init_MUTEX(&cam->param_lock); + init_MUTEX(&cam->busy_lock); reset_camera_struct(cam); @@ -3215,7 +3545,7 @@ cam->vdev.priv = cam; cam->curframe = 0; - for( i=0; iframe[i].width = 0; cam->frame[i].height = 0; cam->frame[i].state = FRAME_UNUSED; @@ -3225,95 +3555,84 @@ cam->decompressed_frame.height = 0; cam->decompressed_frame.state = FRAME_UNUSED; cam->decompressed_frame.data = NULL; - - return; } -int cpia_register_camera(struct cpia_camera_ops *ops) +struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel) { - int i; - struct cam_data *cam; + struct cam_data *camera; + /* Need a lock when adding/removing cameras. This doesn't happen * often and doesn't take very long, so grabbing the kernel lock * should be OK. */ - lock_kernel(); - for(i=0; i < CPIA_MAXCAMS && camera[i] != NULL; ++i); /* no loop body */ - if(i == CPIA_MAXCAMS) { - unlock_kernel(); - return -ENODEV; - } - if((camera[i] = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) { + if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) { unlock_kernel(); - return -ENOMEM; + return NULL; } - init_camera_struct( camera[i], ops ); - camera[i]->index = i; + init_camera_struct( camera, ops ); + camera->lowlevel_data = lowlevel; /* register v4l device */ - if (video_register_device(&camera[i]->vdev, VFL_TYPE_GRABBER) == -1) { - kfree(camera[i]); - camera[i] = NULL; + if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER) == -1) { + kfree(camera); unlock_kernel(); printk(KERN_DEBUG "video_register_device failed\n"); - return -ENODEV; + return NULL; } - unlock_kernel(); + /* get version information from camera: open/reset/close */ - /* FIXME */ -#if 0 - cam = camera[i]; /* open cpia */ - if (cam->ops->open(cam->index, &cam->lowlevel_data)) { - rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data=NULL; - rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image=NULL; - return -ENODEV; - } + if (camera->ops->open(camera->lowlevel_data)) + return camera; /* reset the camera */ - if((i = reset_camera(cam)) != 0) { - cam->ops->close(cam->lowlevel_data); - rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data=NULL; - rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image=NULL; - return i; + if (reset_camera(camera) != 0) { + camera->ops->close(camera->lowlevel_data); + return camera; } -#endif - return i; + + /* close cpia */ + camera->ops->close(camera->lowlevel_data); + + printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n", + camera->params.version.firmwareVersion, + camera->params.version.firmwareRevision, + camera->params.version.vcVersion, + camera->params.version.vcRevision); + printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n", + camera->params.pnpID.vendor, + camera->params.pnpID.product, + camera->params.pnpID.deviceRevision); + printk(KERN_INFO " VP-Version: %d.%d %04x\n", + camera->params.vpVersion.vpVersion, + camera->params.vpVersion.vpRevision, + camera->params.vpVersion.cameraHeadID); + + return camera; } -void cpia_unregister_camera(int camnr) +void cpia_unregister_camera(struct cam_data *cam) { - struct cam_data *cam; - if(camnr >= CPIA_MAXCAMS || camera[camnr] == NULL) return; - cam = camera[camnr]; - + if (!cam->open_count) { DBG("unregistering video\n"); - /* FIXME: Is this safe if the device is open? */ video_unregister_device(&cam->vdev); + } else { + LOG("/dev/video%d removed while open, " + "deferring video_unregister_device\n", cam->vdev.minor); + DBG("camera open -- setting ops to NULL\n"); + cam->ops = NULL; + } - /* Need a lock when adding/removing cameras. This doesn't happen - * often and doesn't take very long, so grabbing the kernel lock - * should be OK. */ - lock_kernel(); - +#ifdef CONFIG_PROC_FS DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor); destroy_proc_cpia_cam(cam); - - if(cam->open_count == 0) { +#endif + if (!cam->open_count) { DBG("freeing camera\n"); - kfree(camera[camnr]); - camera[camnr] = NULL; - } else { - DBG("camera open -- setting ops to NULL\n"); - cam->ops = NULL; + kfree(cam); } - unlock_kernel(); } /**************************************************************************** @@ -3327,7 +3646,9 @@ { printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); +#ifdef CONFIG_PROC_FS proc_cpia_create(); +#endif #ifdef CONFIG_KMOD #ifdef CONFIG_VIDEO_CPIA_PP_MODULE request_module("cpia_pp"); @@ -3341,14 +3662,9 @@ void cleanup_module(void) { - int i; - for(i=0; ivdev); - } - } - +#ifdef CONFIG_PROC_FS proc_cpia_destroy(); +#endif } #else @@ -3357,25 +3673,25 @@ { printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); +#ifdef CONFIG_PROC_FS proc_cpia_create(); +#endif -#ifdef MODULE +#ifdef CONFIG_VIDEO_CPIA_PP + cpia_pp_init(); +#endif #ifdef CONFIG_KMOD #ifdef CONFIG_VIDEO_CPIA_PP_MODULE request_module("cpia_pp"); #endif + #ifdef CONFIG_VIDEO_CPIA_USB_MODULE request_module("cpia_usb"); #endif #endif /* CONFIG_KMOD */ -#else /* !MODULE */ -#ifdef CONFIG_VIDEO_CPIA_PP - cpia_pp_init(); -#endif #ifdef CONFIG_VIDEO_CPIA_USB cpia_usb_init(); #endif -#endif /* !MODULE */ return 0; } diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/char/cpia.h linux-2.2.17/drivers/char/cpia.h --- linux-2.2.17-orig/drivers/char/cpia.h Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/char/cpia.h Sun Sep 24 05:06:37 2000 @@ -0,0 +1,430 @@ +#ifndef cpia_h +#define cpia_h + +/* + * CPiA Parallel Port Video4Linux driver + * + * Supports CPiA based parallel port Video Camera's. + * + * (C) Copyright 1999 Bas Huisman, + * Peter Pregler, + * Scott J. Bertin, + * VLSI Vision Ltd. + * + * 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. + */ + +#define CPIA_MAJ_VER 1 +#define CPIA_MIN_VER 0 +#define CPIA_PATCH_VER 0 + +#define CPIA_PP_MAJ_VER 1 +#define CPIA_PP_MIN_VER 0 +#define CPIA_PP_PATCH_VER 0 + +#define CPIA_MAX_FRAME_SIZE_UNALIGNED (352 * 288 * 4) /* CIF at RGB32 */ +#define CPIA_MAX_FRAME_SIZE ((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */ + +#ifdef __KERNEL__ + +#include +#include +#include + +struct cpia_camera_ops +{ + /* open sets privdata to point to structure for this camera. + * Returns negative value on error, otherwise 0. + */ + int (*open)(void *privdata); + + /* Registers callback function cb to be called with cbdata + * when an image is ready. If cb is NULL, only single image grabs + * should be used. cb should immediately call streamRead to read + * the data or data may be lost. Returns negative value on error, + * otherwise 0. + */ + int (*registerCallback)(void *privdata, void (*cb)(void *cbdata), + void *cbdata); + + /* transferCmd sends commands to the camera. command MUST point to + * an 8 byte buffer in kernel space. data can be NULL if no extra + * data is needed. The size of the data is given by the last 2 + * bytes of comand. data must also point to memory in kernel space. + * Returns negative value on error, otherwise 0. + */ + int (*transferCmd)(void *privdata, u8 *command, u8 *data); + + /* streamStart initiates stream capture mode. + * Returns negative value on error, otherwise 0. + */ + int (*streamStart)(void *privdata); + + /* streamStop terminates stream capture mode. + * Returns negative value on error, otherwise 0. + */ + int (*streamStop)(void *privdata); + + /* streamRead reads a frame from the camera. buffer points to a + * buffer large enough to hold a complete frame in kernel space. + * noblock indicates if this should be a non blocking read. + * Returns the number of bytes read, or negative value on error. + */ + int (*streamRead)(void *privdata, u8 *buffer, int noblock); + + /* close disables the device until open() is called again. + * Returns negative value on error, otherwise 0. + */ + int (*close)(void *privdata); + + /* If wait_for_stream_ready is non-zero, wait until the streamState + * is STREAM_READY before calling streamRead. + */ + int wait_for_stream_ready; +}; + +struct cpia_frame { + u8 *data; + int count; + int width; + int height; + volatile int state; +}; + +struct cam_params { + struct { + u8 firmwareVersion; + u8 firmwareRevision; + u8 vcVersion; + u8 vcRevision; + } version; + struct { + u16 vendor; + u16 product; + u16 deviceRevision; + } pnpID; + struct { + u8 vpVersion; + u8 vpRevision; + u16 cameraHeadID; + } vpVersion; + struct { + u8 systemState; + u8 grabState; + u8 streamState; + u8 fatalError; + u8 cmdError; + u8 debugFlags; + u8 vpStatus; + u8 errorCode; + } status; + struct { + u8 brightness; + u8 contrast; + u8 saturation; + } colourParams; + struct { + u8 gainMode; + u8 expMode; + u8 compMode; + u8 centreWeight; + u8 gain; + u8 fineExp; + u8 coarseExpLo; + u8 coarseExpHi; + u8 redComp; + u8 green1Comp; + u8 green2Comp; + u8 blueComp; + } exposure; + struct { + u8 balanceModeIsAuto; + u8 redGain; + u8 greenGain; + u8 blueGain; + } colourBalance; + struct { + u8 divisor; + u8 baserate; + } sensorFps; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } apcor; + struct { + u8 flickerMode; + u8 coarseJump; + u8 allowableOverExposure; + } flickerControl; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } vlOffset; + struct { + u8 mode; + u8 decimation; + } compression; + struct { + u8 frTargeting; + u8 targetFR; + u8 targetQ; + } compressionTarget; + struct { + u8 yThreshold; + u8 uvThreshold; + } yuvThreshold; + struct { + u8 hysteresis; + u8 threshMax; + u8 smallStep; + u8 largeStep; + u8 decimationHysteresis; + u8 frDiffStepThresh; + u8 qDiffStepThresh; + u8 decimationThreshMod; + } compressionParams; + struct { + u8 videoSize; /* CIF/QCIF */ + u8 subSample; + u8 yuvOrder; + } format; + + struct { /* GA 04/14/00 - Intel QX3 specific data */ + u8 qx3_detected; /* a QX3 is present */ + u8 toplight; /* top light lit , R/W */ + u8 bottomlight; /* bottom light lit, R/W */ + u8 button; /* snapshot button pressed (R/O) */ + u8 cradled; /* microscope is in cradle (R/O) */ + } qx3; + + struct { + u8 colStart; /* skip first 8*colStart pixels */ + u8 colEnd; /* finish at 8*colEnd pixels */ + u8 rowStart; /* skip first 4*rowStart lines */ + u8 rowEnd; /* finish at 4*rowEnd lines */ + } roi; + u8 ecpTiming; + u8 streamStartLine; +}; + +enum v4l_camstates { + CPIA_V4L_IDLE = 0, + CPIA_V4L_ERROR, + CPIA_V4L_COMMAND, + CPIA_V4L_GRABBING, + CPIA_V4L_STREAMING, + CPIA_V4L_STREAMING_PAUSED, +}; + +#define FRAME_NUM 2 /* double buffering for now */ + +struct cam_data { + struct cam_data **previous; + struct cam_data *next; + + struct semaphore busy_lock; /* guard against SMP multithreading */ + struct cpia_camera_ops *ops; /* lowlevel driver operations */ + void *lowlevel_data; /* private data for lowlevel driver */ + u8 *raw_image; /* buffer for raw image data */ + struct cpia_frame decompressed_frame; + /* buffer to hold decompressed frame */ + int image_size; /* sizeof last decompressed image */ + int open_count; /* # of process that have camera open */ + + /* camera status */ + int fps; /* actual fps reported by the camera */ + int transfer_rate; /* transfer rate from camera in kB/s */ + u8 mainsFreq; /* for flicker control */ + + /* proc interface */ + struct semaphore param_lock; /* params lock for this camera */ + struct cam_params params; /* camera settings */ + struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */ + + /* v4l */ + int video_size; /* VIDEO_SIZE_ */ + volatile enum v4l_camstates camstate; /* v4l layer status */ + struct video_device vdev; /* v4l videodev */ + struct video_picture vp; /* v4l camera settings */ + struct video_window vw; /* v4l capture area */ + + /* mmap interface */ + int curframe; /* the current frame to grab into */ + u8 *frame_buf; /* frame buffer data */ + struct cpia_frame frame[FRAME_NUM]; + /* FRAME_NUM-buffering, so we need a array */ + + int first_frame; + int mmap_kludge; /* 'wrong' byte order for mmap */ + volatile u32 cmd_queue; /* queued commands */ +}; + +/* cpia_register_camera is called by low level driver for each camera. + * A unique camera number is returned, or a negative value on error */ +struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel); + +/* cpia_unregister_camera is called by low level driver when a camera + * is removed. This must not fail. */ +void cpia_unregister_camera(struct cam_data *cam); + +/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI + + * one byte 16bit DMA alignment + */ +#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5) + +/* constant value's */ +#define MAGIC_0 0x19 +#define MAGIC_1 0x68 +#define DATA_IN 0xC0 +#define DATA_OUT 0x40 +#define VIDEOSIZE_QCIF 0 /* 176x144 */ +#define VIDEOSIZE_CIF 1 /* 352x288 */ +#define VIDEOSIZE_SIF 2 /* 320x240 */ +#define VIDEOSIZE_QSIF 3 /* 160x120 */ +#define VIDEOSIZE_48_48 4 /* where no one has gone before, iconsize! */ +#define VIDEOSIZE_64_48 5 +#define VIDEOSIZE_128_96 6 +#define VIDEOSIZE_160_120 VIDEOSIZE_QSIF +#define VIDEOSIZE_176_144 VIDEOSIZE_QCIF +#define VIDEOSIZE_192_144 7 +#define VIDEOSIZE_224_168 8 +#define VIDEOSIZE_256_192 9 +#define VIDEOSIZE_288_216 10 +#define VIDEOSIZE_320_240 VIDEOSIZE_SIF +#define VIDEOSIZE_352_288 VIDEOSIZE_CIF +#define VIDEOSIZE_88_72 11 /* quarter CIF */ +#define SUBSAMPLE_420 0 +#define SUBSAMPLE_422 1 +#define YUVORDER_YUYV 0 +#define YUVORDER_UYVY 1 +#define NOT_COMPRESSED 0 +#define COMPRESSED 1 +#define NO_DECIMATION 0 +#define DECIMATION_ENAB 1 +#define EOI 0xff /* End Of Image */ +#define EOL 0xfd /* End Of Line */ +#define FRAME_HEADER_SIZE 64 + +/* Image grab modes */ +#define CPIA_GRAB_SINGLE 0 +#define CPIA_GRAB_CONTINUOUS 1 + +/* Compression parameters */ +#define CPIA_COMPRESSION_NONE 0 +#define CPIA_COMPRESSION_AUTO 1 +#define CPIA_COMPRESSION_MANUAL 2 +#define CPIA_COMPRESSION_TARGET_QUALITY 0 +#define CPIA_COMPRESSION_TARGET_FRAMERATE 1 + +/* Return offsets for GetCameraState */ +#define SYSTEMSTATE 0 +#define GRABSTATE 1 +#define STREAMSTATE 2 +#define FATALERROR 3 +#define CMDERROR 4 +#define DEBUGFLAGS 5 +#define VPSTATUS 6 +#define ERRORCODE 7 + +/* SystemState */ +#define UNINITIALISED_STATE 0 +#define PASS_THROUGH_STATE 1 +#define LO_POWER_STATE 2 +#define HI_POWER_STATE 3 +#define WARM_BOOT_STATE 4 + +/* GrabState */ +#define GRAB_IDLE 0 +#define GRAB_ACTIVE 1 +#define GRAB_DONE 2 + +/* StreamState */ +#define STREAM_NOT_READY 0 +#define STREAM_READY 1 +#define STREAM_OPEN 2 +#define STREAM_PAUSED 3 +#define STREAM_FINISHED 4 + +/* Fatal Error, CmdError, and DebugFlags */ +#define CPIA_FLAG 1 +#define SYSTEM_FLAG 2 +#define INT_CTRL_FLAG 4 +#define PROCESS_FLAG 8 +#define COM_FLAG 16 +#define VP_CTRL_FLAG 32 +#define CAPTURE_FLAG 64 +#define DEBUG_FLAG 128 + +/* VPStatus */ +#define VP_STATE_OK 0x00 + +#define VP_STATE_FAILED_VIDEOINIT 0x01 +#define VP_STATE_FAILED_AECACBINIT 0x02 +#define VP_STATE_AEC_MAX 0x04 +#define VP_STATE_ACB_BMAX 0x08 + +#define VP_STATE_ACB_RMIN 0x10 +#define VP_STATE_ACB_GMIN 0x20 +#define VP_STATE_ACB_RMAX 0x40 +#define VP_STATE_ACB_GMAX 0x80 + +/* ErrorCode */ +#define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum exposure */ + +#define ALOG(lineno,fmt,args...) printk(fmt,lineno,##args) +#define LOG(fmt,args...) ALOG((__LINE__),KERN_INFO __FILE__":"__FUNCTION__"(%d):"fmt,##args) + +#ifdef _CPIA_DEBUG_ +#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, lineno, ##args) +#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):"__FUNCTION__"(%d):"fmt,##args) +#else +#define DBG(fmn,args...) do {} while(0) +#endif + +#define DEB_BYTE(p)\ + DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\ + (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\ + (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0); + +#define ADD_TO_LIST(l, drv) \ + {\ + lock_kernel();\ + (drv)->next = l;\ + (drv)->previous = &(l);\ + (l) = drv;\ + unlock_kernel();\ + } while(0) + +#define REMOVE_FROM_LIST(drv) \ + {\ + if ((drv)->previous != NULL) {\ + lock_kernel();\ + if ((drv)->next != NULL)\ + (drv)->next->previous = (drv)->previous;\ + *((drv)->previous) = (drv)->next;\ + (drv)->previous = NULL;\ + (drv)->next = NULL;\ + unlock_kernel();\ + }\ + } while (0) + + +#endif /* __KERNEL__ */ + +#endif /* cpia_h */ diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/char/cpia_pp.c linux-2.2.17/drivers/char/cpia_pp.c --- linux-2.2.17-orig/drivers/char/cpia_pp.c Wed May 3 17:16:33 2000 +++ linux-2.2.17/drivers/char/cpia_pp.c Sun Sep 24 04:26:22 2000 @@ -3,10 +3,9 @@ * * Supports CPiA based parallel port Video Camera's. * - * (C) 1999 Bas Huisman - * Scott J. Bertin , - * Peter Pregler - * Johannes Erdfelt + * (C) Copyright 1999 Bas Huisman + * (C) Copyright 1999-2000 Scott J. Bertin , + * (C) Copyright 1999-2000 Peter Pregler * * 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 @@ -24,6 +23,11 @@ */ #include +#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) +#undef CONFIG_VIDEO_CPIA_PP_DMA +#endif #include #include @@ -32,6 +36,8 @@ #include #include #include +#include + #ifdef CONFIG_VIDEO_CPIA_PP_DMA #include #endif @@ -41,6 +47,7 @@ #endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) /* If this is a module and parport_pc is not, some parport_pc_* functions * are not directly availbale. parport.h messes this up. * This fixes what we need. @@ -67,11 +74,12 @@ #define parport_disable_irq(p) parport_pc_disable_irq(p) #endif /* parport_enable_irq */ #endif /* !PARPORT_NEED_GENERIC_OPS */ +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) */ -#undef _CPIA_DEBUG_ /* define for verbose debug output */ -#include +/* #define _CPIA_DEBUG_ define for verbose debug output */ +#include "cpia.h" -static int cpia_pp_open(int camnr, void **privdata); +static int cpia_pp_open(void *privdata); static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata), void *cbdata); static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data); @@ -126,6 +134,8 @@ #define ECP_FIFO_SIZE 16 #define DMA_BUFFER_SIZE PAGE_SIZE /* for 16bit DMA make sure DMA_BUFFER_SIZE is 16 bit aligned */ +#define PARPORT_CHUNK_SIZE PAGE_SIZE/* >=2.3.x */ + /* we read this many bytes at once */ #define GetECRMasked(port,mask) (parport_read_econtrol(port) & (mask)) #define GetStatus(port) ((parport_read_status(port)^STATUS_INVERSION_MASK)&(0xf8)) @@ -139,7 +149,6 @@ #define ClearControlMasked(port,mask) SetControl(port,GetControl(port)&~(mask)); #define FrobControlBit(port,mask,value) SetControl(port,(GetControl(port)&~(mask))|((value)&(mask))); -#define PP_MAXCAMS (PARPORT_MAX & Peter Pregler "); MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras"); -MODULE_PARM(parport, "1-" __MODULE_STRING(PP_MAXCAMS) "s"); +MODULE_PARM(parport, "1-" __MODULE_STRING(PARPORT_MAX) "s"); MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); #else -static int parport_nr[PP_MAXCAMS] __initdata = - {[0 ... PP_MAXCAMS - 1] = PPCPIA_PARPORT_UNSPEC}; +static int parport_nr[PARPORT_MAX] __initdata = + {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; static int parport_ptr = 0; #endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) enum comstates { CPIA_FORWARD, CPIA_REVERSE }; //fixme enum camstates { CPIA_PHASE_idle = 0, @@ -187,16 +198,22 @@ "CPIA_PHASE_secpwrite", }; #endif +#endif + struct pp_cam_entry { struct pardevice *pdev; struct parport *port; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) enum comstates state; enum camstates camstate; +#endif struct tq_struct cb_task; int open_count; - int camnr; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) struct wait_queue *wq_stream; - +#else + wait_queue_head_t wq_stream; +#endif /* image state flags */ int image_ready; /* we got an interrupt */ int image_complete; /* we have seen 4 EOI */ @@ -220,8 +237,6 @@ #endif }; -static struct pp_cam_entry *camera[PP_MAXCAMS] = {[0 ... PP_MAXCAMS - 1] = NULL}; - static struct cpia_camera_ops cpia_pp_ops = { cpia_pp_open, @@ -230,9 +245,12 @@ cpia_pp_streamStart, cpia_pp_streamStop, cpia_pp_streamRead, - cpia_pp_close + cpia_pp_close, + 1 }; +static struct cam_data *cam_list; + #ifdef _CPIA_DEBUG_ #define DEB_PORT(port) { \ u8 controll = GetControl(port); \ @@ -269,6 +287,7 @@ return; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) static int while_out(struct pp_cam_entry *cam) { struct parport *port = cam->port; @@ -709,7 +728,7 @@ release_dma_lock(flags); return cam->readbytes; } -#endif +#endif /* CONFIG_VIDEO_CPIA_PP_DMA */ /**************************************************************************** * @@ -779,6 +798,7 @@ return written; } +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) */ /**************************************************************************** * @@ -787,10 +807,13 @@ ***************************************************************************/ static void EndTransferMode(struct pp_cam_entry *cam) { - if (!cam) return; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if (cam->state == CPIA_REVERSE) Reverse2Forward(cam); Valid1284Termination(cam); if(cam->stream_irq) cpia_parport_enable_irq(cam->port); +#else + parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); +#endif } /**************************************************************************** @@ -800,6 +823,7 @@ ***************************************************************************/ static int ForwardSetup(struct pp_cam_entry *cam) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if(cam->stream_irq) cpia_parport_disable_irq(cam->port); if (!Negotiate2SetupPhase(cam, 0)) { if (!ECPSetupPhase(cam)) { @@ -812,6 +836,22 @@ } EndTransferMode(cam); return -1; +#else + int retry; + + /* After some commands the camera needs extra time before + * it will respond again, so we try up to 3 times */ + for(retry=0; retry<3; ++retry) { + if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) { + break; + } + } + if(retry == 3) { + DBG("Unable to negotiate ECP mode\n"); + return -1; + } + return 0; +#endif } /**************************************************************************** @@ -821,6 +861,7 @@ ***************************************************************************/ static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if(cam->stream_irq) cpia_parport_disable_irq(cam->port); if (!Negotiate2SetupPhase(cam, extensibility)) { if (!ECPSetupPhase(cam)) { @@ -837,6 +878,28 @@ } EndTransferMode(cam); return -1; +#else + int retry; + int mode = IEEE1284_MODE_ECP; + if(extensibility) mode = 8|3|IEEE1284_EXT_LINK; + + /* After some commands the camera needs extra time before + * it will respond again, so we try up to 3 times */ + for(retry=0; retry<3; ++retry) { + if(!parport_negotiate(cam->port, mode)) { + break; + } + } + if(retry == 3) { + if(extensibility) + DBG("Unable to negotiate extensibility mode\n"); + else + DBG("Unable to negotiate ECP mode\n"); + return -1; + } + if(extensibility) cam->port->ieee1284.mode = IEEE1284_MODE_ECP; + return 0; +#endif } #ifdef CONFIG_VIDEO_CPIA_PP_DMA @@ -967,7 +1030,7 @@ } return; } -#endif +#endif /* CONFIG_VIDEO_CPIA_PP_DMA */ /**************************************************************************** * @@ -975,6 +1038,7 @@ * ***************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) static void cpia_pp_irq_handler(int irq, void *handle, struct pt_regs *b) { struct pp_cam_entry *cam = handle; @@ -992,7 +1056,7 @@ } return; } -#endif +#endif /* CONFIG_VIDEO_CPIA_PP_DMA */ if (cam->camstate == CPIA_PHASE_ecpread) return; if( cam->camstate != CPIA_PHASE_idle ) DBG("got IRQ(%d) when in %s\n", @@ -1012,6 +1076,7 @@ } return; } +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) */ /**************************************************************************** * @@ -1021,15 +1086,27 @@ static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size) { int retval=0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) + int size_written; +#endif if (packet == NULL) { return -EINVAL; } if (ForwardSetup(cam)) { + DBG("Write failed in setup\n"); return -EIO; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if (SimECPWriteBuffer(cam, packet, size) != size) { retval = -EIO; } +#else + size_written = parport_write(cam->port, packet, size); + if(size_written != size) { + DBG("Write failed, wrote %d/%d\n", size_written, size); + retval = -EIO; + } +#endif EndTransferMode(cam); return retval; } @@ -1048,9 +1125,15 @@ if (ReverseSetup(cam, 0)) { return -EIO; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if (SimECPReadBuffer(cam, packet, size) != size) { retval = -EIO; } +#else + if(parport_read(cam->port, packet, size) != size) { + retval = -EIO; + } +#endif EndTransferMode(cam); return retval; } @@ -1068,7 +1151,6 @@ cam->image_ready=0; //if (ReverseSetup(cam,1)) return -EIO; if(cam->stream_irq) cpia_parport_enable_irq(cam->port); - return 0; } @@ -1089,6 +1171,20 @@ return 0; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) +static int cpia_pp_read(struct parport *port, u8 *buffer, int len) +{ + int bytes_read, new_bytes; + for(bytes_read=0; bytes_read= KERNEL_VERSION(2,3,0)) + int i, endseen, block_size, new_bytes; +#endif + if(cam == NULL) { DBG("Internal driver error: cam is NULL\n"); return -EINVAL; @@ -1120,8 +1220,12 @@ DBG("%d\n", cam->image_ready); } } else { - if (ReverseSetup(cam, 1)) return -EIO; + if (ReverseSetup(cam, 1)) { + DBG("unable to ReverseSetup\n"); + return -EIO; + } } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) read_bytes = ECPReadBuffer(cam, buffer, CPIA_MAX_IMAGE_SIZE); if( 1/*!cam->streaming*/) { EndTransferMode(cam); @@ -1131,6 +1235,36 @@ DBG("incomplete image: %ld / %d / %d\n", jiffies, cam->image_complete, read_bytes); } +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */ + endseen = 0; + block_size = PARPORT_CHUNK_SIZE; + while( !cam->image_complete ) { + if(current->need_resched) schedule(); + + new_bytes = cpia_pp_read(cam->port, buffer, block_size ); + if( new_bytes <= 0 ) { + break; + } + i=-1; + while(++iimage_complete=1; + break; + } + if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) { + block_size=CPIA_MAX_IMAGE_SIZE-read_bytes; + } + } + EndTransferMode(cam); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */ return cam->image_complete ? read_bytes : -EIO; } @@ -1145,6 +1279,7 @@ int retval=0; int databytes; struct pp_cam_entry *cam = privdata; + if(cam == NULL) { DBG("Internal driver error: cam is NULL\n"); return -EINVAL; @@ -1155,6 +1290,7 @@ } databytes = (((int)command[7])<<8) | command[6]; if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) { + DBG("Error writing command\n"); return err; } if(command[0] == DATA_IN) { @@ -1163,9 +1299,9 @@ DBG("Internal driver error: data is NULL\n"); return -EINVAL; } - err = ReadPacket(cam, buffer, 8); - if(err < 0) { + if((err = ReadPacket(cam, buffer, 8)) < 0) { return err; + DBG("Error reading command result\n"); } memcpy(data, buffer, databytes); } else if(command[0] == DATA_OUT) { @@ -1174,7 +1310,10 @@ DBG("Internal driver error: data is NULL\n"); retval = -EINVAL; } else { - WritePacket(cam, data, databytes); + if((err=WritePacket(cam, data, databytes)) < 0){ + DBG("Error writing command data\n"); + return err; + } } } } else { @@ -1189,29 +1328,19 @@ * cpia_pp_open * ***************************************************************************/ -static int cpia_pp_open(int camnr, void **privdata) +static int cpia_pp_open(void *privdata) { - struct pp_cam_entry *cam=NULL; - int i; - for(i=0; icamnr == camnr) { - DBG("Found camera[%d]\n", camnr); - break; - } - } + struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata; - *privdata = cam; + if (cam == NULL) + return -EINVAL; if(cam->open_count == 0) { if (parport_claim(cam->pdev)) { DBG("failed to claim the port\n"); return -EBUSY; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) parport_write_econtrol(cam->port, PARPORT_MODE_PCECR); parport_frob_econtrol(cam->port, ECR_serviceIntr|ECR_dmaEn|ECR_nErrIntrEn, @@ -1222,6 +1351,15 @@ #endif cam->stream_irq=0; cpia_parport_disable_irq(cam->port); +#else + parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); + parport_data_forward(cam->port); + parport_write_control(cam->port, PARPORT_CONTROL_SELECT); + udelay(50); + parport_write_control(cam->port, + PARPORT_CONTROL_SELECT + | PARPORT_CONTROL_INIT); +#endif } ++cam->open_count; @@ -1266,6 +1404,7 @@ MOD_DEC_USE_COUNT; #endif if (--cam->open_count == 0) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if (cam->port->irq != PARPORT_IRQ_NONE) { cpia_parport_disable_irq(cam->port); } @@ -1278,11 +1417,12 @@ interruptible_sleep_on(&cam->wq_dma); } restore_flags(flags); -#endif +#endif /* CONFIG_VIDEO_CPIA_PP_DMA */ if (waitqueue_active(&cam->wq_stream)) { /* FIXME */ wake_up(&cam->wq_stream); } +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) */ parport_release(cam->pdev); } return 0; @@ -1293,47 +1433,55 @@ * cpia_pp_register * ***************************************************************************/ -static int cpia_pp_register(int nr, struct parport *port) +static int cpia_pp_register(struct parport *port) { struct pardevice *pdev = NULL; - int camnr; + struct pp_cam_entry *cam; + struct cam_data *cpia; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if (!(port->modes & PARPORT_MODE_PCECP)) { +#else + if (!(port->modes & PARPORT_MODE_ECP) && + !(port->modes & PARPORT_MODE_TRISTATE)) { +#endif LOG("port is not ECP capable\n"); return -ENXIO; } - if((camnr = cpia_register_camera(&cpia_pp_ops)) < 0) { - LOG("failed to cpia_register_camera\n"); - return -ENXIO; - } - camera[nr] = kmalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); - if (camera[nr] == NULL) { + cam = kmalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); + if (cam == NULL) { LOG("failed to allocate camera structure\n"); - cpia_unregister_camera(camnr); return -ENOMEM; } - memset(camera[nr],0,sizeof(struct pp_cam_entry)); + memset(cam,0,sizeof(struct pp_cam_entry)); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) + pdev = parport_register_device(port, "cpia_pp", NULL, NULL, + cpia_pp_irq_handler, 0, cam); +#else pdev = parport_register_device(port, "cpia_pp", NULL, NULL, - cpia_pp_irq_handler, 0, camera[nr]); + NULL, 0, cam); +#endif if (!pdev) { LOG("failed to parport_register_device\n"); - cpia_unregister_camera(camnr); - kfree(camera[nr]); + kfree(cam); return -ENXIO; } - camera[nr]->pdev = pdev; - camera[nr]->port = port; - camera[nr]->state = CPIA_FORWARD; - camera[nr]->camstate = CPIA_PHASE_idle; - camera[nr]->camnr = camnr; - init_waitqueue(&camera[nr]->wq_stream); + cam->pdev = pdev; + cam->port = port; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) + cam->state = CPIA_FORWARD; + cam->camstate = CPIA_PHASE_idle; + init_waitqueue(&cam->wq_stream); +#else + init_waitqueue_head(&cam->wq_stream); +#endif - camera[nr]->streaming = 0; - camera[nr]->stream_irq = 0; + cam->streaming = 0; + cam->stream_irq = 0; #ifdef CONFIG_VIDEO_CPIA_PP_DMA if (pdev->port->irq != PARPORT_IRQ_NONE && @@ -1341,66 +1489,151 @@ if(request_dma(pdev->port->dma, "cpia_pp")) { LOG("failed to register dma %d\n", pdev->port->dma); parport_unregister_device(pdev); - cpia_unregister_camera(camnr); - kfree(camera[nr]); + kfree(cam); return -ENXIO; } - camera[nr]->dma_buf=kmalloc(DMA_BUFFER_SIZE, GFP_DMA); - if(camera[nr]->dma_buf == NULL) { - free_dma(camera[nr]->pdev->port->dma); - LOG("failed to allocate dma buffer, using FIFO mode\n"); + cam->dma_buf=kmalloc(DMA_BUFFER_SIZE, GFP_DMA); + if(cam->dma_buf == NULL) { + free_dma(cam->pdev->port->dma); + LOG("failed to allocate dma buffer, using PIO mode\n"); } else { - init_waitqueue(&camera[nr]->wq_dma); + init_waitqueue(&cam->wq_dma); printk(KERN_INFO " using DMA mode (irq %d, DMA %d)\n", pdev->port->irq, pdev->port->dma); } - memset(camera[nr]->dma_buf, 0, DMA_BUFFER_SIZE); + memset(cam->dma_buf, 0, DMA_BUFFER_SIZE); } else { printk(KERN_INFO " using PIO mode\n"); } #endif + if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) { + LOG("failed to cpia_register_camera\n"); +#ifdef CONFIG_VIDEO_CPIA_PP_DMA + if (cam->dma_buf) { + free_dma(cam->pdev->port->dma); + kfree(cam->dma_buf); + } +#endif + parport_unregister_device(pdev); + kfree(cam); + return -ENXIO; + } + ADD_TO_LIST(cam_list, cpia); + return 0; } +static void cpia_pp_detach (struct parport *port) +{ + struct cam_data *cpia; + + for(cpia = cam_list; cpia != NULL; cpia = cpia->next) { + struct pp_cam_entry *cam = cpia->lowlevel_data; + if (cam && cam->port->number == port->number) { + REMOVE_FROM_LIST(cpia); + +#ifdef CONFIG_VIDEO_CPIA_PP_DMA + if (cam->dma_buf) { + free_dma(cam->pdev->port->dma); + kfree(cam->dma_buf); + } +#endif + + cpia_unregister_camera(cpia); + + if(cam->open_count > 0) { + cpia_pp_close(cam); + } + + parport_unregister_device(cam->pdev); + + kfree(cam); + cpia->lowlevel_data = NULL; + break; + } + } +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) +static void cpia_pp_attach (struct parport *port) +{ + unsigned int i; + + switch (parport_nr[0]) + { + case PPCPIA_PARPORT_UNSPEC: + case PPCPIA_PARPORT_AUTO: + if (port->probe_info[0].class != PARPORT_CLASS_MEDIA || + port->probe_info[0].cmdset == NULL || + strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0) + return; + + cpia_pp_register(port); + + break; + + default: + for (i = 0; i < PARPORT_MAX; ++i) { + if (port->number == parport_nr[i]) { + cpia_pp_register(port); + break; + } + } + break; + } +} + +static struct parport_driver cpia_pp_driver = { + "cpia_pp", + cpia_pp_attach, + cpia_pp_detach, + NULL +}; +#endif + int cpia_pp_init(void) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) struct parport *port; int i, count = 0; +#endif printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER); - switch (parport_nr[0]) { - case PPCPIA_PARPORT_OFF: + if(parport_nr[0] == PPCPIA_PARPORT_OFF) { printk(" disabled\n"); return 0; + } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) + switch (parport_nr[0]) { case PPCPIA_PARPORT_UNSPEC: case PPCPIA_PARPORT_AUTO: for (port = parport_enumerate(); port; port = port->next) { #if defined(CONFIG_PNP_PARPORT) || \ (defined(MODULE) && defined(CONFIG_PNP_PARPORT_MODULE)) + if(port->probe_info.model) { if (port->probe_info.class != PARPORT_CLASS_MEDIA || port->probe_info.cmdset == NULL || strncmp(port->probe_info.cmdset, "CPIA_1", 6) != 0){ continue; } + } #endif - - if (cpia_pp_register(count, port) == 0 && - ++count == PP_MAXCAMS) { - break; + if (!cpia_pp_register(port)) { + ++count; } } break; default: - for (i = 0; i < PP_MAXCAMS; i++) { + for (i = 0; i < PARPORT_MAX; i++) { for (port = parport_enumerate(); port; port = port->next) { if (port->number == parport_nr[i]) { - if (!cpia_pp_register(i, port)) { + if (!cpia_pp_register(port)) { count++; } break; @@ -1414,6 +1647,13 @@ if (count == 0) { return -ENODEV; } +#else /* kernel version >= 2.3.0 */ + if (parport_register_driver (&cpia_pp_driver)) { + LOG ("unable to register with parport\n"); + return -EIO; + } +#endif /* kernel version >= 2.3.0 */ + return 0; } @@ -1426,7 +1666,7 @@ parport_nr[0] = PPCPIA_PARPORT_AUTO; } else { int n; - for (n = 0; n < PP_MAXCAMS && parport[n]; n++) { + for (n = 0; n < PARPORT_MAX && parport[n]; n++) { if (!strncmp(parport[n], "none", 4)) { parport_nr[n] = PPCPIA_PARPORT_NONE; } else { @@ -1443,31 +1683,23 @@ } } #if defined(CONFIG_KMOD) && defined(CONFIG_PNP_PARPORT_MODULE) + if(parport_enumerate() && !parport_enumerate()->probe_info.model) { request_module("parport_probe"); + } #endif return cpia_pp_init(); } void cleanup_module(void) { - int i; - for (i = 0; camera[i] != NULL && i < PP_MAXCAMS; i++) { - while(camera[i]->open_count > 0) { - LOG("You forgot to close camera %d, will do it for you\n", camera[i]->camnr); - cpia_pp_close(camera[i]); - } - -#ifdef CONFIG_VIDEO_CPIA_PP_DMA - if (camera[i]->dma_buf) { - free_dma(camera[i]->pdev->port->dma); - kfree(camera[i]->dma_buf); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) + parport_unregister_driver (&cpia_pp_driver); +#else + while(cam_list != NULL) { + struct pp_cam_entry *cam = cam_list->lowlevel_data; + cpia_pp_detach(cam->port); } #endif - - cpia_unregister_camera(camera[i]->camnr); - parport_unregister_device(camera[i]->pdev); - kfree(camera[i]); - } return; } @@ -1482,7 +1714,7 @@ } } else if (!strncmp(str, "parport", 7)) { int n = simple_strtoul(str + 7, NULL, 10); - if (parport_ptr < PP_MAXCAMS) { + if (parport_ptr < PARPORT_MAX) { parport_nr[parport_ptr++] = n; } else { LOG("too many ports, %s ignored.\n", str); @@ -1493,5 +1725,8 @@ parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE; } } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) +__setup("cpia_pp=", cpia_pp_setup); +#endif #endif /* !MODULE */ diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/char/cpia_usb.c linux-2.2.17/drivers/char/cpia_usb.c --- linux-2.2.17-orig/drivers/char/cpia_usb.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/char/cpia_usb.c Sun Sep 24 04:26:22 2000 @@ -0,0 +1,636 @@ +/* + * cpia_usb CPiA USB driver + * + * Supports CPiA based parallel port Video Camera's. + * + * Copyright (C) 1999 Jochen Scharrlach + * Copyright (C) 1999, 2000 Johannes Erdfelt + * + * 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 "cpia.h" + +#define USB_REQ_CPIA_GRAB_FRAME 0xC1 +#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2 +#define WAIT_FOR_NEXT_FRAME 0 +#define FORCE_FRAME_UPLOAD 1 + +#define FRAMES_PER_DESC 10 +#define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */ +#define CPIA_NUMSBUF 2 +#define STREAM_BUF_SIZE (PAGE_SIZE * 4) +#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) + +struct cpia_sbuf { + char *data; + urb_t *urb; +}; + +#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100) +enum framebuf_status { + FRAME_EMPTY, + FRAME_READING, + FRAME_READY, + FRAME_ERROR, +}; + +struct framebuf { + int length; + enum framebuf_status status; + u8 data[FRAMEBUF_LEN]; + struct framebuf *next; +}; + +struct usb_cpia { + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; + wait_queue_head_t wq_stream; + + int cursbuf; /* Current receiving sbuf */ + struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ + + int streaming; + int open; + int present; + struct framebuf *buffers[3]; + struct framebuf *curbuff, *workbuff; +}; + +static int cpia_usb_open(void *privdata); +static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata); +static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data); +static int cpia_usb_streamStart(void *privdata); +static int cpia_usb_streamStop(void *privdata); +static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock); +static int cpia_usb_close(void *privdata); + +#define ABOUT "USB driver for Vision CPiA based cameras" + +static struct cpia_camera_ops cpia_usb_ops = { + cpia_usb_open, + cpia_usb_registerCallback, + cpia_usb_transferCmd, + cpia_usb_streamStart, + cpia_usb_streamStop, + cpia_usb_streamRead, + cpia_usb_close, + 0 +}; + +static struct cam_data *cam_list; + +static void cpia_usb_complete(struct urb *urb) +{ + int i; + char *cdata; + struct usb_cpia *ucpia; + + if (!urb || !urb->context) + return; + + ucpia = (struct usb_cpia *) urb->context; + + if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open) + return; + + if (ucpia->workbuff->status == FRAME_EMPTY) { + ucpia->workbuff->status = FRAME_READING; + ucpia->workbuff->length = 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + if (st) + printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st); + + if (FRAMEBUF_LEN < ucpia->workbuff->length + n) { + printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n); + return; + } + + if (n) { + if ((ucpia->workbuff->length > 0) || + (0x19 == cdata[0] && 0x68 == cdata[1])) { + memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n); + ucpia->workbuff->length += n; + } else + DBG("Ignoring packet!\n"); + } else { + if (ucpia->workbuff->length > 4 && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) { + ucpia->workbuff->status = FRAME_READY; + ucpia->curbuff = ucpia->workbuff; + ucpia->workbuff = ucpia->workbuff->next; + ucpia->workbuff->status = FRAME_EMPTY; + ucpia->workbuff->length = 0; + + if (waitqueue_active(&ucpia->wq_stream)) + wake_up_interruptible(&ucpia->wq_stream); + } + } + } +} + +static int cpia_usb_open(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + urb_t *urb; + int ret, retval = 0, fx, err; + + if (!ucpia) + return -EINVAL; + + ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ucpia->sbuf[0].data) + return -EINVAL; + + ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ucpia->sbuf[1].data) { + retval = -EINVAL; + goto error_0; + } + + ret = usb_set_interface(ucpia->dev, ucpia->iface, 3); + if (ret < 0) { + printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret); + retval = -EBUSY; + goto error_all; + } + + ucpia->buffers[0]->status = FRAME_EMPTY; + ucpia->buffers[0]->length = 0; + ucpia->buffers[1]->status = FRAME_EMPTY; + ucpia->buffers[1]->length = 0; + ucpia->buffers[2]->status = FRAME_EMPTY; + ucpia->buffers[2]->length = 0; + ucpia->curbuff = ucpia->buffers[0]; + ucpia->workbuff = ucpia->buffers[1]; + + /* We double buffer the Iso lists */ + urb = usb_alloc_urb(FRAMES_PER_DESC); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); + retval = -ENOMEM; + goto error_all; + } + + ucpia->sbuf[0].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[0].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + + urb = usb_alloc_urb(FRAMES_PER_DESC); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); + retval = -ENOMEM; + goto error_all; + } + + ucpia->sbuf[1].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[1].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + + ucpia->sbuf[1].urb->next = ucpia->sbuf[0].urb; + ucpia->sbuf[0].urb->next = ucpia->sbuf[1].urb; + + err = usb_submit_urb(ucpia->sbuf[0].urb); + if (err) + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n", + err); + err = usb_submit_urb(ucpia->sbuf[1].urb); + if (err) + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n", + err); + + ucpia->streaming = 1; + ucpia->open = 1; + + return 0; + +error_all: + kfree (ucpia->sbuf[1].data); +error_0: + kfree (ucpia->sbuf[0].data); + + return retval; +} + +// +// convenience functions +// + +/**************************************************************************** + * + * WritePacket + * + ***************************************************************************/ +static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size) +{ + if (!packet) + return -EINVAL; + + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + packet[1] + (packet[0] << 8), + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + packet[2] + (packet[3] << 8), + packet[4] + (packet[5] << 8), buf, size, HZ); +} + +/**************************************************************************** + * + * ReadPacket + * + ***************************************************************************/ +static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size) +{ + if (!packet || size <= 0) + return -EINVAL; + + return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + packet[1] + (packet[0] << 8), + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + packet[2] + (packet[3] << 8), + packet[4] + (packet[5] << 8), buf, size, HZ); +} + +static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data) +{ + int err = 0; + int databytes; + struct usb_cpia *ucpia = (struct usb_cpia *)privdata; + struct usb_device *udev = ucpia->dev; + + if (!udev) { + DBG("Internal driver error: udev is NULL\n"); + return -EINVAL; + } + + if (!command) { + DBG("Internal driver error: command is NULL\n"); + return -EINVAL; + } + + databytes = (((int)command[7])<<8) | command[6]; + + if (command[0] == DATA_IN) { + u8 buffer[8]; + + if (!data) { + DBG("Internal driver error: data is NULL\n"); + return -EINVAL; + } + + err = ReadPacket(udev, command, buffer, 8); + if (err < 0) + return err; + + memcpy(data, buffer, databytes); + } else if(command[0] == DATA_OUT) + WritePacket(udev, command, data, databytes); + else { + DBG("Unexpected first byte of command: %x\n", command[0]); + err = -EINVAL; + } + + return 0; +} + +static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamStart(void *privdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamStop(void *privdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + struct framebuf *mybuff; + + if (!ucpia || !ucpia->present) + return -1; + + if (ucpia->curbuff->status != FRAME_READY) + interruptible_sleep_on(&ucpia->wq_stream); + else + DBG("Frame already waiting!\n"); + + mybuff = ucpia->curbuff; + + if (!mybuff) + return -1; + + if (mybuff->status != FRAME_READY || mybuff->length < 4) { + DBG("Something went wrong!\n"); + return -1; + } + + memcpy(frame, mybuff->data, mybuff->length); + mybuff->status = FRAME_EMPTY; + +/* DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n", */ +/* mybuff->length, frame[0], frame[1], */ +/* frame[mybuff->length-4], frame[mybuff->length-3], */ +/* frame[mybuff->length-2], frame[mybuff->length-1]); */ + + return mybuff->length; +} + +static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try) +{ + if (!ucpia->streaming) + return; + + ucpia->streaming = 0; + + /* Set packet size to 0 */ + if (try) { + int ret; + + ret = usb_set_interface(ucpia->dev, ucpia->iface, 0); + if (ret < 0) { + printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret); + return; + } + } + + /* Unschedule all of the iso td's */ + if (ucpia->sbuf[1].urb) { + usb_unlink_urb(ucpia->sbuf[1].urb); + usb_free_urb(ucpia->sbuf[1].urb); + ucpia->sbuf[1].urb = NULL; + } + + if (ucpia->sbuf[0].urb) { + usb_unlink_urb(ucpia->sbuf[0].urb); + usb_free_urb(ucpia->sbuf[0].urb); + ucpia->sbuf[0].urb = NULL; + } +} + +static int cpia_usb_close(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + + ucpia->open = 0; + + cpia_usb_free_resources(ucpia, 1); + + if (!ucpia->present) + kfree(ucpia); + + return 0; +} + +int cpia_usb_init(void) +{ + /* return -ENODEV; */ + return 0; +} + +/* Probing and initializing */ + +static void *cpia_probe(struct usb_device *udev, unsigned int ifnum) +{ + struct usb_interface_descriptor *interface; + struct usb_cpia *ucpia; + struct cam_data *cam; + int ret; + + /* A multi-config CPiA camera? */ + if (udev->descriptor.bNumConfigurations != 1) + return NULL; + + interface = &udev->actconfig->interface[ifnum].altsetting[0]; + + /* Is it a CPiA? */ + if (!((udev->descriptor.idVendor == 0x0553 && + udev->descriptor.idProduct == 0x0002) || + (udev->descriptor.idVendor == 0x0813 && + udev->descriptor.idProduct == 0x0001))) /* GA 04/14/00 */ + return NULL; + + /* We found a CPiA */ + printk(KERN_INFO "USB CPiA camera found\n"); + + ucpia = kmalloc(sizeof(*ucpia), GFP_KERNEL); + if (!ucpia) { + printk(KERN_ERR "couldn't kmalloc cpia struct\n"); + return NULL; + } + + memset(ucpia, 0, sizeof(*ucpia)); + + ucpia->dev = udev; + ucpia->iface = interface->bInterfaceNumber; + init_waitqueue_head(&ucpia->wq_stream); + + ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0])); + if (!ucpia->buffers[0]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 0\n"); + goto fail_alloc_0; + } + + ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1])); + if (!ucpia->buffers[1]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 1\n"); + goto fail_alloc_1; + } + + ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2])); + if (!ucpia->buffers[2]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 2\n"); + goto fail_alloc_2; + } + + ucpia->buffers[0]->next = ucpia->buffers[1]; + ucpia->buffers[1]->next = ucpia->buffers[2]; + ucpia->buffers[2]->next = ucpia->buffers[0]; + + ret = usb_set_interface(udev, ucpia->iface, 0); + if (ret < 0) { + printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret); + /* goto fail_all; */ + } + + /* Before register_camera, important */ + ucpia->present = 1; + + cam = cpia_register_camera(&cpia_usb_ops, ucpia); + if (!cam) { + LOG("failed to cpia_register_camera\n"); + goto fail_all; + } + + ADD_TO_LIST(cam_list, cam); + + return cam; + +fail_all: + vfree(ucpia->buffers[2]); + ucpia->buffers[2] = NULL; +fail_alloc_2: + vfree(ucpia->buffers[1]); + ucpia->buffers[1] = NULL; +fail_alloc_1: + vfree(ucpia->buffers[0]); + ucpia->buffers[0] = NULL; +fail_alloc_0: + + return NULL; +} + +static void cpia_disconnect(struct usb_device *dev, void *ptr); + +static struct usb_driver cpia_driver = { + "cpia", + cpia_probe, + cpia_disconnect, + { NULL, NULL } +}; + +/* don't use dev, it may be NULL! (see usb_cpia_cleanup) */ +/* _disconnect from usb_cpia_cleanup is not necessary since usb_deregister */ +/* will do it for us as well as passing a udev structure - jerdfelt */ +static void cpia_disconnect(struct usb_device *udev, void *ptr) +{ + struct cam_data *cam = (struct cam_data *) ptr; + struct usb_cpia *ucpia = (struct usb_cpia *) cam->lowlevel_data; + + REMOVE_FROM_LIST(cam); + + /* Don't even try to reset the altsetting if we're disconnected */ + cpia_usb_free_resources(ucpia, 0); + + ucpia->present = 0; + + cpia_unregister_camera(cam); + + ucpia->curbuff->status = FRAME_ERROR; + + if (waitqueue_active(&ucpia->wq_stream)) + wake_up_interruptible(&ucpia->wq_stream); + + usb_driver_release_interface(&cpia_driver, + &udev->actconfig->interface[0]); + + ucpia->curbuff = ucpia->workbuff = NULL; + + if (ucpia->buffers[2]) { + vfree(ucpia->buffers[2]); + ucpia->buffers[2] = NULL; + } + + if (ucpia->buffers[1]) { + vfree(ucpia->buffers[1]); + ucpia->buffers[1] = NULL; + } + + if (ucpia->buffers[0]) { + vfree(ucpia->buffers[0]); + ucpia->buffers[0] = NULL; + } + + if (!ucpia->open) + kfree(ucpia); +} + +int usb_cpia_init(void) +{ + cam_list = NULL; + + return usb_register(&cpia_driver); +} + +void usb_cpia_cleanup(void) +{ +/* + struct cam_data *cam; + + while ((cam = cam_list) != NULL) + cpia_disconnect(NULL, cam); +*/ + + usb_deregister(&cpia_driver); +} + +#ifdef MODULE +int init_module(void) +{ + return usb_cpia_init(); +} + +void cleanup_module(void) +{ + usb_cpia_cleanup(); +} + +#else /* !MODULE */ + +__initfunc(void cpia_usb_setup(char *str, int *ints)) +{ +} + +#endif /* !MODULE */ diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/char/keyboard.c linux-2.2.17/drivers/char/keyboard.c --- linux-2.2.17-orig/drivers/char/keyboard.c Mon Aug 9 12:05:01 1999 +++ linux-2.2.17/drivers/char/keyboard.c Sun Sep 24 04:26:22 2000 @@ -61,7 +61,9 @@ #define KBD_DEFLOCK 0 #endif +void (*kbd_ledfunc)(unsigned int led) = NULL; EXPORT_SYMBOL(handle_scancode); +EXPORT_SYMBOL(kbd_ledfunc); extern void ctrl_alt_del(void); @@ -905,6 +907,7 @@ if (leds != ledstate) { ledstate = leds; kbd_leds(leds); + if (kbd_ledfunc) kbd_ledfunc(leds); } } diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/char/mem.c linux-2.2.17/drivers/char/mem.c --- linux-2.2.17-orig/drivers/char/mem.c Tue Jan 4 10:12:14 2000 +++ linux-2.2.17/drivers/char/mem.c Sun Sep 24 04:26:22 2000 @@ -55,14 +55,8 @@ #if defined(CONFIG_PPC) || defined(CONFIG_MAC) extern void adbdev_init(void); #endif -#ifdef CONFIG_USB_UHCI -int uhci_init(void); -#endif -#ifdef CONFIG_USB_OHCI -int ohci_init(void); -#endif -#ifdef CONFIG_USB_OHCI_HCD -int ohci_hcd_init(void); +#ifdef CONFIG_USB +int usb_init(void); #endif static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp, @@ -621,15 +615,7 @@ printk("unable to get major %d for memory devs\n", MEM_MAJOR); rand_initialize(); #ifdef CONFIG_USB -#ifdef CONFIG_USB_UHCI - uhci_init(); -#endif -#ifdef CONFIG_USB_OHCI - ohci_init(); -#endif -#ifdef CONFIG_USB_OHCI_HCD - ohci_hcd_init(); -#endif + usb_init(); #endif #if defined (CONFIG_FB) fbmem_init(); diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/char/pc_keyb.c linux-2.2.17/drivers/char/pc_keyb.c --- linux-2.2.17-orig/drivers/char/pc_keyb.c Mon Sep 4 10:39:17 2000 +++ linux-2.2.17/drivers/char/pc_keyb.c Sun Sep 24 04:26:22 2000 @@ -428,6 +428,8 @@ #endif } +static unsigned char kbd_exists = 1; + /* * This reads the keyboard status port, and does the * appropriate action. @@ -450,6 +452,7 @@ if (status & KBD_STAT_MOUSE_OBF) { handle_mouse_event(scancode); } else { + kbd_exists = 1; if (do_acknowledge(scancode)) handle_scancode(scancode, !(scancode & 0x80)); mark_bh(KEYBOARD_BH); @@ -506,7 +509,7 @@ mdelay(1); if (!--timeout) { #ifdef KBD_REPORT_TIMEOUTS - printk(KERN_WARNING "Keyboard timeout[2]\n"); + printk(KERN_WARNING "keyboard: Timeout - AT keyboard not present?\n"); #endif return 0; } @@ -520,8 +523,10 @@ void pckbd_leds(unsigned char leds) { - if (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds)) + if (kbd_exists && (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))) { send_data(KBD_CMD_ENABLE); /* re-enable kbd if any errors */ + kbd_exists = 0; + } } /* diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/CREDITS linux-2.2.17/drivers/usb/CREDITS --- linux-2.2.17-orig/drivers/usb/CREDITS Tue May 11 10:04:03 1999 +++ linux-2.2.17/drivers/usb/CREDITS Wed Dec 31 16:00:00 1969 @@ -1,154 +0,0 @@ -Credits for the Simple Linux USB Driver: - -The following people have contributed to this code (in alphabetical -order by last name). I'm sure this list should be longer, its -difficult to maintain, add yourself with a patch if desired. - - Alan Cox - Johannes Erdfelt - ham - Bradley M Keryan - Vojtech Pavlik - Gregory P. Smith - Linus Torvalds - Roman Weissgaerber - - -Special thanks to: - - Inaky Perez Gonzalez for starting the - Linux USB driver effort and writing much of the larger uusbd driver. - Much has been learned from that effort. - - The NetBSD & FreeBSD USB developers. For being on the Linux USB list - and offering suggestions and sharing implementation experiences. - -Additional thanks to the following companies and people for donations -of hardware, support, time and development (this is from the original -THANKS file in Inaky's driver): - - The following corporations have helped us in the development -of Linux USB / UUSBD: - - - USAR Systems provided us with one of their excellent USB - Evaluation Kits. It allows us to test the Linux-USB driver - for compilance with the latest USB specification. USAR - Systems recognized the importance of an up-to-date open - Operating System and supports this project with - Hardware. Thanks!. - - - Thanks to Intel Corporation for their precious help. - - - We teamed up with Cherry to make Linux the first OS with - built-in USB support. Cherry is one of the biggest keyboard - makers in the world. - - - CMD Technology, Inc. sponsored us kindly donating a CSA-6700 - PCI-to-USB Controller Board to test the OHCI implementation. - - - Due to their support to us, Keytronic can be sure that they - will sell keyboards to some of the 3 million (at least) - Linux users. - - - Many thanks to ing büro h doran [http://www.ibhdoran.com]! - It was almost imposible to get a PC backplate USB connector - for the motherboard here at Europe (mine, home-made, was - quite lowsy :). Now I know where to adquire nice USB stuff! - - - Genius Germany donated a USB mouse to test the mouse boot - protocol. They've also donated a F-23 digital joystick and a - NetMouse Pro. Thanks! - - - AVM GmbH Berlin is supporting the development of the Linux - USB driver for the AVM ISDN Controller B1 USB. AVM is a - leading manufacturer for active and passive ISDN Controllers - and CAPI 2.0-based software. The active design of the AVM B1 - is open for all OS platforms, including Linux. - - - Thanks to Y-E Data, Inc. for donating their FlashBuster-U - USB Floppy Disk Drive, so we could test the bulk transfer - code. - - - Many thanks to Logitech for contributing a three axis USB - mouse. - - Logitech designs, manufactures and markets - Human Interface Devices, having a long history and - experience in making devices such as keyboards, mice, - trackballs, cameras, loudspeakers and control devices for - gaming and professional use. - - Being a recognized vendor and seller for all these devices, - they have donated USB mice, a joystick and a scanner, as a - way to acknowledge the importance of Linux and to allow - Logitech customers to enjoy support in their favorite - operating systems and all Linux users to use Logitech and - other USB hardware. - - Logitech is official sponsor of the Linux Conference on - Feb. 11th 1999 in Vienna, where we'll will present the - current state of the Linux USB effort. - - - CATC has provided means to uncover dark corners of the UHCI - inner workings with a USB Inspector. - - - Thanks to Entrega for providing PCI to USB cards, hubs and - converter products for development. - - - And thanks go to (hey! in no particular order :) - - - Oren Tirosh , for standing so patiently - all my doubts'bout USB and giving lots of cool ideas. - - - Jochen Karrer , for - pointing out mortal bugs and giving advice. - - - Edmund Humemberger , for it's great work on - public relationships and general management stuff for the - Linux-USB effort. - - - Alberto Menegazzi is starting the - documentation for the UUSBD. Go for it! - - - Ric Klaren for doing nice - introductory documents (compiting with Alberto's :). - - - Christian Groessler , for it's help on those - itchy bits ... :) - - - Paul MacKerras for polishing OHCI and pushing me harder for - the iMac support, giving improvements and enhancements. - - - Fernando Herrera has taken - charge of composing, maintaining and feeding the - long-awaited, unique and marvelous UUSBD FAQ! Tadaaaa!!! - - - Rasca Gmelch has revived the raw driver and - pointed bugs, as well as started the uusbd-utils package. - - - Peter Dettori is unconvering bugs like - crazy, as well as making cool suggestions, great :) - - - All the Free Software and Linux community, the FSF & the GNU - project, the MIT X consortium, the TeX people ... everyone! - You know who you are! - - - Big thanks to Richard Stallman for creating Emacs! - - - The people at the linux-usb mailing list, for reading so - many messages :) Ok, no more kidding; for all your advices! - - - All the people at the USB Implementors Forum for their - help and assistance. - - - Nathan Myers , for his advice! (hope you - liked Cibeles' party). - - - Linus Torvalds, for starting, developing and managing Linux. - - - Mike Smith, Craig Keithley, Thierry Giron and Janet Schank - for convincing me USB Standard hubs are not that standard - and that's good to allow for vendor specific quirks on the - standard hub driver. - diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/Config.in linux-2.2.17/drivers/usb/Config.in --- linux-2.2.17-orig/drivers/usb/Config.in Mon May 10 10:18:34 1999 +++ linux-2.2.17/drivers/usb/Config.in Sun Sep 24 04:26:22 2000 @@ -1,30 +1,84 @@ # # USB device configuration # -# NOTE NOTE NOTE! This is still considered extremely experimental. -# Right now hubs, mice and keyboards work - at least with UHCI. -# But that may be more a lucky coincidence than anything else.. -# -# This was all developed modularly, but I've been lazy in cleaning -# it up, so right now they are all bools. -# mainmenu_option next_comment -comment 'USB drivers - not for the faint of heart' +comment 'USB support' -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Support for USB (EXPERIMENTAL!)' CONFIG_USB - if [ ! "$CONFIG_USB" = "n" ]; then - bool 'UHCI (intel PIIX4 and others) support?' CONFIG_USB_UHCI - bool 'OHCI (compaq and some others) support?' CONFIG_USB_OHCI - bool 'OHCI-HCD (other OHCI opt. Virt. Root Hub) support?' CONFIG_USB_OHCI_HCD - if [ "$CONFIG_USB_OHCI_HCD" = "y" ]; then - bool 'OHCI-HCD Virtual Root Hub' CONFIG_USB_OHCI_VROOTHUB +tristate 'Support for USB' CONFIG_USB +if [ ! "$CONFIG_USB" = "n" ]; then + bool ' USB verbose debug messages' CONFIG_USB_DEBUG + +comment 'Miscellaneous USB options' + bool ' Preliminary USB device filesystem' CONFIG_USB_DEVICEFS + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' Enforce USB bandwidth allocation (EXPERIMENTAL)' CONFIG_USB_BANDWIDTH + else + define_bool CONFIG_USB_BANDWIDTH n + fi + +comment 'USB Controllers' + if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then + dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB fi + if [ "$CONFIG_USB_UHCI" != "y" ]; then + dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB + fi + dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB - bool 'USB mouse support' CONFIG_USB_MOUSE - bool 'USB keyboard support' CONFIG_USB_KBD - bool 'USB audio parsing support' CONFIG_USB_AUDIO +comment 'USB Devices' + dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB + dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB + dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND + dep_tristate ' USB Modem (CDC ACM) support' CONFIG_USB_ACM $CONFIG_USB + dep_tristate ' USB Serial Converter support' CONFIG_USB_SERIAL $CONFIG_USB + if [ "$CONFIG_USB_SERIAL" != "n" ]; then + bool ' USB Generic Serial Driver' CONFIG_USB_SERIAL_GENERIC + bool ' USB Handspring Visor Driver' CONFIG_USB_SERIAL_VISOR + if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then + bool ' USB ConnectTech WhiteHEAT Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_WHITEHEAT + bool ' USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO + bool ' USB Keyspan PDA Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN_PDA + bool ' USB Digi International AccelePort USB Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_DIGI_ACCELEPORT + bool ' USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_OMNINET + fi + bool ' USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG + fi + dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB + dep_tristate ' USB Mass Storage support (EXPERIMENTAL)' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI + if [ "$CONFIG_USB_STORAGE" != "n" ]; then + bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG + fi + fi +# dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT + dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' PLUSB Prolific USB-Network driver (EXPERIMENTAL)' CONFIG_USB_PLUSB $CONFIG_USB $CONFIG_NET + dep_tristate ' USB ADMtek Pegasus-based device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET + dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB + dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' Microtek X6USB scanner support (EXPERIMENTAL)' CONFIG_USB_MICROTEK $CONFIG_SCSI + fi + +comment 'USB HID' + dep_tristate ' USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB + if [ "$CONFIG_USB_HID" != "y" ]; then + dep_tristate ' USB HIDBP Keyboard support' CONFIG_USB_KBD $CONFIG_USB + dep_tristate ' USB HIDBP Mouse support' CONFIG_USB_MOUSE $CONFIG_USB + fi + dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB + dep_tristate ' Logitech WingMan Force joystick support' CONFIG_USB_WMFORCE $CONFIG_USB + dep_tristate ' Keyboard support' CONFIG_INPUT_KEYBDEV $CONFIG_USB + dep_tristate ' Mouse support' CONFIG_INPUT_MOUSEDEV $CONFIG_USB + if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then + int ' Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024 + int ' Vertical screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768 fi + dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_USB + dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV $CONFIG_USB fi endmenu diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/Makefile linux-2.2.17/drivers/usb/Makefile --- linux-2.2.17-orig/drivers/usb/Makefile Mon May 10 10:18:34 1999 +++ linux-2.2.17/drivers/usb/Makefile Sun Sep 24 04:26:22 2000 @@ -1,88 +1,121 @@ # -# Makefile for the kernel usb device drivers. +# Makefile for the kernel USB device drivers. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. -# -# This isn't actually supported yet. Don't try to use it. + +# Subdirs. SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) +MOD_IN_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) serial + +# The target object and module list name. -L_TARGET := usb.a +O_TARGET := usbdrv.o M_OBJS := -L_OBJS := -LX_OBJS := -USBX_OBJS := usb.o hub.o usb-debug.o +O_OBJS := +MOD_LIST_NAME := USB_MODULES -ifeq ($(CONFIG_USB_MOUSE),y) - USBX_OBJS += mouse.o -endif +# Objects that export symbols. -ifeq ($(CONFIG_USB_KBD),y) - USBX_OBJS += keyboard.o keymap.o -endif +export-objs := usb.o input.o -ifeq ($(CONFIG_USB_AUDIO),y) - USBX_OBJS += audio.o -endif +# Multipart objects. -ifeq ($(CONFIG_USB), y) - L_OBJS += $(USBX_OBJS) -endif +list-multi := usbcore.o +usbcore-objs := usb.o usb-debug.o usb-core.o hub.o -ifeq ($(CONFIG_USB_UHCI),y) - ifeq ($(CONFIG_USB), y) - L_OBJS += uhci.o uhci-debug.o - else - ifeq ($(CONFIG_USB),m) - M_OBJS += usb-uhci.o - MIX_OBJS += $(USBX_OBJS) - endif - endif -endif +# Optional parts of multipart objects. -ifeq ($(CONFIG_USB_OHCI),y) - ifeq ($(CONFIG_USB), y) - L_OBJS += ohci.o ohci-debug.o - else - ifeq ($(CONFIG_USB),m) - USBO_OBJS += ohci.o ohci-debug.o - M_OBJS += usb-ohci.o - MIX_OBJS += $(USBX_OBJS) - endif - endif +ifeq ($(CONFIG_USB_DEVICEFS),y) + usbcore-objs += devio.o inode.o drivers.o devices.o endif -ifeq ($(CONFIG_USB_OHCI_HCD),y) - ifeq ($(CONFIG_USB), y) - L_OBJS += ohci-hcd.o ohci-root-hub.o - else - ifeq ($(CONFIG_USB),m) - USBO_OBJS += ohci-hcd.o ohci-root-hub.o - M_OBJS += usb-ohci-hcd.o - MIX_OBJS += $(USBX_OBJS) - endif +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + +# Object files in subdirectories + +ifeq ($(CONFIG_USB_SERIAL),y) + SUB_DIRS += serial + obj-y += serial/usb-serial.o +else + ifeq ($(CONFIG_USB_SERIAL),m) + MOD_IN_SUB_DIRS += serial endif endif -include $(TOPDIR)/Rules.make -keymap.o: keymap.c -keymap.c: maps/serial.map maps/usb.map maps/fixup.map - ./mkmap > $@ +# Each configuration option enables a list of files. + +obj-$(CONFIG_USB) += usbcore.o +obj-$(CONFIG_USB_UHCI) += usb-uhci.o +obj-$(CONFIG_USB_UHCI_ALT) += uhci.o +obj-$(CONFIG_USB_OHCI) += usb-ohci.o + +obj-$(CONFIG_USB_MOUSE) += usbmouse.o input.o +obj-$(CONFIG_USB_HID) += hid.o input.o +obj-$(CONFIG_USB_KBD) += usbkbd.o input.o +obj-$(CONFIG_USB_WACOM) += wacom.o input.o +obj-$(CONFIG_USB_WMFORCE) += wmforce.o input.o +obj-$(CONFIG_INPUT_KEYBDEV) += keybdev.o input.o +obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o input.o +obj-$(CONFIG_INPUT_JOYDEV) += joydev.o input.o +obj-$(CONFIG_INPUT_EVDEV) += evdev.o input.o + +obj-$(CONFIG_USB_SCANNER) += scanner.o +obj-$(CONFIG_USB_ACM) += acm.o +obj-$(CONFIG_USB_PRINTER) += printer.o +obj-$(CONFIG_USB_AUDIO) += audio.o +obj-$(CONFIG_USB_IBMCAM) += ibmcam.o +obj-$(CONFIG_USB_DC2XX) += dc2xx.o +obj-$(CONFIG_USB_MDC800) += mdc800.o +obj-$(CONFIG_USB_STORAGE) += usb-storage.o +obj-$(CONFIG_USB_USS720) += uss720.o +obj-$(CONFIG_USB_DABUSB) += dabusb.o +obj-$(CONFIG_USB_PLUSB) += plusb.o +obj-$(CONFIG_USB_OV511) += ov511.o +obj-$(CONFIG_USB_PEGASUS) += pegasus.o +obj-$(CONFIG_USB_RIO500) += rio500.o +obj-$(CONFIG_USB_DSBR) += dsbr100.o +obj-$(CONFIG_USB_MICROTEK) += microtek.o + +# Extract lists of the multi-part drivers. +# The 'int-*' lists are the intermediate files used to build the multi's. + +multi-y := $(filter $(list-multi), $(obj-y)) +multi-m := $(filter $(list-multi), $(obj-m)) +int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) +int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) + +# Files that are both resident and modular: remove from modular. -usb-uhci.o: uhci.o uhci-debug.o $(USBX_OBJS) - $(LD) $(LD_RFLAG) -r -o $@ uhci.o uhci-debug.o $(USBX_OBJS) +obj-m := $(filter-out $(obj-y), $(obj-m)) +int-m := $(filter-out $(int-y), $(int-m)) + +# Take multi-part drivers out of obj-y and put components in. + +obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y) + +# Translate to Rules.make lists. + +O_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) +OX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) +MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) +MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) + +# The global Rules.make. + +include $(TOPDIR)/Rules.make -usb-ohci.o: ohci.o ohci-debug.o $(USBX_OBJS) - $(LD) $(LD_RFLAG) -r -o $@ ohci.o ohci-debug.o $(USBX_OBJS) +# Link rules for multi-part drivers. -usb-ohci-hcd.o: ohci-hcd.o ohci-root-hub.o $(USBX_OBJS) - $(LD) $(LD_RFLAG) -r -o $@ ohci-hcd.o ohci-root-hub.o $(USBX_OBJS) +usbcore.o: $(usbcore-objs) + $(LD) -r -o $@ $(usbcore-objs) diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/README.kbd linux-2.2.17/drivers/usb/README.kbd --- linux-2.2.17-orig/drivers/usb/README.kbd Wed Apr 21 10:29:09 1999 +++ linux-2.2.17/drivers/usb/README.kbd Wed Dec 31 16:00:00 1969 @@ -1,65 +0,0 @@ -This is a simple USB keyboard driver written from Linus' -USB driver (started with Greg's usb-0.03b.tar.gz source -tree) - -It works fine with my BTC keyboard but I'm still investigating -trouble with my MS keyboard (trouble starts with an inability -to set into boot protocol mode, though, this very well could -be all due to crappy hardware). - -Anyway, I would appreciate you taking a look if you have -any USB keyboards lying around. Oh also, I'm doing this on -UHCI so sorry if it breaks with OHCI. - --ham - - - -Keyboard patch --------------- - -Instead of using the multiple keyboard patch and then running into all -of the kernel version problems that the current Linux-USB project has -had, I'm just mapping the USB keycodes to the standard AT-101 keycodes -and sending them directly to "handle_scancode". - -This may or may not be considered a hack. Anyway it is effective, and -I think safe, and allows USB keyboards to coexist with a serial -keyboard (oh yeah, one side effect is that you can for example hold -down the control key on the serial keyboard and press "a" on the USB -keyboard and you get Control-a like with Windows USB) and works -fine for console and X. - -You do need to make a *tiny* patch the kernel source tree so that the -function "handle_scancode" is exported from keyboard.c though. - - $ cd /usr/src/linux - $ patch -p0 < kbd.patch - -And, of course, then, you need to rebuild and install the kernel. - -** [Vojtech]: Alternately, just 'insmod kbd-stub', if you don't want -to use the keyboard and are too lazy to patch the kernel. - -Keyboard map ------------- - -I'm including a stupid utility "mkmap" which generates the USB->serial -keymap. It takes in maps/serial.map (the current serial keymap, -generated by "dumpkeys"), maps/usb.map (the USB keymap), and -maps/fixup.map (fixes for e0 keys and misc.) and spits out keymap.c -Anyway, it is not beautiful but should serve its purpose for the -moment. - -Other changes -------------- -uhci.c: - * added a context value to the irq callback function - (this is exactly like the "dev_id" field to request_irq) - * played with uhci_reset_port to get better hot-plug results - (eg. do a wait_ms(200) before calling uhci_reset_port) -usb.c: - * disconnect all devices after uhci-control thread is killed - * skip over the HID descriptor - * disconnect the high-level driver in usb_disconnect - diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/README.ohci linux-2.2.17/drivers/usb/README.ohci --- linux-2.2.17-orig/drivers/usb/README.ohci Tue May 11 10:04:03 1999 +++ linux-2.2.17/drivers/usb/README.ohci Wed Dec 31 16:00:00 1969 @@ -1,26 +0,0 @@ -May 09, 1999 16:25:58 - -Cool, things are working "well" now. (I'm not getting oops's from the -OHCI code anyways.. ;). I can attach a usb hub and mouse in any -possible arrangement of the two and they get configured properly. - -You can see that the mouse Interrupt transfers are occuring and being -acknowledged because /proc/interrupts usb-ohci goes up accordingly with -mouse movements/events. That means the TD at least returns some data -and requeues itself. - -Device attach/detach from the root hub is not working well. Currently -every interrupt checks for root hub status changes and frame number -overflow interrupts are enabled. This means you shouldn't have to -wait more than 32-33 seconds for the change to occur, less if there is -other activity. (due to checking in the WDH caused interrupts) -My OHCI controller [SiS 5598 motherboard] doesn't seem to play well -with the RHSC interrupt so it has been disabled. The ohci_timer -should be polling but it not currently working, I haven't had time to -look into that problem. - -However, when I tried telling X to use /dev/psaux for the mouse my -machine locked up... - -- greg@electricrain.com - diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/README.ohci_hcd linux-2.2.17/drivers/usb/README.ohci_hcd --- linux-2.2.17-orig/drivers/usb/README.ohci_hcd Mon May 10 10:18:34 1999 +++ linux-2.2.17/drivers/usb/README.ohci_hcd Wed Dec 31 16:00:00 1969 @@ -1,112 +0,0 @@ - -The OHCI HCD layer is a simple but nearly complete implementation of what the -USB people would call a HCD for the OHCI. - (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled) -It is based on Linus Torvalds UHCI code and Gregory Smith OHCI fragments (0.03 source tree). -The layer (functions) on top of it, is for interfacing to the alternate-usb device-drivers. - -- Roman Weissgaerber - - - * v2.1 1999/05/09 ep_addr correction, code cleanup - * v0.2.0 1999/05/04 - * everything has been moved into 2 files (ohci-hcd.c, ohci-hub-root.c and headers) - * virtual root hub is now an option, - * memory allocation based on kmalloc and kfree now, simple Bus error handling, - * INT and CTRL transfers enabled, Bulk included but disabled, ISO needs completion - * - * from Linus Torvalds (uhci.c): APM (not tested); hub, usb_device, bus and related stuff - * from Greg Smith (ohci.c): better reset ohci-controller handling, hub - * - * v0.1.0 1999/04/27 initial release - -to remove the module try: -killall root-hub -: -rmmod usb-ohci-hcd - -Features: -- virtual root hub, all basic hub descriptors and commands (state: complete) - this is an option now (v0.2.0) - #define CONFIG_USB_OHCI_VROOTHUB includes the virtual hub code, (VROOTHUB) - default is without. - (at the moment: the Virtual Root Hub option is not recommended) - - files: ohci-root-hub.c, ohci-root-hub.h - - -- Endpoint Descriptor (ED) handling more static approach - (EDs should be allocated in parallel to the SET CONFIGURATION command and they live - as long as the function (device) is alive or another configuration is choosen. - In the HCD layer the EDs has to be allocated manually either by calling a subroutine - or by sending a USB root hub vendor specific command to the virtual root hub. - At the alternate linux usb stack EDs will be added (allocated) at their first use. - - files: ohci-hcd.c ohci-hcd.h - routines: (do not use for drivers, use the top layer alternate usb commands instead) - - int usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr1, - int interval, int load, f_handler handler, int ep_size, int speed) - adds an endpoint, (if the endpoint already exists some parameters will be updated) - - int usb_ohci_rm_ep(struct usb_ohci_ed *ed, struct ohci * ohci) - removes an endpoint and all pending TDs of that EP - - usb_ohci_rm_function( struct ohci * ohci, union ep_addr_ ep_addr) - removes all Endpoints of a function (device) - -- Transfer Descriptors (TD): handling and allocation of TDs is transparent to the upper layers - The HCD takes care of TDs and EDs memory allocation whereas the upper layers (UBSD ...) has - to take care of buffer allocation. - files: ohci-hcd.c ohci-hcd.h - - There is one basic command for all types of bus transfers (INT, BULK, ISO, CTRL): - - int ohci_trans_req(struct ohci * ohci, int ep_addr, int ctrl_len, void *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1) - - CTRL: ctrl, ctrl_len ... cmd buffer - data, data_len ... data buffer (in or out) - INT, BULK: ctrl = NULL, ctrl_len=0, - data, data_len ... data buffer (in or out) - ISO: tbd - - There is no buffer reinsertion done by the internal HCD function. - (The interface layer does this for a INT-pipe on request.) - If you want a transfer then you have to - provide buffers by sending ohci_trans_req requests. As they are queued as TDs on an ED - you can send as many as you like. They should come back by the callback f_handler in - the same order (for each endpoint, not globally) If an error occurs all - queued transfers of an endpoint will return unsent. They will be marked with an error status. - - e.g double-buffering for int transfers: - - ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0) - ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0) - - and when a data0 packet returns by the callback f_handler requeue it: - ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0) - and when a data1 packet returns by the callback f_handler requeue it: - ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0) - - lw0, lw1 are private fields for upper layers for ids or fine grained handlers. - The alternate usb uses them for dev_id and usb_device_irq handler. - - -- Done list handling: returns the requests (callback f_handler in ED) and does - some error handling, root-hub request dequeuing - (files: ohci-done-list.c in ohci-hcd.c now(v0.2.0)) - -ep_addr union or int is for addressing devices&endpoints: -__u8 ep_addr.bep.ep ... bit 3..0 endpoint address - bit 4 0 - bit 6,5 type: eg. 10 CTRL, 11 BULK, 01 INT, 00 ISO - bit 7 direction 1 IN, 0 OUT - -__u8 ep_addr.bep.fa ... bit 6..0 function address - bit 7 0 - -(__u8 ep_addr.bep.hc ... host controller nr) not used -(__u8 ep_addr.bep.host ... host nr) not used - - - diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/acm.c linux-2.2.17/drivers/usb/acm.c --- linux-2.2.17-orig/drivers/usb/acm.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/acm.c Sun Sep 24 04:26:22 2000 @@ -0,0 +1,691 @@ +/* + * acm.c Version 0.16 + * + * Copyright (c) 1999 Armin Fuerst + * Copyright (c) 1999 Pavel Machek + * Copyright (c) 1999 Johannes Erdfelt + * Copyright (c) 2000 Vojtech Pavlik + * + * USB Abstract Control Model driver for USB modems and ISDN adapters + * + * Sponsored by SuSE + * + * ChangeLog: + * v0.9 - thorough cleaning, URBification, almost a rewrite + * v0.10 - some more cleanups + * v0.11 - fixed flow control, read error doesn't stop reads + * v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced + * v0.13 - added termios, added hangup + * v0.14 - sized down struct acm + * v0.15 - fixed flow control again - characters could be lost + * v0.16 - added code for modems with swapped data and control interfaces + */ + +/* + * 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 +#define DEBUG +#include + +/* + * CMSPAR, some architectures can't have space and mark parity. + */ + +#ifndef CMSPAR +#define CMSPAR 0 +#endif + +/* + * Major and minor numbers. + */ + +#define ACM_TTY_MAJOR 166 +#define ACM_TTY_MINORS 32 + +/* + * Requests. + */ + +#define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE) + +#define ACM_REQ_COMMAND 0x00 +#define ACM_REQ_RESPONSE 0x01 +#define ACM_REQ_SET_FEATURE 0x02 +#define ACM_REQ_GET_FEATURE 0x03 +#define ACM_REQ_CLEAR_FEATURE 0x04 + +#define ACM_REQ_SET_LINE 0x20 +#define ACM_REQ_GET_LINE 0x21 +#define ACM_REQ_SET_CONTROL 0x22 +#define ACM_REQ_SEND_BREAK 0x23 + +/* + * IRQs. + */ + +#define ACM_IRQ_NETWORK 0x00 +#define ACM_IRQ_LINE_STATE 0x20 + +/* + * Output control lines. + */ + +#define ACM_CTRL_DTR 0x01 +#define ACM_CTRL_RTS 0x02 + +/* + * Input control lines and line errors. + */ + +#define ACM_CTRL_DCD 0x01 +#define ACM_CTRL_DSR 0x02 +#define ACM_CTRL_BRK 0x04 +#define ACM_CTRL_RI 0x08 + +#define ACM_CTRL_FRAMING 0x10 +#define ACM_CTRL_PARITY 0x20 +#define ACM_CTRL_OVERRUN 0x40 + +/* + * Line speed and caracter encoding. + */ + +struct acm_line { + __u32 speed; + __u8 stopbits; + __u8 parity; + __u8 databits; +} __attribute__ ((packed)); + +/* + * Internal driver structures. + */ + +struct acm { + struct usb_device *dev; /* the coresponding usb device */ + struct usb_interface *iface; /* the interfaces - +0 control +1 data */ + struct tty_struct *tty; /* the coresponding tty */ + struct urb ctrlurb, readurb, writeurb; /* urbs */ + struct acm_line line; /* line coding (bits, stop, parity) */ + struct tq_struct tqueue; /* task queue for line discipline waking up */ + unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ + unsigned int ctrlout; /* output control lines (DTR, RTS) */ + unsigned int writesize; /* max packet size for the output bulk endpoint */ + unsigned int used; /* someone has this acm's device open */ + unsigned int minor; /* acm minor number */ + unsigned char throttle; /* throttled by tty layer */ + unsigned char clocal; /* termios CLOCAL */ +}; + +static struct usb_driver acm_driver; +static struct tty_driver acm_tty_driver; +static struct acm *acm_table[ACM_TTY_MINORS] = { NULL, /* .... */ }; + +#define ACM_READY(acm) (acm && acm->dev && acm->used) + +/* + * Functions for ACM control messages. + */ + +static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len) +{ + int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), + request, USB_RT_ACM, value, acm->iface[0].altsetting[0].bInterfaceNumber, buf, len, HZ * 5); + dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval); + return retval < 0 ? retval : 0; +} + +#define acm_set_control(acm, control) acm_ctrl_msg(acm, ACM_REQ_SET_CONTROL, control, NULL, 0) +#define acm_set_line(acm, line) acm_ctrl_msg(acm, ACM_REQ_SET_LINE, 0, line, sizeof(struct acm_line)) +#define acm_send_break(acm, ms) acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0) + +/* + * Interrupt handler for various ACM control events + */ + +static void acm_ctrl_irq(struct urb *urb) +{ + struct acm *acm = urb->context; + devrequest *dr = urb->transfer_buffer; + unsigned char *data = (unsigned char *)(dr + 1); + int newctrl; + + if (!ACM_READY(acm)) return; + + if (urb->status < 0) { + dbg("nonzero ctrl irq status received: %d", urb->status); + return; + } + + switch (dr->request) { + + case ACM_IRQ_NETWORK: + + dbg("%s network", data[0] ? "connected to" : "disconnected from"); + return; + + case ACM_IRQ_LINE_STATE: + + newctrl = le16_to_cpup((__u16 *) data); + +#if 0 + /* Please someone tell me how to do this properly to kill pppd and not kill minicom */ + if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { + dbg("calling hangup"); + tty_hangup(acm->tty); + } +#endif + + acm->ctrlin = newctrl; + + dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c", + acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', + acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-', + acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-', + acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); + + return; + + default: + dbg("unknown control event received: request %d index %d len %d data0 %d data1 %d", + dr->request, dr->index, dr->length, data[0], data[1]); + return; + } +} + +static void acm_read_bulk(struct urb *urb) +{ + struct acm *acm = urb->context; + struct tty_struct *tty = acm->tty; + unsigned char *data = urb->transfer_buffer; + int i = 0; + + if (!ACM_READY(acm)) return; + + if (urb->status) + dbg("nonzero read bulk status received: %d", urb->status); + + if (!urb->status & !acm->throttle) { + for (i = 0; i < urb->actual_length && !acm->throttle; i++) + tty_insert_flip_char(tty, data[i], 0); + tty_flip_buffer_push(tty); + } + + if (acm->throttle) { + memmove(data, data + i, urb->actual_length - i); + urb->actual_length -= i; + return; + } + + urb->actual_length = 0; + + if (usb_submit_urb(urb)) + dbg("failed resubmitting read urb"); +} + +static void acm_write_bulk(struct urb *urb) +{ + struct acm *acm = (struct acm *)urb->context; + + if (!ACM_READY(acm)) return; + + if (urb->status) + dbg("nonzero write bulk status received: %d", urb->status); + + queue_task(&acm->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void acm_softint(void *private) +{ + struct acm *acm = private; + struct tty_struct *tty = acm->tty; + + if (!ACM_READY(acm)) return; + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + + wake_up_interruptible(&tty->write_wait); +} + +/* + * TTY handlers + */ + +static int acm_tty_open(struct tty_struct *tty, struct file *filp) +{ + struct acm *acm = acm_table[MINOR(tty->device)]; + + if (!acm || !acm->dev) return -EINVAL; + + tty->driver_data = acm; + acm->tty = tty; + + MOD_INC_USE_COUNT; + + if (acm->used++) return 0; + + if (usb_submit_urb(&acm->ctrlurb)) + dbg("usb_submit_urb(ctrl irq) failed"); + + if (usb_submit_urb(&acm->readurb)) + dbg("usb_submit_urb(read bulk) failed"); + + acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS); + + return 0; +} + +static void acm_tty_close(struct tty_struct *tty, struct file *filp) +{ + struct acm *acm = tty->driver_data; + + if (!acm || !acm->used) return; + + if (!--acm->used) { + if (acm->dev) { + acm_set_control(acm, acm->ctrlout = 0); + usb_unlink_urb(&acm->ctrlurb); + usb_unlink_urb(&acm->writeurb); + usb_unlink_urb(&acm->readurb); + } else { + tty_unregister_devfs(&acm_tty_driver, acm->minor); + acm_table[acm->minor] = NULL; + kfree(acm); + } + } + MOD_DEC_USE_COUNT; +} + +static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) +{ + struct acm *acm = tty->driver_data; + + if (!ACM_READY(acm)) return -EINVAL; + if (acm->writeurb.status == -EINPROGRESS) return 0; + if (!count) return 0; + + count = (count > acm->writesize) ? acm->writesize : count; + + if (from_user) + copy_from_user(acm->writeurb.transfer_buffer, buf, count); + else + memcpy(acm->writeurb.transfer_buffer, buf, count); + + acm->writeurb.transfer_buffer_length = count; + + if (usb_submit_urb(&acm->writeurb)) + dbg("usb_submit_urb(write bulk) failed"); + + return count; +} + +static int acm_tty_write_room(struct tty_struct *tty) +{ + struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return -EINVAL; + return acm->writeurb.status == -EINPROGRESS ? 0 : acm->writesize; +} + +static int acm_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return -EINVAL; + return acm->writeurb.status == -EINPROGRESS ? acm->writeurb.transfer_buffer_length : 0; +} + +static void acm_tty_throttle(struct tty_struct *tty) +{ + struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return; + acm->throttle = 1; +} + +static void acm_tty_unthrottle(struct tty_struct *tty) +{ + struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return; + acm->throttle = 0; + if (acm->readurb.status != -EINPROGRESS) + acm_read_bulk(&acm->readurb); +} + +static void acm_tty_break_ctl(struct tty_struct *tty, int state) +{ + struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return; + if (acm_send_break(acm, state ? 0xffff : 0)) + dbg("send break failed"); +} + +static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct acm *acm = tty->driver_data; + unsigned int retval, mask, newctrl; + + if (!ACM_READY(acm)) return -EINVAL; + + switch (cmd) { + + case TIOCMGET: + + return put_user((acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | + (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | + (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | + (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | + (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | + TIOCM_CTS, (unsigned long *) arg); + + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + + if ((retval = get_user(mask, (unsigned long *) arg))) return retval; + + newctrl = acm->ctrlout; + mask = (mask & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (mask & TIOCM_RTS ? ACM_CTRL_RTS : 0); + + switch (cmd) { + case TIOCMSET: newctrl = mask; break; + case TIOCMBIS: newctrl |= mask; break; + case TIOCMBIC: newctrl &= ~mask; break; + } + + if (acm->ctrlout == newctrl) return 0; + return acm_set_control(acm, acm->ctrlout = newctrl); + } + + return -ENOIOCTLCMD; +} + +static __u32 acm_tty_speed[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, + 1200, 1800, 2400, 4800, 9600, 19200, 38400, + 57600, 115200, 230400, 460800, 500000, 576000, + 921600, 1000000, 1152000, 1500000, 2000000, + 2500000, 3000000, 3500000, 4000000 +}; + +static __u8 acm_tty_size[] = { + 5, 6, 7, 8 +}; + +static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_old) +{ + struct acm *acm = tty->driver_data; + struct termios *termios = tty->termios; + struct acm_line newline; + int newctrl = acm->ctrlout; + + if (!ACM_READY(acm)) return; + + newline.speed = cpu_to_le32p(acm_tty_speed + + (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0)); + newline.stopbits = termios->c_cflag & CSTOPB ? 2 : 0; + newline.parity = termios->c_cflag & PARENB ? + (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; + newline.databits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; + + acm->clocal = termios->c_cflag & CLOCAL; + + if (!newline.speed) { + newline.speed = acm->line.speed; + newctrl &= ~ACM_CTRL_DTR; + } else newctrl |= ACM_CTRL_DTR; + + if (newctrl != acm->ctrlout) + acm_set_control(acm, acm->ctrlout = newctrl); + + if (memcmp(&acm->line, &newline, sizeof(struct acm_line))) { + memcpy(&acm->line, &newline, sizeof(struct acm_line)); + dbg("set line: %d %d %d %d", newline.speed, newline.stopbits, newline.parity, newline.databits); + acm_set_line(acm, &acm->line); + } +} + +/* + * USB probe and disconnect routines. + */ + +static void *acm_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct acm *acm; + struct usb_config_descriptor *cfacm; + struct usb_interface_descriptor *ifcom, *ifdata; + struct usb_endpoint_descriptor *epctrl, *epread, *epwrite; + int readsize, ctrlsize, minor, i; + unsigned char *buf; + + if (dev->descriptor.bDeviceClass != 2 || dev->descriptor.bDeviceSubClass != 0 + || dev->descriptor.bDeviceProtocol != 0) return NULL; + + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + + cfacm = dev->config + i; + + dbg("probing config %d", cfacm->bConfigurationValue); + + if (cfacm->bNumInterfaces != 2 || + usb_interface_claimed(cfacm->interface + 0) || + usb_interface_claimed(cfacm->interface + 1)) + continue; + + ifcom = cfacm->interface[0].altsetting + 0; + ifdata = cfacm->interface[1].altsetting + 0; + + if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2) { + ifcom = cfacm->interface[1].altsetting + 0; + ifdata = cfacm->interface[0].altsetting + 0; + if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2) + continue; + } + + if (ifcom->bInterfaceClass != 2 || ifcom->bInterfaceSubClass != 2 || + ifcom->bInterfaceProtocol != 1 || ifcom->bNumEndpoints != 1) + continue; + + epctrl = ifcom->endpoint + 0; + epread = ifdata->endpoint + 0; + epwrite = ifdata->endpoint + 1; + + if ((epctrl->bEndpointAddress & 0x80) != 0x80 || (epctrl->bmAttributes & 3) != 3 || + (epread->bmAttributes & 3) != 2 || (epwrite->bmAttributes & 3) != 2 || + ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80) + continue; + + if ((epread->bEndpointAddress & 0x80) != 0x80) { + epread = ifdata->endpoint + 1; + epwrite = ifdata->endpoint + 0; + } + + usb_set_configuration(dev, cfacm->bConfigurationValue); + + for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); + if (acm_table[minor]) { + err("no more free acm devices"); + return NULL; + } + + if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) { + err("out of memory"); + return NULL; + } + memset(acm, 0, sizeof(struct acm)); + + ctrlsize = epctrl->wMaxPacketSize; + readsize = epread->wMaxPacketSize; + acm->writesize = epwrite->wMaxPacketSize; + acm->iface = cfacm->interface; + acm->minor = minor; + acm->dev = dev; + + acm->tqueue.routine = acm_softint; + acm->tqueue.data = acm; + + if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) { + err("out of memory"); + kfree(acm); + return NULL; + } + + FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress), + buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); + + FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), + buf += ctrlsize, readsize, acm_read_bulk, acm); + + FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), + buf += readsize, acm->writesize, acm_write_bulk, acm); + + printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor); + + acm_set_control(acm, acm->ctrlout); + + acm->line.speed = cpu_to_le32(9600); + acm->line.databits = 8; + acm_set_line(acm, &acm->line); + + usb_driver_claim_interface(&acm_driver, acm->iface + 0, acm); + usb_driver_claim_interface(&acm_driver, acm->iface + 1, acm); + + tty_register_devfs(&acm_tty_driver, 0, minor); + return acm_table[minor] = acm; + } + + return NULL; +} + +static void acm_disconnect(struct usb_device *dev, void *ptr) +{ + struct acm *acm = ptr; + + if (!acm || !acm->dev) { + dbg("disconnect on nonexisting interface"); + return; + } + + acm->dev = NULL; + + usb_unlink_urb(&acm->ctrlurb); + usb_unlink_urb(&acm->readurb); + usb_unlink_urb(&acm->writeurb); + + kfree(acm->ctrlurb.transfer_buffer); + + usb_driver_release_interface(&acm_driver, acm->iface + 0); + usb_driver_release_interface(&acm_driver, acm->iface + 1); + + if (!acm->used) { + tty_unregister_devfs(&acm_tty_driver, acm->minor); + acm_table[acm->minor] = NULL; + kfree(acm); + return; + } + + if (acm->tty) + tty_hangup(acm->tty); +} + +/* + * USB driver structure. + */ + +static struct usb_driver acm_driver = { + name: "acm", + probe: acm_probe, + disconnect: acm_disconnect +}; + +/* + * TTY driver structures. + */ + +static int acm_tty_refcount; + +static struct tty_struct *acm_tty_table[ACM_TTY_MINORS]; +static struct termios *acm_tty_termios[ACM_TTY_MINORS]; +static struct termios *acm_tty_termios_locked[ACM_TTY_MINORS]; + +static struct tty_driver acm_tty_driver = { + magic: TTY_DRIVER_MAGIC, + driver_name: "acm", + name: "usb/acm/%d", + major: ACM_TTY_MAJOR, + minor_start: 0, + num: ACM_TTY_MINORS, + type: TTY_DRIVER_TYPE_SERIAL, + subtype: SERIAL_TYPE_NORMAL, + flags: TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, + + refcount: &acm_tty_refcount, + + table: acm_tty_table, + termios: acm_tty_termios, + termios_locked: acm_tty_termios_locked, + + open: acm_tty_open, + close: acm_tty_close, + write: acm_tty_write, + write_room: acm_tty_write_room, + ioctl: acm_tty_ioctl, + throttle: acm_tty_throttle, + unthrottle: acm_tty_unthrottle, + chars_in_buffer: acm_tty_chars_in_buffer, + break_ctl: acm_tty_break_ctl, + set_termios: acm_tty_set_termios +}; + +/* + * Init / exit. + */ + +static int __init acm_init(void) +{ + acm_tty_driver.init_termios = tty_std_termios; + acm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + + if (tty_register_driver(&acm_tty_driver)) + return -1; + + if (usb_register(&acm_driver) < 0) { + tty_unregister_driver(&acm_tty_driver); + return -1; + } + + return 0; +} + +static void __exit acm_exit(void) +{ + usb_deregister(&acm_driver); + tty_unregister_driver(&acm_tty_driver); +} + +module_init(acm_init); +module_exit(acm_exit); diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/audio.c linux-2.2.17/drivers/usb/audio.c --- linux-2.2.17-orig/drivers/usb/audio.c Wed Apr 28 11:14:03 1999 +++ linux-2.2.17/drivers/usb/audio.c Sun Sep 24 04:26:22 2000 @@ -1,126 +1,3717 @@ +/*****************************************************************************/ + +/* + * audio.c -- USB Audio Class driver + * + * Copyright (C) 1999, 2000 + * Alan Cox (alan@lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * 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. + * + * + * 1999-09-07: Alan Cox + * Parsing Audio descriptor patch + * 1999-09-08: Thomas Sailer + * Added OSS compatible data io functions; both parts of the + * driver remain to be glued together + * 1999-09-10: Thomas Sailer + * Beautified the driver. Added sample format conversions. + * Still not properly glued with the parsing code. + * The parsing code seems to have its problems btw, + * Since it parses all available configs but doesn't + * store which iface/altsetting belongs to which config. + * 1999-09-20: Thomas Sailer + * Threw out Alan's parsing code and implemented my own one. + * You cannot reasonnably linearly parse audio descriptors, + * especially the AudioClass descriptors have to be considered + * pointer lists. Mixer parsing untested, due to lack of device. + * First stab at synch pipe implementation, the Dallas USB DAC + * wants to use an Asynch out pipe. usb_audio_state now basically + * only contains lists of mixer and wave devices. We can therefore + * now have multiple mixer/wave devices per USB device. + * 1999-10-28: Thomas Sailer + * Converted to URB API. Fixed a taskstate/wakeup semantics mistake + * that made the driver consume all available CPU cycles. + * Now runs stable on UHCI-Acher/Fliegl/Sailer. + * 1999-10-31: Thomas Sailer + * Audio can now be unloaded if it is not in use by any mixer + * or dsp client (formerly you had to disconnect the audio devices + * from the USB port) + * Finally, about three months after ordering, my "Maxxtro SPK222" + * speakers arrived, isn't disdata a great mail order company 8-) + * Parse class specific endpoint descriptor of the audiostreaming + * interfaces and take the endpoint attributes from there. + * Unbelievably, the Philips USB DAC has a sampling rate range + * of over a decade, yet does not support the sampling rate control! + * No wonder it sounds so bad, has very audible sampling rate + * conversion distortion. Don't try to listen to it using + * decent headphones! + * "Let's make things better" -> but please Philips start with your + * own stuff!!!! + * 1999-11-02: Thomas Sailer + * It takes the Philips boxes several seconds to acquire synchronisation + * that means they won't play short sounds. Should probably maintain + * the ISO datastream even if there's nothing to play. + * Fix counting the total_bytes counter, RealPlayer G2 depends on it. + * 1999-12-20: Thomas Sailer + * Fix bad bug in conversion to per interface probing. + * disconnect was called multiple times for the audio device, + * leading to a premature freeing of the audio structures + * 2000-05-13: Thomas Sailer + * I don't remember who changed the find_format routine, + * but the change was completely broken for the Dallas + * chip. Anyway taking sampling rate into account in find_format + * is bad and should not be done unless there are devices with + * completely broken audio descriptors. Unless someone shows + * me such a descriptor, I will not allow find_format to + * take the sampling rate into account. + * Also, the former find_format made: + * - mpg123 play mono instead of stereo + * - sox completely fail for wav's with sample rates < 44.1kHz + * for the Dallas chip. + * Also fix a rather long standing problem with applications that + * use "small" writes producing no sound at all. + * 2000-05-15: Thomas Sailer + * My fears came true, the Philips camera indeed has pretty stupid + * audio descriptors. + * 2000-05-17: Thomas Sailer + * Nemsoft spotted my stupid last minute change, thanks + * 2000-05-19: Thomas Sailer + * Fixed FEATURE_UNIT thinkos found thanks to the KC Technology + * Xtend device. Basically the driver treated FEATURE_UNIT's sourced + * by mono terminals as stereo. + * 2000-05-20: Thomas Sailer + * SELECTOR support (and thus selecting record channels from the mixer). + * Somewhat peculiar due to OSS interface limitations. Only works + * for channels where a "slider" is already in front of it (i.e. + * a MIXER unit or a FEATURE unit with volume capability). + * + */ + +/* + * Strategy: + * + * Alan Cox and Thomas Sailer are starting to dig at opposite ends and + * are hoping to meet in the middle, just like tunnel diggers :) + * Alan tackles the descriptor parsing, Thomas the actual data IO and the + * OSS compatible interface. + * + * Data IO implementation issues + * + * A mmap'able ring buffer per direction is implemented, because + * almost every OSS app expects it. It is however impractical to + * transmit/receive USB data directly into and out of the ring buffer, + * due to alignment and synchronisation issues. Instead, the ring buffer + * feeds a constant time delay line that handles the USB issues. + * + * Now we first try to find an alternate setting that exactly matches + * the sample format requested by the user. If we find one, we do not + * need to perform any sample rate conversions. If there is no matching + * altsetting, we choose the closest one and perform sample format + * conversions. We never do sample rate conversion; these are too + * expensive to be performed in the kernel. + * + * Current status: + * - Pretty stable on UHCI-Acher/Fliegl/Sailer + * - Does not work on OHCI due to lack of OHCI driver supporting URB's + * + * Generally: Due to the brokenness of the Audio Class spec + * it seems generally impossible to write a generic Audio Class driver, + * so a reasonable driver should implement the features that are actually + * used. + * + * Parsing implementation issues + * + * One cannot reasonably parse the AudioClass descriptors linearly. + * Therefore the current implementation features routines to look + * for a specific descriptor in the descriptor list. + * + * How does the parsing work? First, all interfaces are searched + * for an AudioControl class interface. If found, the config descriptor + * that belongs to the current configuration is fetched from the device. + * Then the HEADER descriptor is fetched. It contains a list of + * all AudioStreaming and MIDIStreaming devices. This list is then walked, + * and all AudioStreaming interfaces are classified into input and output + * interfaces (according to the endpoint0 direction in altsetting1) (MIDIStreaming + * is currently not supported). The input & output list is then used + * to group inputs and outputs together and issued pairwise to the + * AudioStreaming class parser. Finally, all OUTPUT_TERMINAL descriptors + * are walked and issued to the mixer construction routine. + * + * The AudioStreaming parser simply enumerates all altsettings belonging + * to the specified interface. It looks for AS_GENERAL and FORMAT_TYPE + * class specific descriptors to extract the sample format/sample rate + * data. Only sample format types PCM and PCM8 are supported right now, and + * only FORMAT_TYPE_I is handled. The isochronous data endpoint needs to + * be the first endpoint of the interface, and the optional synchronisation + * isochronous endpoint the second one. + * + * Mixer construction works as follows: The various TERMINAL and UNIT + * descriptors span a tree from the root (OUTPUT_TERMINAL) through the + * intermediate nodes (UNITs) to the leaves (INPUT_TERMINAL). We walk + * that tree in a depth first manner. FEATURE_UNITs may contribute volume, + * bass and treble sliders to the mixer, MIXER_UNITs volume sliders. + * The terminal type encoded in the INPUT_TERMINALs feeds a heuristic + * to determine "meaningful" OSS slider numbers, however we will see + * how well this works in practice. Other features are not used at the + * moment, they seem less often used. Also, it seems difficult at least + * to construct recording source switches from SELECTOR_UNITs, but + * since there are not many USB ADC's available, we leave that for later. + */ + +/*****************************************************************************/ + +#include #include #include #include #include #include -#include "usb.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -static int usb_audio_probe(struct usb_device *dev); -static void usb_audio_disconnect(struct usb_device *dev); -static LIST_HEAD(usb_audio_list); +#include "audio.h" -struct usb_audio -{ - struct usb_device *dev; +#define AUDIO_DEBUG 1 + +#define SND_DEV_DSP16 5 + + +/* --------------------------------------------------------------------- */ + +/* + * Linked list of all audio devices... + */ +static struct list_head audiodevs = LIST_HEAD_INIT(audiodevs); +static DECLARE_MUTEX(open_sem); + +/* + * wait queue for processes wanting to open an USB audio device + */ +static DECLARE_WAIT_QUEUE_HEAD(open_wait); + + +#define MAXFORMATS MAX_ALT +#define DMABUFSHIFT 17 /* 128k worth of DMA buffer */ +#define NRSGBUF (1U<<(DMABUFSHIFT-PAGE_SHIFT)) + +/* + * This influences: + * - Latency + * - Interrupt rate + * - Synchronisation behaviour + * Don't touch this if you don't understand all of the above. + */ +#define DESCFRAMES 5 +#define SYNCFRAMES DESCFRAMES + +#define MIXFLG_STEREOIN 1 +#define MIXFLG_STEREOOUT 2 + +struct mixerchannel { + __u16 value; + __u16 osschannel; /* number of the OSS channel */ + __s16 minval, maxval; + __u16 slctunitid; + __u8 unitid; + __u8 selector; + __u8 chnum; + __u8 flags; +}; + +struct audioformat { + unsigned int format; + unsigned int sratelo; + unsigned int sratehi; + unsigned char altsetting; + unsigned char attributes; +}; + +struct dmabuf { + /* buffer data format */ + unsigned int format; + unsigned int srate; + /* physical buffer */ + unsigned char *sgbuf[NRSGBUF]; + unsigned bufsize; + unsigned numfrag; + unsigned fragshift; + unsigned wrptr, rdptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; +}; + +struct usb_audio_state; + +#define FLG_URB0RUNNING 1 +#define FLG_URB1RUNNING 2 +#define FLG_SYNC0RUNNING 4 +#define FLG_SYNC1RUNNING 8 +#define FLG_RUNNING 16 +#define FLG_CONNECTED 32 + +struct my_data_urb { + urb_t urb; + iso_packet_descriptor_t isoframe[DESCFRAMES]; +}; + +struct my_sync_urb { + urb_t urb; + iso_packet_descriptor_t isoframe[SYNCFRAMES]; +}; + + +struct usb_audiodev { struct list_head list; + struct usb_audio_state *state; + + /* soundcore stuff */ + int dev_audio; + + /* wave stuff */ + mode_t open_mode; + spinlock_t lock; /* DMA buffer access spinlock */ + + struct usbin { + int interface; /* Interface number, -1 means not used */ + unsigned int format; /* USB data format */ + unsigned int datapipe; /* the data input pipe */ + unsigned int syncpipe; /* the synchronisation pipe - 0 for anything but adaptive IN mode */ + unsigned int syncinterval; /* P for adaptive IN mode, 0 otherwise */ + unsigned int freqn; /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */ + unsigned int freqmax; /* maximum sampling rate, used for buffer management */ + unsigned int phase; /* phase accumulator */ + unsigned int flags; /* see FLG_ defines */ + + struct my_data_urb durb[2]; /* ISO descriptors for the data endpoint */ + struct my_sync_urb surb[2]; /* ISO sync pipe descriptor if needed */ + + struct dmabuf dma; + } usbin; + + struct usbout { + int interface; /* Interface number, -1 means not used */ + unsigned int format; /* USB data format */ + unsigned int datapipe; /* the data input pipe */ + unsigned int syncpipe; /* the synchronisation pipe - 0 for anything but asynchronous OUT mode */ + unsigned int syncinterval; /* P for asynchronous OUT mode, 0 otherwise */ + unsigned int freqn; /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */ + unsigned int freqm; /* momentary sampling rate in USB format, i.e. fs/1000 in Q10.14 */ + unsigned int freqmax; /* maximum sampling rate, used for buffer management */ + unsigned int phase; /* phase accumulator */ + unsigned int flags; /* see FLG_ defines */ + + struct my_data_urb durb[2]; /* ISO descriptors for the data endpoint */ + struct my_sync_urb surb[2]; /* ISO sync pipe descriptor if needed */ + + struct dmabuf dma; + } usbout; + + + unsigned int numfmtin, numfmtout; + struct audioformat fmtin[MAXFORMATS]; + struct audioformat fmtout[MAXFORMATS]; }; -static struct usb_driver usb_audio_driver = -{ - "audio", - usb_audio_probe, - usb_audio_disconnect, - {NULL, NULL} +struct usb_mixerdev { + struct list_head list; + struct usb_audio_state *state; + + /* soundcore stuff */ + int dev_mixer; + + unsigned char iface; /* interface number of the AudioControl interface */ + + /* USB format descriptions */ + unsigned int numch, modcnt; + + /* mixch is last and gets allocated dynamically */ + struct mixerchannel ch[0]; }; +struct usb_audio_state { + struct list_head audiodev; -static int usb_audio_irq(int state, void *buffer, void *dev_id) + /* USB device */ + struct usb_device *usbdev; + + struct list_head audiolist; + struct list_head mixerlist; + + unsigned count; /* usage counter; NOTE: the usb stack is also considered a user */ +}; + +/* private audio format extensions */ +#define AFMT_STEREO 0x80000000 +#define AFMT_ISSTEREO(x) ((x) & AFMT_STEREO) +#define AFMT_IS16BIT(x) ((x) & (AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE)) +#define AFMT_ISUNSIGNED(x) ((x) & (AFMT_U8|AFMT_U16_LE|AFMT_U16_BE)) +#define AFMT_BYTESSHIFT(x) ((AFMT_ISSTEREO(x) ? 1 : 0) + (AFMT_IS16BIT(x) ? 1 : 0)) +#define AFMT_BYTES(x) (1<= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +/* + * OSS compatible ring buffer management. The ring buffer may be mmap'ed into + * an application address space. + * + * I first used the rvmalloc stuff copied from bttv. Alan Cox did not like it, so + * we now use an array of pointers to a single page each. This saves us the + * kernel page table manipulations, but we have to do a page table alike mechanism + * (though only one indirection) in software. + */ + +static void dmabuf_release(struct dmabuf *db) +{ + unsigned int nr; + void *p; + + for(nr = 0; nr < NRSGBUF; nr++) { + if (!(p = db->sgbuf[nr])) + continue; + mem_map_unreserve(MAP_NR(p)); + free_page((unsigned long)p); + db->sgbuf[nr] = NULL; + } + db->mapped = db->ready = 0; +} + +static int dmabuf_init(struct dmabuf *db) +{ + unsigned int nr, bytepersec, bufs; + void *p; + + /* initialize some fields */ + db->rdptr = db->wrptr = db->total_bytes = db->count = db->error = 0; + /* calculate required buffer size */ + bytepersec = db->srate << AFMT_BYTESSHIFT(db->format); + bufs = 1U << DMABUFSHIFT; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->dmasize = db->numfrag << db->fragshift; + for(nr = 0; nr < NRSGBUF; nr++) { + if (!db->sgbuf[nr]) { + p = (void *)get_free_page(GFP_KERNEL); + if (!p) + return -ENOMEM; + db->sgbuf[nr] = p; + mem_map_reserve(MAP_NR(p)); + } + memset(db->sgbuf[nr], AFMT_ISUNSIGNED(db->format) ? 0x80 : 0, PAGE_SIZE); + if ((nr << PAGE_SHIFT) >= db->dmasize) + break; + } + db->bufsize = nr << PAGE_SHIFT; + db->ready = 1; + printk(KERN_DEBUG "dmabuf_init: bytepersec %d bufs %d ossfragshift %d ossmaxfrags %d " + "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d fmt 0x%x\n", + bytepersec, bufs, db->ossfragshift, db->ossmaxfrags, db->fragshift, db->fragsize, + db->numfrag, db->dmasize, db->bufsize, db->format); + return 0; } -static int usb_audio_probe(struct usb_device *dev) +static int dmabuf_mmap(struct dmabuf *db, unsigned long start, unsigned long size, pgprot_t prot) { - struct usb_interface_descriptor *interface; - struct usb_endpoint_descriptor *endpoint; - struct usb_audio *aud; + unsigned int nr; - int i; - int na=0; + if (!db->ready || db->mapped || (start | size) & (PAGE_SIZE-1) || size > db->bufsize) + return -EINVAL; + size >>= PAGE_SHIFT; + for(nr = 0; nr < size; nr++) + if (!db->sgbuf[nr]) + return -EINVAL; + db->mapped = 1; + for(nr = 0; nr < size; nr++) { + if (remap_page_range(start, virt_to_phys(db->sgbuf[nr]), PAGE_SIZE, prot)) + return -EAGAIN; + start += PAGE_SIZE; + } + return 0; +} - interface = &dev->config[0].interface[0]; +static void dmabuf_copyin(struct dmabuf *db, const void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; - for(i=0;iconfig[0].bNumInterfaces;i++) - { - int x; + db->total_bytes += size; + for (;;) { + if (size <= 0) + return; + pgrem = ((~db->wrptr) & (PAGE_SIZE-1)) + 1; + if (pgrem > size) + pgrem = size; + rem = db->dmasize - db->wrptr; + if (pgrem > rem) + pgrem = rem; + memcpy((db->sgbuf[db->wrptr >> PAGE_SHIFT]) + (db->wrptr & (PAGE_SIZE-1)), buffer, pgrem); + size -= pgrem; + (char *)buffer += pgrem; + db->wrptr += pgrem; + if (db->wrptr >= db->dmasize) + db->wrptr = 0; + } +} - endpoint = &interface->endpoint[i]; +static void dmabuf_copyout(struct dmabuf *db, void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; - if(interface->bInterfaceClass != 1) - continue; + db->total_bytes += size; + for (;;) { + if (size <= 0) + return; + pgrem = ((~db->rdptr) & (PAGE_SIZE-1)) + 1; + if (pgrem > size) + pgrem = size; + rem = db->dmasize - db->rdptr; + if (pgrem > rem) + pgrem = rem; + memcpy(buffer, (db->sgbuf[db->rdptr >> PAGE_SHIFT]) + (db->rdptr & (PAGE_SIZE-1)), pgrem); + size -= pgrem; + (char *)buffer += pgrem; + db->rdptr += pgrem; + if (db->rdptr >= db->dmasize) + db->rdptr = 0; + } +} + +static int dmabuf_copyin_user(struct dmabuf *db, unsigned int ptr, const void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; + + if (!db->ready || db->mapped) + return -EINVAL; + for (;;) { + if (size <= 0) + return 0; + pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1; + if (pgrem > size) + pgrem = size; + rem = db->dmasize - ptr; + if (pgrem > rem) + pgrem = rem; + copy_from_user_ret((db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), buffer, pgrem, -EFAULT); + size -= pgrem; + (char *)buffer += pgrem; + ptr += pgrem; + if (ptr >= db->dmasize) + ptr = 0; + } +} - printk(KERN_INFO "USB audio device detected.\n"); +static int dmabuf_copyout_user(struct dmabuf *db, unsigned int ptr, void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; + + if (!db->ready || db->mapped) + return -EINVAL; + for (;;) { + if (size <= 0) + return 0; + pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1; + if (pgrem > size) + pgrem = size; + rem = db->dmasize - ptr; + if (pgrem > rem) + pgrem = rem; + copy_to_user_ret(buffer, (db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), pgrem, -EFAULT); + size -= pgrem; + (char *)buffer += pgrem; + ptr += pgrem; + if (ptr >= db->dmasize) + ptr = 0; + } +} + +/* --------------------------------------------------------------------- */ +/* + * USB I/O code. We do sample format conversion if necessary + */ + +static void usbin_stop(struct usb_audiodev *as) +{ + struct usbin *u = &as->usbin; + unsigned long flags; + unsigned int i, notkilled = 1; + + spin_lock_irqsave(&as->lock, flags); + u->flags &= ~FLG_RUNNING; + i = u->flags; + spin_unlock_irqrestore(&as->lock, flags); + while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) { + set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + spin_lock_irqsave(&as->lock, flags); + i = u->flags; + spin_unlock_irqrestore(&as->lock, flags); + if (notkilled && signal_pending(current)) { + if (i & FLG_URB0RUNNING) + usb_unlink_urb(&u->durb[0].urb); + if (i & FLG_URB1RUNNING) + usb_unlink_urb(&u->durb[1].urb); + if (i & FLG_SYNC0RUNNING) + usb_unlink_urb(&u->surb[0].urb); + if (i & FLG_SYNC1RUNNING) + usb_unlink_urb(&u->surb[1].urb); + notkilled = 0; + } + } + set_current_state(TASK_RUNNING); + if (u->durb[0].urb.transfer_buffer) + kfree(u->durb[0].urb.transfer_buffer); + if (u->durb[1].urb.transfer_buffer) + kfree(u->durb[1].urb.transfer_buffer); + if (u->surb[0].urb.transfer_buffer) + kfree(u->surb[0].urb.transfer_buffer); + if (u->surb[1].urb.transfer_buffer) + kfree(u->surb[1].urb.transfer_buffer); + u->durb[0].urb.transfer_buffer = u->durb[1].urb.transfer_buffer = + u->surb[0].urb.transfer_buffer = u->surb[1].urb.transfer_buffer = NULL; +} + +static inline void usbin_release(struct usb_audiodev *as) +{ + usbin_stop(as); +} - switch(interface->bInterfaceSubClass) - { - case 0x01: - printk(KERN_INFO "audio: Control device.\n"); +static void usbin_disc(struct usb_audiodev *as) +{ + struct usbin *u = &as->usbin; + + unsigned long flags; + + spin_lock_irqsave(&as->lock, flags); + u->flags &= ~(FLG_RUNNING | FLG_CONNECTED); + spin_unlock_irqrestore(&as->lock, flags); + usbin_stop(as); +} + +static void conversion(const void *ibuf, unsigned int ifmt, void *obuf, unsigned int ofmt, void *tmp, unsigned int scnt) +{ + unsigned int cnt, i; + __s16 *sp, *sp2, s; + unsigned char *bp; + + cnt = scnt; + if (AFMT_ISSTEREO(ifmt)) + cnt <<= 1; + sp = ((__s16 *)tmp) + cnt; + switch (ifmt & ~AFMT_STEREO) { + case AFMT_U8: + for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) { + bp--; + sp--; + *sp = (*bp ^ 0x80) << 8; + } break; - case 0x02: - printk(KERN_INFO "audio: streaming.\n"); + + case AFMT_S8: + for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) { + bp--; + sp--; + *sp = *bp << 8; + } break; - case 0x03: - printk(KERN_INFO "audio: nonstreaming.\n"); + + case AFMT_U16_LE: + for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = (bp[0] | (bp[1] << 8)) ^ 0x8000; + } break; + + case AFMT_U16_BE: + for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = (bp[1] | (bp[0] << 8)) ^ 0x8000; } - na++; + break; + + case AFMT_S16_LE: + for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = bp[0] | (bp[1] << 8); } + break; - if(na==0) - return -1; + case AFMT_S16_BE: + for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = bp[1] | (bp[0] << 8); + } + break; + } + if (!AFMT_ISSTEREO(ifmt) && AFMT_ISSTEREO(ofmt)) { + /* expand from mono to stereo */ + for (sp = ((__s16 *)tmp)+scnt, sp2 = ((__s16 *)tmp)+2*scnt, i = 0; i < scnt; i++) { + sp--; + sp2 -= 2; + sp2[0] = sp2[1] = sp[0]; + } + } + if (AFMT_ISSTEREO(ifmt) && !AFMT_ISSTEREO(ofmt)) { + /* contract from stereo to mono */ + for (sp = sp2 = ((__s16 *)tmp), i = 0; i < scnt; i++, sp++, sp2 += 2) + sp[0] = (sp2[0] + sp2[1]) >> 1; + } + cnt = scnt; + if (AFMT_ISSTEREO(ofmt)) + cnt <<= 1; + sp = ((__s16 *)tmp); + bp = ((unsigned char *)obuf); + switch (ofmt & ~AFMT_STEREO) { + case AFMT_U8: + for (i = 0; i < cnt; i++, sp++, bp++) + *bp = (*sp >> 8) ^ 0x80; + break; + + case AFMT_S8: + for (i = 0; i < cnt; i++, sp++, bp++) + *bp = *sp >> 8; + break; + + case AFMT_U16_LE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[0] = s; + bp[1] = (s >> 8) ^ 0x80; + } + break; + + case AFMT_U16_BE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[1] = s; + bp[0] = (s >> 8) ^ 0x80; + } + break; + + case AFMT_S16_LE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[0] = s; + bp[1] = s >> 8; + } + break; + + case AFMT_S16_BE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[1] = s; + bp[0] = s >> 8; + } + break; + } + +} + +static void usbin_convert(struct usbin *u, unsigned char *buffer, unsigned int samples) +{ + union { + __s16 s[64]; + unsigned char b[0]; + } tmp; + unsigned int scnt, maxs, ufmtsh, dfmtsh; + + ufmtsh = AFMT_BYTESSHIFT(u->format); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64; + while (samples > 0) { + scnt = samples; + if (scnt > maxs) + scnt = maxs; + conversion(buffer, u->format, tmp.b, u->dma.format, tmp.b, scnt); + dmabuf_copyin(&u->dma, tmp.b, scnt << dfmtsh); + buffer += scnt << ufmtsh; + samples -= scnt; + } +} + +static int usbin_prepare_desc(struct usbin *u, purb_t urb) +{ + unsigned int i, maxsize, offs; + + maxsize = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); + //printk(KERN_DEBUG "usbin_prepare_desc: maxsize %d freq 0x%x format 0x%x\n", maxsize, u->freqn, u->format); + for (i = offs = 0; i < DESCFRAMES; i++, offs += maxsize) { + urb->iso_frame_desc[i].length = maxsize; + urb->iso_frame_desc[i].offset = offs; + } + return 0; +} - aud = kmalloc(sizeof(struct usb_audio), GFP_KERNEL); - if(aud) - { - memset(aud, 0, sizeof(*aud)); - aud->dev = dev; - dev->private = aud; +/* + * return value: 0 if descriptor should be restarted, -1 otherwise + * convert sample format on the fly if necessary + */ +static int usbin_retire_desc(struct usbin *u, purb_t urb) +{ + unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, dmafree; + unsigned char *cp; - endpoint = &interface->endpoint[0]; + ufmtsh = AFMT_BYTESSHIFT(u->format); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + for (i = 0; i < DESCFRAMES; i++) { + cp = ((unsigned char *)urb->transfer_buffer) + urb->iso_frame_desc[i].offset; + if (urb->iso_frame_desc[i].status) { + printk(KERN_DEBUG "usbin_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status); + continue; + } + scnt = urb->iso_frame_desc[i].actual_length >> ufmtsh; + if (!scnt) + continue; + cnt = scnt << dfmtsh; + if (!u->dma.mapped) { + dmafree = u->dma.dmasize - u->dma.count; + if (cnt > dmafree) { + scnt = dmafree >> dfmtsh; + cnt = scnt << dfmtsh; + err++; + } + } + u->dma.count += cnt; + if (u->format == u->dma.format) { + /* we do not need format conversion */ + dmabuf_copyin(&u->dma, cp, cnt); + } else { + /* we need sampling format conversion */ + usbin_convert(u, cp, scnt); + } + } + if (err) + u->dma.error++; + if (u->dma.count >= (signed)u->dma.fragsize) + wake_up(&u->dma.wait); + return err ? -1 : 0; +} -// usb_set_configuration(dev, dev->config[0].bConfigurationValue); -// usb_set_protocol(dev, 0); -// usb_set_idle(dev, 0, 0); +static void usbin_completed(struct urb *urb) +{ + struct usb_audiodev *as = (struct usb_audiodev *)urb->context; + struct usbin *u = &as->usbin; + unsigned long flags; + unsigned int mask; + int suret = USB_ST_NOERROR; + +#if 0 + printk(KERN_DEBUG "usbin_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags); +#endif + if (urb == &u->durb[0].urb) + mask = FLG_URB0RUNNING; + else if (urb == &u->durb[1].urb) + mask = FLG_URB1RUNNING; + else { + mask = 0; + printk(KERN_ERR "usbin_completed: panic: unknown URB\n"); + } + spin_lock_irqsave(&as->lock, flags); + if (!usbin_retire_desc(u, urb) && + u->flags & FLG_RUNNING && + !usbin_prepare_desc(u, urb) && + (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) { + u->flags |= mask; + } else { + u->flags &= ~(mask | FLG_RUNNING); + wake_up(&u->dma.wait); + printk(KERN_DEBUG "usbin_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret); + } + spin_unlock_irqrestore(&as->lock, flags); +} - usb_request_irq(dev, - usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), - usb_audio_irq, - endpoint->bInterval, - aud); +/* + * we output sync data + */ +static int usbin_sync_prepare_desc(struct usbin *u, purb_t urb) +{ + unsigned char *cp = urb->transfer_buffer; + unsigned int i, offs; - list_add(&aud->list, &usb_audio_list); + for (i = offs = 0; i < SYNCFRAMES; i++, offs += 3, cp += 3) { + urb->iso_frame_desc[i].length = 3; + urb->iso_frame_desc[i].offset = offs; + cp[0] = u->freqn; + cp[1] = u->freqn >> 8; + cp[2] = u->freqn >> 16; } return 0; } -static void usb_audio_disconnect(struct usb_device *dev) +/* + * return value: 0 if descriptor should be restarted, -1 otherwise + */ +static int usbin_sync_retire_desc(struct usbin *u, purb_t urb) +{ + unsigned int i; + + for (i = 0; i < SYNCFRAMES; i++) + if (urb->iso_frame_desc[0].status) + printk(KERN_DEBUG "usbin_sync_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status); + return 0; +} + +static void usbin_sync_completed(struct urb *urb) { - struct usb_audio *aud = (struct usb_audio*) dev->private; - if(aud) - { - dev->private = NULL; - list_del(&aud->list); - kfree(aud); + struct usb_audiodev *as = (struct usb_audiodev *)urb->context; + struct usbin *u = &as->usbin; + unsigned long flags; + unsigned int mask; + int suret = USB_ST_NOERROR; + +#if 0 + printk(KERN_DEBUG "usbin_sync_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags); +#endif + if (urb == &u->surb[0].urb) + mask = FLG_SYNC0RUNNING; + else if (urb == &u->surb[1].urb) + mask = FLG_SYNC1RUNNING; + else { + mask = 0; + printk(KERN_ERR "usbin_sync_completed: panic: unknown URB\n"); + } + spin_lock_irqsave(&as->lock, flags); + if (!usbin_sync_retire_desc(u, urb) && + u->flags & FLG_RUNNING && + !usbin_sync_prepare_desc(u, urb) && + (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) { + u->flags |= mask; + } else { + u->flags &= ~(mask | FLG_RUNNING); + wake_up(&u->dma.wait); + printk(KERN_DEBUG "usbin_sync_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret); } - printk(KERN_INFO "USB audio driver removed.\n"); + spin_unlock_irqrestore(&as->lock, flags); } -int usb_audio_init(void) +static int usbin_start(struct usb_audiodev *as) { - usb_register(&usb_audio_driver); + struct usb_device *dev = as->state->usbdev; + struct usbin *u = &as->usbin; + purb_t urb; + unsigned long flags; + unsigned int maxsze, bufsz; + +#if 0 + printk(KERN_DEBUG "usbin_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n", + dev->devnum, u->format, u->dma.format, u->dma.srate); +#endif + /* allocate USB storage if not already done */ + spin_lock_irqsave(&as->lock, flags); + if (!(u->flags & FLG_CONNECTED)) { + spin_unlock_irqrestore(&as->lock, flags); + return -EIO; + } + if (!(u->flags & FLG_RUNNING)) { + spin_unlock_irqrestore(&as->lock, flags); + u->freqn = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */ + u->freqmax = u->freqn + (u->freqn >> 2); + u->phase = 0; + maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); + bufsz = DESCFRAMES * maxsze; + if (u->durb[0].urb.transfer_buffer) + kfree(u->durb[0].urb.transfer_buffer); + u->durb[0].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL); + u->durb[0].urb.transfer_buffer_length = bufsz; + if (u->durb[1].urb.transfer_buffer) + kfree(u->durb[1].urb.transfer_buffer); + u->durb[1].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL); + u->durb[1].urb.transfer_buffer_length = bufsz; + if (u->syncpipe) { + if (u->surb[0].urb.transfer_buffer) + kfree(u->surb[0].urb.transfer_buffer); + u->surb[0].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); + u->surb[0].urb.transfer_buffer_length = 3*SYNCFRAMES; + if (u->surb[1].urb.transfer_buffer) + kfree(u->surb[1].urb.transfer_buffer); + u->surb[1].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); + u->surb[1].urb.transfer_buffer_length = 3*SYNCFRAMES; + } + if (!u->durb[0].urb.transfer_buffer || !u->durb[1].urb.transfer_buffer || + (u->syncpipe && (!u->surb[0].urb.transfer_buffer || !u->surb[1].urb.transfer_buffer))) { + printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum); + return 0; + } + spin_lock_irqsave(&as->lock, flags); + } + if (u->dma.count >= u->dma.dmasize && !u->dma.mapped) + return 0; + u->flags |= FLG_RUNNING; + if (!(u->flags & FLG_URB0RUNNING)) { + urb = &u->durb[0].urb; + urb->dev = dev; + urb->pipe = u->datapipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = DESCFRAMES; + urb->context = as; + urb->complete = usbin_completed; + if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_URB0RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + if (u->flags & FLG_RUNNING && !(u->flags & FLG_URB1RUNNING)) { + urb = &u->durb[1].urb; + urb->dev = dev; + urb->pipe = u->datapipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = DESCFRAMES; + urb->context = as; + urb->complete = usbin_completed; + if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_URB1RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + if (u->syncpipe) { + if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC0RUNNING)) { + urb = &u->surb[0].urb; + urb->dev = dev; + urb->pipe = u->syncpipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = SYNCFRAMES; + urb->context = as; + urb->complete = usbin_sync_completed; + /* stride: u->syncinterval */ + if (!usbin_sync_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_SYNC0RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC1RUNNING)) { + urb = &u->surb[1].urb; + urb->dev = dev; + urb->pipe = u->syncpipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = SYNCFRAMES; + urb->context = as; + urb->complete = usbin_sync_completed; + /* stride: u->syncinterval */ + if (!usbin_sync_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_SYNC1RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + } + spin_unlock_irqrestore(&as->lock, flags); + return 0; +} + +static void usbout_stop(struct usb_audiodev *as) +{ + struct usbout *u = &as->usbout; + unsigned long flags; + unsigned int i, notkilled = 1; + + spin_lock_irqsave(&as->lock, flags); + u->flags &= ~FLG_RUNNING; + i = u->flags; + spin_unlock_irqrestore(&as->lock, flags); + while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) { + set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + spin_lock_irqsave(&as->lock, flags); + i = u->flags; + spin_unlock_irqrestore(&as->lock, flags); + if (notkilled && signal_pending(current)) { + if (i & FLG_URB0RUNNING) + usb_unlink_urb(&u->durb[0].urb); + if (i & FLG_URB1RUNNING) + usb_unlink_urb(&u->durb[1].urb); + if (i & FLG_SYNC0RUNNING) + usb_unlink_urb(&u->surb[0].urb); + if (i & FLG_SYNC1RUNNING) + usb_unlink_urb(&u->surb[1].urb); + notkilled = 0; + } + } + set_current_state(TASK_RUNNING); + if (u->durb[0].urb.transfer_buffer) + kfree(u->durb[0].urb.transfer_buffer); + if (u->durb[1].urb.transfer_buffer) + kfree(u->durb[1].urb.transfer_buffer); + if (u->surb[0].urb.transfer_buffer) + kfree(u->surb[0].urb.transfer_buffer); + if (u->surb[1].urb.transfer_buffer) + kfree(u->surb[1].urb.transfer_buffer); + u->durb[0].urb.transfer_buffer = u->durb[1].urb.transfer_buffer = + u->surb[0].urb.transfer_buffer = u->surb[1].urb.transfer_buffer = NULL; +} + +static inline void usbout_release(struct usb_audiodev *as) +{ + usbout_stop(as); +} + +static void usbout_disc(struct usb_audiodev *as) +{ + struct usbout *u = &as->usbout; + unsigned long flags; + + spin_lock_irqsave(&as->lock, flags); + u->flags &= ~(FLG_RUNNING | FLG_CONNECTED); + spin_unlock_irqrestore(&as->lock, flags); + usbout_stop(as); +} + +static void usbout_convert(struct usbout *u, unsigned char *buffer, unsigned int samples) +{ + union { + __s16 s[64]; + unsigned char b[0]; + } tmp; + unsigned int scnt, maxs, ufmtsh, dfmtsh; + + ufmtsh = AFMT_BYTESSHIFT(u->format); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64; + while (samples > 0) { + scnt = samples; + if (scnt > maxs) + scnt = maxs; + dmabuf_copyout(&u->dma, tmp.b, scnt << dfmtsh); + conversion(tmp.b, u->dma.format, buffer, u->format, tmp.b, scnt); + buffer += scnt << ufmtsh; + samples -= scnt; + } +} + +static int usbout_prepare_desc(struct usbout *u, purb_t urb) +{ + unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, offs; + unsigned char *cp = urb->transfer_buffer; + + ufmtsh = AFMT_BYTESSHIFT(u->format); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + for (i = offs = 0; i < DESCFRAMES; i++) { + urb->iso_frame_desc[i].offset = offs; + u->phase = (u->phase & 0x3fff) + u->freqm; + scnt = u->phase >> 14; + if (!scnt) { + urb->iso_frame_desc[i].length = 0; + continue; + } + cnt = scnt << dfmtsh; + if (!u->dma.mapped) { + if (cnt > u->dma.count) { + scnt = u->dma.count >> dfmtsh; + cnt = scnt << dfmtsh; + err++; + } + u->dma.count -= cnt; + } else + u->dma.count += cnt; + if (u->format == u->dma.format) { + /* we do not need format conversion */ + dmabuf_copyout(&u->dma, cp, cnt); + } else { + /* we need sampling format conversion */ + usbout_convert(u, cp, scnt); + } + cnt = scnt << ufmtsh; + urb->iso_frame_desc[i].length = cnt; + offs += cnt; + cp += cnt; + } + if (err) + u->dma.error++; + if (u->dma.mapped) { + if (u->dma.count >= (signed)u->dma.fragsize) + wake_up(&u->dma.wait); + } else { + if ((signed)u->dma.dmasize >= u->dma.count + (signed)u->dma.fragsize) + wake_up(&u->dma.wait); + } + return err ? -1 : 0; +} + +/* + * return value: 0 if descriptor should be restarted, -1 otherwise + */ +static int usbout_retire_desc(struct usbout *u, purb_t urb) +{ + unsigned int i; + + for (i = 0; i < DESCFRAMES; i++) { + if (urb->iso_frame_desc[i].status) { + printk(KERN_DEBUG "usbout_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status); + continue; + } + } + return 0; +} + +static void usbout_completed(struct urb *urb) +{ + struct usb_audiodev *as = (struct usb_audiodev *)urb->context; + struct usbout *u = &as->usbout; + unsigned long flags; + unsigned int mask; + int suret = USB_ST_NOERROR; + +#if 0 + printk(KERN_DEBUG "usbout_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags); +#endif + if (urb == &u->durb[0].urb) + mask = FLG_URB0RUNNING; + else if (urb == &u->durb[1].urb) + mask = FLG_URB1RUNNING; + else { + mask = 0; + printk(KERN_ERR "usbout_completed: panic: unknown URB\n"); + } + spin_lock_irqsave(&as->lock, flags); + if (!usbout_retire_desc(u, urb) && + u->flags & FLG_RUNNING && + !usbout_prepare_desc(u, urb) && + (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) { + u->flags |= mask; + } else { + u->flags &= ~(mask | FLG_RUNNING); + wake_up(&u->dma.wait); + printk(KERN_DEBUG "usbout_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret); + } + spin_unlock_irqrestore(&as->lock, flags); +} + +static int usbout_sync_prepare_desc(struct usbout *u, purb_t urb) +{ + unsigned int i, offs; + + for (i = offs = 0; i < SYNCFRAMES; i++, offs += 3) { + urb->iso_frame_desc[i].length = 3; + urb->iso_frame_desc[i].offset = offs; + } return 0; } /* - * Support functions for parsing + * return value: 0 if descriptor should be restarted, -1 otherwise */ +static int usbout_sync_retire_desc(struct usbout *u, purb_t urb) +{ + unsigned char *cp = urb->transfer_buffer; + unsigned int f, i; + + for (i = 0; i < SYNCFRAMES; i++, cp += 3) { + if (urb->iso_frame_desc[i].status) { + printk(KERN_DEBUG "usbout_sync_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status); + continue; + } + if (urb->iso_frame_desc[i].actual_length < 3) { + printk(KERN_DEBUG "usbout_sync_retire_desc: frame %u length %d\n", i, urb->iso_frame_desc[i].actual_length); + continue; + } + f = cp[0] | (cp[1] << 8) | (cp[2] << 16); + if (abs(f - u->freqn) > (u->freqn >> 3) || f > u->freqmax) { + printk(KERN_WARNING "usbout_sync_retire_desc: requested frequency %u (nominal %u) out of range!\n", f, u->freqn); + continue; + } + u->freqm = f; + } + return 0; +} + +static void usbout_sync_completed(struct urb *urb) +{ + struct usb_audiodev *as = (struct usb_audiodev *)urb->context; + struct usbout *u = &as->usbout; + unsigned long flags; + unsigned int mask; + int suret = USB_ST_NOERROR; + +#if 0 + printk(KERN_DEBUG "usbout_sync_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags); +#endif + if (urb == &u->surb[0].urb) + mask = FLG_SYNC0RUNNING; + else if (urb == &u->surb[1].urb) + mask = FLG_SYNC1RUNNING; + else { + mask = 0; + printk(KERN_ERR "usbout_sync_completed: panic: unknown URB\n"); + } + spin_lock_irqsave(&as->lock, flags); + if (!usbout_sync_retire_desc(u, urb) && + u->flags & FLG_RUNNING && + !usbout_sync_prepare_desc(u, urb) && + (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) { + u->flags |= mask; + } else { + u->flags &= ~(mask | FLG_RUNNING); + wake_up(&u->dma.wait); + printk(KERN_DEBUG "usbout_sync_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret); + } + spin_unlock_irqrestore(&as->lock, flags); +} + +static int usbout_start(struct usb_audiodev *as) +{ + struct usb_device *dev = as->state->usbdev; + struct usbout *u = &as->usbout; + purb_t urb; + unsigned long flags; + unsigned int maxsze, bufsz; + +#if 0 + printk(KERN_DEBUG "usbout_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n", + dev->devnum, u->format, u->dma.format, u->dma.srate); +#endif + /* allocate USB storage if not already done */ + spin_lock_irqsave(&as->lock, flags); + if (!(u->flags & FLG_CONNECTED)) { + spin_unlock_irqrestore(&as->lock, flags); + return -EIO; + } + if (!(u->flags & FLG_RUNNING)) { + spin_unlock_irqrestore(&as->lock, flags); + u->freqn = u->freqm = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */ + u->freqmax = u->freqn + (u->freqn >> 2); + u->phase = 0; + maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); + bufsz = DESCFRAMES * maxsze; + if (u->durb[0].urb.transfer_buffer) + kfree(u->durb[0].urb.transfer_buffer); + u->durb[0].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL); + u->durb[0].urb.transfer_buffer_length = bufsz; + if (u->durb[1].urb.transfer_buffer) + kfree(u->durb[1].urb.transfer_buffer); + u->durb[1].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL); + u->durb[1].urb.transfer_buffer_length = bufsz; + if (u->syncpipe) { + if (u->surb[0].urb.transfer_buffer) + kfree(u->surb[0].urb.transfer_buffer); + u->surb[0].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); + u->surb[0].urb.transfer_buffer_length = 3*SYNCFRAMES; + if (u->surb[1].urb.transfer_buffer) + kfree(u->surb[1].urb.transfer_buffer); + u->surb[1].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); + u->surb[1].urb.transfer_buffer_length = 3*SYNCFRAMES; + } + if (!u->durb[0].urb.transfer_buffer || !u->durb[1].urb.transfer_buffer || + (u->syncpipe && (!u->surb[0].urb.transfer_buffer || !u->surb[1].urb.transfer_buffer))) { + printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum); + return 0; + } + spin_lock_irqsave(&as->lock, flags); + } + if (u->dma.count <= 0 && !u->dma.mapped) + return 0; + u->flags |= FLG_RUNNING; + if (!(u->flags & FLG_URB0RUNNING)) { + urb = &u->durb[0].urb; + urb->dev = dev; + urb->pipe = u->datapipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = DESCFRAMES; + urb->context = as; + urb->complete = usbout_completed; + if (!usbout_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_URB0RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + if (u->flags & FLG_RUNNING && !(u->flags & FLG_URB1RUNNING)) { + urb = &u->durb[1].urb; + urb->dev = dev; + urb->pipe = u->datapipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = DESCFRAMES; + urb->context = as; + urb->complete = usbout_completed; + if (!usbout_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_URB1RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + if (u->syncpipe) { + if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC0RUNNING)) { + urb = &u->surb[0].urb; + urb->dev = dev; + urb->pipe = u->syncpipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = SYNCFRAMES; + urb->context = as; + urb->complete = usbout_sync_completed; + /* stride: u->syncinterval */ + if (!usbout_sync_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_SYNC0RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC1RUNNING)) { + urb = &u->surb[1].urb; + urb->dev = dev; + urb->pipe = u->syncpipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = SYNCFRAMES; + urb->context = as; + urb->complete = usbout_sync_completed; + /* stride: u->syncinterval */ + if (!usbout_sync_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_SYNC1RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + } + spin_unlock_irqrestore(&as->lock, flags); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static unsigned int format_goodness(struct audioformat *afp, unsigned int fmt, unsigned int srate) +{ + unsigned int g = 0; + + if (srate < afp->sratelo) + g += afp->sratelo - srate; + if (srate > afp->sratehi) + g += srate - afp->sratehi; + if (AFMT_ISSTEREO(afp->format) && !AFMT_ISSTEREO(fmt)) + g += 0x100000; + if (!AFMT_ISSTEREO(afp->format) && AFMT_ISSTEREO(fmt)) + g += 0x400000; + if (AFMT_IS16BIT(afp->format) && !AFMT_IS16BIT(fmt)) + g += 0x100000; + if (!AFMT_IS16BIT(afp->format) && AFMT_IS16BIT(fmt)) + g += 0x400000; + return g; +} + +static int find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt, unsigned int srate) +{ + unsigned int i, g, gb = ~0; + int j = -1; /* default to failure */ + + /* find "best" format (according to format_goodness) */ + for (i = 0; i < nr; i++) { + g = format_goodness(&afp[i], fmt, srate); + if (g >= gb) + continue; + j = i; + gb = g; + } + return j; +} + +static int set_format_in(struct usb_audiodev *as) +{ + struct usb_device *dev = as->state->usbdev; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface_descriptor *alts; + struct usb_interface *iface; + struct usbin *u = &as->usbin; + struct dmabuf *d = &u->dma; + struct audioformat *fmt; + unsigned int ep; + unsigned char data[3]; + int fmtnr, ret; + + if (u->interface < 0 || u->interface >= config->bNumInterfaces) + return 0; + iface = &config->interface[u->interface]; + + fmtnr = find_format(as->fmtin, as->numfmtin, d->format, d->srate); + if (fmtnr < 0) { + printk(KERN_ERR "usbaudio: set_format_in(): failed to find desired format/speed combination.\n"); + return -1; + } + + fmt = as->fmtin + fmtnr; + alts = &iface->altsetting[fmt->altsetting]; + u->format = fmt->format; + u->datapipe = usb_rcvisocpipe(dev, alts->endpoint[0].bEndpointAddress & 0xf); + u->syncpipe = u->syncinterval = 0; + if ((alts->endpoint[0].bmAttributes & 0x0c) == 0x08) { + if (alts->bNumEndpoints < 2 || + alts->endpoint[1].bmAttributes != 0x01 || + alts->endpoint[1].bSynchAddress != 0 || + alts->endpoint[1].bEndpointAddress != (alts->endpoint[0].bSynchAddress & 0x7f)) { + printk(KERN_ERR "usbaudio: device %d interface %d altsetting %d invalid synch pipe\n", + dev->devnum, u->interface, fmt->altsetting); + return -1; + } + u->syncpipe = usb_sndisocpipe(dev, alts->endpoint[1].bEndpointAddress & 0xf); + u->syncinterval = alts->endpoint[1].bRefresh; + } + if (d->srate < fmt->sratelo) + d->srate = fmt->sratelo; + if (d->srate > fmt->sratehi) + d->srate = fmt->sratehi; +#if 1 + printk(KERN_DEBUG "usb_audio: set_format_in: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting); +#endif + if (usb_set_interface(dev, alts->bInterfaceNumber, fmt->altsetting) < 0) { + printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n", + dev->devnum, u->interface, fmt->altsetting); + return -1; + } + if (fmt->sratelo == fmt->sratehi) + return 0; + ep = usb_pipeendpoint(u->datapipe) | (u->datapipe & USB_DIR_IN); + /* if endpoint has pitch control, enable it */ + if (fmt->attributes & 0x02) { + data[0] = 1; + if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + PITCH_CONTROL << 8, ep, data, 1, HZ)) < 0) { + printk(KERN_ERR "usbaudio: failure (error %d) to set output pitch control device %d interface %u endpoint 0x%x to %u\n", + ret, dev->devnum, u->interface, ep, d->srate); + return -1; + } + } + /* if endpoint has sampling rate control, set it */ + if (fmt->attributes & 0x01) { + data[0] = d->srate; + data[1] = d->srate >> 8; + data[2] = d->srate >> 16; + if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + printk(KERN_ERR "usbaudio: failure (error %d) to set input sampling frequency device %d interface %u endpoint 0x%x to %u\n", + ret, dev->devnum, u->interface, ep, d->srate); + return -1; + } + if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + printk(KERN_ERR "usbaudio: failure (error %d) to get input sampling frequency device %d interface %u endpoint 0x%x\n", + ret, dev->devnum, u->interface, ep); + return -1; + } + printk(KERN_DEBUG "usbaudio: set_format_in: device %d interface %d altsetting %d srate req: %u real %u\n", + dev->devnum, u->interface, fmt->altsetting, d->srate, data[0] | (data[1] << 8) | (data[2] << 16)); + d->srate = data[0] | (data[1] << 8) | (data[2] << 16); + } + return 0; +} + +static int set_format_out(struct usb_audiodev *as) +{ + struct usb_device *dev = as->state->usbdev; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface_descriptor *alts; + struct usb_interface *iface; + struct usbout *u = &as->usbout; + struct dmabuf *d = &u->dma; + struct audioformat *fmt; + unsigned int ep; + unsigned char data[3]; + int fmtnr, ret; + + if (u->interface < 0 || u->interface >= config->bNumInterfaces) + return 0; + iface = &config->interface[u->interface]; + + fmtnr = find_format(as->fmtout, as->numfmtout, d->format, d->srate); + if (fmtnr < 0) { + printk(KERN_ERR "usbaudio: set_format_out(): failed to find desired format/speed combination.\n"); + return -1; + } + + fmt = as->fmtout + fmtnr; + u->format = fmt->format; + alts = &iface->altsetting[fmt->altsetting]; + u->datapipe = usb_sndisocpipe(dev, alts->endpoint[0].bEndpointAddress & 0xf); + u->syncpipe = u->syncinterval = 0; + if ((alts->endpoint[0].bmAttributes & 0x0c) == 0x04) { +#if 0 + printk(KERN_DEBUG "bNumEndpoints 0x%02x endpoint[1].bmAttributes 0x%02x\n" + KERN_DEBUG "endpoint[1].bSynchAddress 0x%02x endpoint[1].bEndpointAddress 0x%02x\n" + KERN_DEBUG "endpoint[0].bSynchAddress 0x%02x\n", alts->bNumEndpoints, + alts->endpoint[1].bmAttributes, alts->endpoint[1].bSynchAddress, + alts->endpoint[1].bEndpointAddress, alts->endpoint[0].bSynchAddress); +#endif + if (alts->bNumEndpoints < 2 || + alts->endpoint[1].bmAttributes != 0x01 || + alts->endpoint[1].bSynchAddress != 0 || + alts->endpoint[1].bEndpointAddress != (alts->endpoint[0].bSynchAddress | 0x80)) { + printk(KERN_ERR "usbaudio: device %d interface %d altsetting %d invalid synch pipe\n", + dev->devnum, u->interface, fmt->altsetting); + return -1; + } + u->syncpipe = usb_rcvisocpipe(dev, alts->endpoint[1].bEndpointAddress & 0xf); + u->syncinterval = alts->endpoint[1].bRefresh; + } + if (d->srate < fmt->sratelo) + d->srate = fmt->sratelo; + if (d->srate > fmt->sratehi) + d->srate = fmt->sratehi; +#if 1 + printk(KERN_DEBUG "usb_audio: set_format_out: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting); +#endif + if (usb_set_interface(dev, u->interface, fmt->altsetting) < 0) { + printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n", + dev->devnum, u->interface, fmt->altsetting); + return -1; + } + if (fmt->sratelo == fmt->sratehi) + return 0; + ep = usb_pipeendpoint(u->datapipe) | (u->datapipe & USB_DIR_IN); + /* if endpoint has pitch control, enable it */ + if (fmt->attributes & 0x02) { + data[0] = 1; + if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + PITCH_CONTROL << 8, ep, data, 1, HZ)) < 0) { + printk(KERN_ERR "usbaudio: failure (error %d) to set output pitch control device %d interface %u endpoint 0x%x to %u\n", + ret, dev->devnum, u->interface, ep, d->srate); + return -1; + } + } + /* if endpoint has sampling rate control, set it */ + if (fmt->attributes & 0x01) { + data[0] = d->srate; + data[1] = d->srate >> 8; + data[2] = d->srate >> 16; + if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + printk(KERN_ERR "usbaudio: failure (error %d) to set output sampling frequency device %d interface %u endpoint 0x%x to %u\n", + ret, dev->devnum, u->interface, ep, d->srate); + return -1; + } + if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + printk(KERN_ERR "usbaudio: failure (error %d) to get output sampling frequency device %d interface %u endpoint 0x%x\n", + ret, dev->devnum, u->interface, ep); + return -1; + } + printk(KERN_DEBUG "usbaudio: set_format_out: device %d interface %d altsetting %d srate req: %u real %u\n", + dev->devnum, u->interface, fmt->altsetting, d->srate, data[0] | (data[1] << 8) | (data[2] << 16)); + d->srate = data[0] | (data[1] << 8) | (data[2] << 16); + } + return 0; +} + +static int set_format(struct usb_audiodev *s, unsigned int fmode, unsigned int fmt, unsigned int srate) +{ + int ret1 = 0, ret2 = 0; + + if (!(fmode & (FMODE_READ|FMODE_WRITE))) + return -EINVAL; + if (fmode & FMODE_READ) { + usbin_stop(s); + s->usbin.dma.ready = 0; + if (fmt == AFMT_QUERY) + fmt = s->usbin.dma.format; + else + s->usbin.dma.format = fmt; + if (!srate) + srate = s->usbin.dma.srate; + else + s->usbin.dma.srate = srate; + } + if (fmode & FMODE_WRITE) { + usbout_stop(s); + s->usbout.dma.ready = 0; + if (fmt == AFMT_QUERY) + fmt = s->usbout.dma.format; + else + s->usbout.dma.format = fmt; + if (!srate) + srate = s->usbout.dma.srate; + else + s->usbout.dma.srate = srate; + } + if (fmode & FMODE_READ) + ret1 = set_format_in(s); + if (fmode & FMODE_WRITE) + ret2 = set_format_out(s); + return ret1 ? ret1 : ret2; +} + +/* --------------------------------------------------------------------- */ + +static int wrmixer(struct usb_mixerdev *ms, unsigned mixch, unsigned value) +{ + struct usb_device *dev = ms->state->usbdev; + unsigned char data[2]; + struct mixerchannel *ch; + int v1, v2, v3; + + if (mixch >= ms->numch) + return -1; + ch = &ms->ch[mixch]; + v3 = ch->maxval - ch->minval; + v1 = value & 0xff; + v2 = (value >> 8) & 0xff; + if (v1 > 100) + v1 = 100; + if (v2 > 100) + v2 = 100; + if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) + v2 = v1; + ch->value = v1 | (v2 << 8); + v1 = (v1 * v3) / 100 + ch->minval; + v2 = (v2 * v3) / 100 + ch->minval; + switch (ch->selector) { + case 0: /* mixer unit request */ + data[0] = v1; + data[1] = v1 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->chnum << 8) | 1, ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; + if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) + return 0; + data[0] = v2; + data[1] = v2 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + ((ch->chnum + !!(ch->flags & MIXFLG_STEREOIN)) << 8) | (1 + !!(ch->flags & MIXFLG_STEREOOUT)), + ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; + return 0; + + /* various feature unit controls */ + case VOLUME_CONTROL: + data[0] = v1; + data[1] = v1 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; + if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) + return 0; + data[0] = v2; + data[1] = v2 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; + return 0; + + case BASS_CONTROL: + case MID_CONTROL: + case TREBLE_CONTROL: + data[0] = v1 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 1, HZ) < 0) + goto err; + if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) + return 0; + data[0] = v2 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 1, HZ) < 0) + goto err; + return 0; + + default: + return -1; + } + return 0; + + err: + printk(KERN_ERR "usbaudio: mixer request device %u if %u unit %u ch %u selector %u failed\n", + dev->devnum, ms->iface, ch->unitid, ch->chnum, ch->selector); + return -1; +} + +static int get_rec_src(struct usb_mixerdev *ms) +{ + struct usb_device *dev = ms->state->usbdev; + unsigned int mask = 0, retmask = 0; + unsigned int i, j; + unsigned char buf; + int err = 0; + + for (i = 0; i < ms->numch; i++) { + if (!ms->ch[i].slctunitid || (mask & (1 << i))) + continue; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + 0, ms->iface | (ms->ch[i].slctunitid << 8), &buf, 1, HZ) < 0) { + err = -EIO; + printk(KERN_ERR "usbaudio: selector read request device %u if %u unit %u failed\n", + dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff); + continue; + } + for (j = i; j < ms->numch; j++) { + if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) + continue; + mask |= 1 << j; + if (buf == (ms->ch[j].slctunitid >> 8)) + retmask |= 1 << ms->ch[j].osschannel; + } + } + if (err) + return -EIO; + return retmask; +} + +static int set_rec_src(struct usb_mixerdev *ms, int srcmask) +{ + struct usb_device *dev = ms->state->usbdev; + unsigned int mask = 0, smask, bmask; + unsigned int i, j; + unsigned char buf; + int err = 0; + + for (i = 0; i < ms->numch; i++) { + if (!ms->ch[i].slctunitid || (mask & (1 << i))) + continue; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + 0, ms->iface | (ms->ch[i].slctunitid << 8), &buf, 1, HZ) < 0) { + err = -EIO; + printk(KERN_ERR "usbaudio: selector read request device %u if %u unit %u failed\n", + dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff); + continue; + } + /* first generate smask */ + smask = bmask = 0; + for (j = i; j < ms->numch; j++) { + if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) + continue; + smask |= 1 << ms->ch[j].osschannel; + if (buf == (ms->ch[j].slctunitid >> 8)) + bmask |= 1 << ms->ch[j].osschannel; + mask |= 1 << j; + } + /* check for multiple set sources */ + j = hweight32(srcmask & smask); + if (j == 0) + continue; + if (j > 1) + srcmask &= ~bmask; + for (j = i; j < ms->numch; j++) { + if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) + continue; + if (!(srcmask & (1 << ms->ch[j].osschannel))) + continue; + buf = ms->ch[j].slctunitid >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + 0, ms->iface | (ms->ch[j].slctunitid << 8), &buf, 1, HZ) < 0) { + err = -EIO; + printk(KERN_ERR "usbaudio: selector write request device %u if %u unit %u failed\n", + dev->devnum, ms->iface, ms->ch[j].slctunitid & 0xff); + continue; + } + } + } + return err ? -EIO : 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * should be called with open_sem hold, so that no new processes + * look at the audio device to be destroyed + */ + +static void release(struct usb_audio_state *s) +{ + struct usb_audiodev *as; + struct usb_mixerdev *ms; + + s->count--; + if (s->count) { + up(&open_sem); + return; + } + up(&open_sem); + wake_up(&open_wait); + while (!list_empty(&s->audiolist)) { + as = list_entry(s->audiolist.next, struct usb_audiodev, list); + list_del(&as->list); + usbin_release(as); + usbout_release(as); + dmabuf_release(&as->usbin.dma); + dmabuf_release(&as->usbout.dma); + kfree(as); + } + while (!list_empty(&s->mixerlist)) { + ms = list_entry(s->mixerlist.next, struct usb_mixerdev, list); + list_del(&ms->list); + kfree(ms); + } + kfree(s); +} + +extern inline int prog_dmabuf_in(struct usb_audiodev *as) +{ + usbin_stop(as); + return dmabuf_init(&as->usbin.dma); +} + +extern inline int prog_dmabuf_out(struct usb_audiodev *as) +{ + usbout_stop(as); + return dmabuf_init(&as->usbout.dma); +} + +/* --------------------------------------------------------------------- */ + +static loff_t usb_audio_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +/* --------------------------------------------------------------------- */ + +static int usb_audio_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct list_head *devs, *mdevs; + struct usb_mixerdev *ms; + struct usb_audio_state *s; + + down(&open_sem); + for (devs = audiodevs.next; devs != &audiodevs; devs = devs->next) { + s = list_entry(devs, struct usb_audio_state, audiodev); + for (mdevs = s->mixerlist.next; mdevs != &s->mixerlist; mdevs = mdevs->next) { + ms = list_entry(mdevs, struct usb_mixerdev, list); + if (ms->dev_mixer == minor) + goto mixer_found; + } + } + up(&open_sem); + return -ENODEV; + + mixer_found: + if (!s->usbdev) { + up(&open_sem); + return -EIO; + } + file->private_data = ms; + s->count++; + + up(&open_sem); + return 0; +} + +static int usb_audio_release_mixdev(struct inode *inode, struct file *file) +{ + struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data; + struct usb_audio_state *s = ms->state; + + down(&open_sem); + release(s); + return 0; +} + +static int usb_audio_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data; + int i, j, val; + + if (!ms->state->usbdev) + return -ENODEV; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "USB_AUDIO", sizeof(info.id)); + strncpy(info.name, "USB Audio Class Driver", sizeof(info.name)); + info.modify_counter = ms->modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "USB_AUDIO", sizeof(info.id)); + strncpy(info.name, "USB Audio Class Driver", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + val = get_rec_src(ms); + if (val < 0) + return val; + return put_user(val, (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < ms->numch; i++) + val |= 1 << ms->ch[i].osschannel; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < ms->numch; i++) + if (ms->ch[i].slctunitid) + val |= 1 << ms->ch[i].osschannel; + return put_user(val, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < ms->numch; i++) + if (ms->ch[i].flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) + val |= 1 << ms->ch[i].osschannel; + return put_user(val, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + for (j = 0; j < ms->numch; j++) { + if (ms->ch[j].osschannel == i) { + return put_user(ms->ch[j].value, (int *)arg); + } + } + return -EINVAL; + } + } + if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) + return -EINVAL; + ms->modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + get_user_ret(val, (int *)arg, -EFAULT); + return set_rec_src(ms, val); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + for (j = 0; j < ms->numch && ms->ch[j].osschannel != i; j++); + if (j >= ms->numch) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (wrmixer(ms, j, val)) + return -EIO; + return put_user(ms->ch[j].value, (int *)arg); + } +} + +static /*const*/ struct file_operations usb_mixer_fops = { + llseek: usb_audio_llseek, + ioctl: usb_audio_ioctl_mixdev, + open: usb_audio_open_mixdev, + release: usb_audio_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_out(struct usb_audiodev *as, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (as->usbout.dma.mapped || !as->usbout.dma.ready) + return 0; + usbout_start(as); + add_wait_queue(&as->usbout.dma.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&as->lock, flags); + count = as->usbout.dma.count; + spin_unlock_irqrestore(&as->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&as->usbout.dma.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * count / as->usbout.dma.srate; + tmo >>= AFMT_BYTESSHIFT(as->usbout.dma.format); + if (!schedule_timeout(tmo + 1)) { + printk(KERN_DEBUG "usbaudio: dma timed out??\n"); + break; + } + } + remove_wait_queue(&as->usbout.dma.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t usb_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned int ptr; + int cnt, err; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (as->usbin.dma.mapped) + return -ENXIO; + if (!as->usbin.dma.ready && (ret = prog_dmabuf_in(as))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + add_wait_queue(&as->usbin.dma.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&as->lock, flags); + ptr = as->usbin.dma.rdptr; + cnt = as->usbin.dma.count; + /* set task state early to avoid wakeup races */ + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&as->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (usbin_start(as)) { + if (!ret) + ret = -ENODEV; + break; + } + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if ((err = dmabuf_copyout_user(&as->usbin.dma, ptr, buffer, cnt))) { + if (!ret) + ret = err; + break; + } + ptr += cnt; + if (ptr >= as->usbin.dma.dmasize) + ptr -= as->usbin.dma.dmasize; + spin_lock_irqsave(&as->lock, flags); + as->usbin.dma.rdptr = ptr; + as->usbin.dma.count -= cnt; + spin_unlock_irqrestore(&as->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&as->usbin.dma.wait, &wait); + return ret; +} + +static ssize_t usb_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned int ptr; + unsigned int start_thr; + int cnt, err; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (as->usbout.dma.mapped) + return -ENXIO; + if (!as->usbout.dma.ready && (ret = prog_dmabuf_out(as))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + start_thr = (as->usbout.dma.srate << AFMT_BYTESSHIFT(as->usbout.dma.format)) / (1000 / (3 * DESCFRAMES)); + add_wait_queue(&as->usbout.dma.wait, &wait); + while (count > 0) { +#if 0 + printk(KERN_DEBUG "usb_audio_write: count %u dma: count %u rdptr %u wrptr %u dmasize %u fragsize %u flags 0x%02x taskst 0x%lx\n", + count, as->usbout.dma.count, as->usbout.dma.rdptr, as->usbout.dma.wrptr, as->usbout.dma.dmasize, as->usbout.dma.fragsize, + as->usbout.flags, current->state); +#endif + spin_lock_irqsave(&as->lock, flags); + if (as->usbout.dma.count < 0) { + as->usbout.dma.count = 0; + as->usbout.dma.rdptr = as->usbout.dma.wrptr; + } + ptr = as->usbout.dma.wrptr; + cnt = as->usbout.dma.dmasize - as->usbout.dma.count; + /* set task state early to avoid wakeup races */ + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&as->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (usbout_start(as)) { + if (!ret) + ret = -ENODEV; + break; + } + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if ((err = dmabuf_copyin_user(&as->usbout.dma, ptr, buffer, cnt))) { + if (!ret) + ret = err; + break; + } + ptr += cnt; + if (ptr >= as->usbout.dma.dmasize) + ptr -= as->usbout.dma.dmasize; + spin_lock_irqsave(&as->lock, flags); + as->usbout.dma.wrptr = ptr; + as->usbout.dma.count += cnt; + spin_unlock_irqrestore(&as->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (as->usbout.dma.count >= start_thr && usbout_start(as)) { + if (!ret) + ret = -ENODEV; + break; + } + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&as->usbout.dma.wait, &wait); + return ret; +} + +/* Called without the kernel lock - fine */ +static unsigned int usb_audio_poll(struct file *file, struct poll_table_struct *wait) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) { + if (!as->usbout.dma.ready) + prog_dmabuf_out(as); + poll_wait(file, &as->usbout.dma.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!as->usbin.dma.ready) + prog_dmabuf_in(as); + poll_wait(file, &as->usbin.dma.wait, wait); + } + spin_lock_irqsave(&as->lock, flags); + if (file->f_mode & FMODE_READ) { + if (as->usbin.dma.count >= (signed)as->usbin.dma.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (as->usbout.dma.mapped) { + if (as->usbout.dma.count >= (signed)as->usbout.dma.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)as->usbout.dma.dmasize >= as->usbout.dma.count + (signed)as->usbout.dma.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&as->lock, flags); + return mask; +} + +static int usb_audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + struct dmabuf *db; + int ret; + + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_out(as)) != 0) + return ret; + db = &as->usbout.dma; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_in(as)) != 0) + return ret; + db = &as->usbin.dma; + } else + return -EINVAL; + + return dmabuf_mmap(db, vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + +static int usb_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + struct usb_audio_state *s = as->state; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, val2, mapped, ret; + + if (!s->usbdev) + return -EIO; + mapped = ((file->f_mode & FMODE_WRITE) && as->usbout.dma.mapped) || + ((file->f_mode & FMODE_READ) && as->usbin.dma.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_out(as, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | + DSP_CAP_MMAP | DSP_CAP_BATCH, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + usbout_stop(as); + as->usbout.dma.rdptr = as->usbout.dma.wrptr = as->usbout.dma.count = as->usbout.dma.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + usbin_stop(as); + as->usbin.dma.rdptr = as->usbin.dma.wrptr = as->usbin.dma.count = as->usbin.dma.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (val < 4000) + val = 4000; + if (val > 100000) + val = 100000; + if (set_format(as, file->f_mode, AFMT_QUERY, val)) + return -EIO; + } + return put_user((file->f_mode & FMODE_READ) ? as->usbin.dma.srate : as->usbout.dma.srate, (int *)arg); + + case SNDCTL_DSP_STEREO: + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + if (set_format(as, file->f_mode, val2 | AFMT_STEREO, 0)) + return -EIO; + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + if (val == 1) + val2 &= ~AFMT_STEREO; + else + val2 |= AFMT_STEREO; + if (set_format(as, file->f_mode, val2, 0)) + return -EIO; + } + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE | + AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + if (hweight32(val) != 1) + return -EINVAL; + if (!(val & (AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE | + AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE))) + return -EINVAL; + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + val |= val2 & AFMT_STEREO; + if (set_format(as, file->f_mode, val, 0)) + return -EIO; + } + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + return put_user(val2 & ~AFMT_STEREO, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && as->usbin.flags & FLG_RUNNING) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && as->usbout.flags & FLG_RUNNING) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!as->usbin.dma.ready && (ret = prog_dmabuf_in(as))) + return ret; + if (usbin_start(as)) + return -ENODEV; + } else + usbin_stop(as); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!as->usbout.dma.ready && (ret = prog_dmabuf_out(as))) + return ret; + if (usbout_start(as)) + return -ENODEV; + } else + usbout_stop(as); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(as->usbout.flags & FLG_RUNNING) && (val = prog_dmabuf_out(as)) != 0) + return val; + spin_lock_irqsave(&as->lock, flags); + abinfo.fragsize = as->usbout.dma.fragsize; + abinfo.bytes = as->usbout.dma.dmasize - as->usbout.dma.count; + abinfo.fragstotal = as->usbout.dma.numfrag; + abinfo.fragments = abinfo.bytes >> as->usbout.dma.fragshift; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!(as->usbin.flags & FLG_RUNNING) && (val = prog_dmabuf_in(as)) != 0) + return val; + spin_lock_irqsave(&as->lock, flags); + abinfo.fragsize = as->usbin.dma.fragsize; + abinfo.bytes = as->usbin.dma.count; + abinfo.fragstotal = as->usbin.dma.numfrag; + abinfo.fragments = abinfo.bytes >> as->usbin.dma.fragshift; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&as->lock, flags); + val = as->usbout.dma.count; + spin_unlock_irqrestore(&as->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&as->lock, flags); + cinfo.bytes = as->usbin.dma.total_bytes; + cinfo.blocks = as->usbin.dma.count >> as->usbin.dma.fragshift; + cinfo.ptr = as->usbin.dma.wrptr; + if (as->usbin.dma.mapped) + as->usbin.dma.count &= as->usbin.dma.fragsize-1; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&as->lock, flags); + cinfo.bytes = as->usbout.dma.total_bytes; + cinfo.blocks = as->usbout.dma.count >> as->usbout.dma.fragshift; + cinfo.ptr = as->usbout.dma.rdptr; + if (as->usbout.dma.mapped) + as->usbout.dma.count &= as->usbout.dma.fragsize-1; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_out(as))) + return val; + return put_user(as->usbout.dma.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_in(as))) + return val; + return put_user(as->usbin.dma.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + as->usbin.dma.ossfragshift = val & 0xffff; + as->usbin.dma.ossmaxfrags = (val >> 16) & 0xffff; + if (as->usbin.dma.ossfragshift < 4) + as->usbin.dma.ossfragshift = 4; + if (as->usbin.dma.ossfragshift > 15) + as->usbin.dma.ossfragshift = 15; + if (as->usbin.dma.ossmaxfrags < 4) + as->usbin.dma.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + as->usbout.dma.ossfragshift = val & 0xffff; + as->usbout.dma.ossmaxfrags = (val >> 16) & 0xffff; + if (as->usbout.dma.ossfragshift < 4) + as->usbout.dma.ossfragshift = 4; + if (as->usbout.dma.ossfragshift > 15) + as->usbout.dma.ossfragshift = 15; + if (as->usbout.dma.ossmaxfrags < 4) + as->usbout.dma.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && as->usbin.dma.subdivision) || + (file->f_mode & FMODE_WRITE && as->usbout.dma.subdivision)) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + as->usbin.dma.subdivision = val; + if (file->f_mode & FMODE_WRITE) + as->usbout.dma.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? as->usbin.dma.srate : as->usbout.dma.srate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + return put_user(AFMT_IS16BIT(val2) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -ENOIOCTLCMD; +} + +static int usb_audio_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct list_head *devs, *adevs; + struct usb_audiodev *as; + struct usb_audio_state *s; + + for (;;) { + down(&open_sem); + for (devs = audiodevs.next; devs != &audiodevs; devs = devs->next) { + s = list_entry(devs, struct usb_audio_state, audiodev); + for (adevs = s->audiolist.next; adevs != &s->audiolist; adevs = adevs->next) { + as = list_entry(adevs, struct usb_audiodev, list); + if (!((as->dev_audio ^ minor) & ~0xf)) + goto device_found; + } + } + up(&open_sem); + return -ENODEV; + + device_found: + if (!s->usbdev) { + up(&open_sem); + return -EIO; + } + /* wait for device to become free */ + if (!(as->open_mode & file->f_mode)) + break; + if (file->f_flags & O_NONBLOCK) { + up(&open_sem); + return -EBUSY; + } + __set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&open_wait, &wait); + up(&open_sem); + schedule(); + __set_current_state(TASK_RUNNING); + remove_wait_queue(&open_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + } + if (file->f_mode & FMODE_READ) + as->usbin.dma.ossfragshift = as->usbin.dma.ossmaxfrags = as->usbin.dma.subdivision = 0; + if (file->f_mode & FMODE_WRITE) + as->usbout.dma.ossfragshift = as->usbout.dma.ossmaxfrags = as->usbout.dma.subdivision = 0; + if (set_format(as, file->f_mode, ((minor & 0xf) == SND_DEV_DSP16) ? AFMT_S16_LE : AFMT_U8 /* AFMT_ULAW */, 8000)) { + up(&open_sem); + return -EIO; + } + file->private_data = as; + as->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + s->count++; + up(&open_sem); + return 0; +} + +static int usb_audio_release(struct inode *inode, struct file *file) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + struct usb_audio_state *s = as->state; + struct usb_device *dev = s->usbdev; + struct usb_interface *iface; + + if (file->f_mode & FMODE_WRITE) + drain_out(as, file->f_flags & O_NONBLOCK); + down(&open_sem); + if (file->f_mode & FMODE_WRITE) { + usbout_stop(as); + if (dev && as->usbout.interface >= 0) { + iface = &dev->actconfig->interface[as->usbout.interface]; + usb_set_interface(dev, iface->altsetting->bInterfaceNumber, 0); + } + dmabuf_release(&as->usbout.dma); + usbout_release(as); + } + if (file->f_mode & FMODE_READ) { + usbin_stop(as); + if (dev && as->usbin.interface >= 0) { + iface = &dev->actconfig->interface[as->usbin.interface]; + usb_set_interface(dev, iface->altsetting->bInterfaceNumber, 0); + } + dmabuf_release(&as->usbin.dma); + usbin_release(as); + } + as->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + release(s); + wake_up(&open_wait); + return 0; +} + +static /*const*/ struct file_operations usb_audio_fops = { + llseek: usb_audio_llseek, + read: usb_audio_read, + write: usb_audio_write, + poll: usb_audio_poll, + ioctl: usb_audio_ioctl, + mmap: usb_audio_mmap, + open: usb_audio_open, + release: usb_audio_release, +}; + +/* --------------------------------------------------------------------- */ + +static void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum); +static void usb_audio_disconnect(struct usb_device *dev, void *ptr); + +static struct usb_driver usb_audio_driver = { + "audio", + usb_audio_probe, + usb_audio_disconnect, + LIST_HEAD_INIT(usb_audio_driver.driver_list), + NULL, + 0 +}; + +static void *find_descriptor(void *descstart, unsigned int desclen, void *after, + u8 dtype, int iface, int altsetting) +{ + u8 *p, *end, *next; + int ifc = -1, as = -1; + + p = descstart; + end = p + desclen; + for (; p < end;) { + if (p[0] < 2) + return NULL; + next = p + p[0]; + if (next > end) + return NULL; + if (p[1] == USB_DT_INTERFACE) { + /* minimum length of interface descriptor */ + if (p[0] < 9) + return NULL; + ifc = p[2]; + as = p[3]; + } + if (p[1] == dtype && (!after || (void *)p > after) && + (iface == -1 || iface == ifc) && (altsetting == -1 || altsetting == as)) { + return p; + } + p = next; + } + return NULL; +} + +static void *find_csinterface_descriptor(void *descstart, unsigned int desclen, void *after, u8 dsubtype, int iface, int altsetting) +{ + unsigned char *p; + + p = find_descriptor(descstart, desclen, after, USB_DT_CS_INTERFACE, iface, altsetting); + while (p) { + if (p[0] >= 3 && p[2] == dsubtype) + return p; + p = find_descriptor(descstart, desclen, p, USB_DT_CS_INTERFACE, iface, altsetting); + } + return NULL; +} + +static void *find_audiocontrol_unit(void *descstart, unsigned int desclen, void *after, u8 unit, int iface) +{ + unsigned char *p; + + p = find_descriptor(descstart, desclen, after, USB_DT_CS_INTERFACE, iface, -1); + while (p) { + if (p[0] >= 4 && p[2] >= INPUT_TERMINAL && p[2] <= EXTENSION_UNIT && p[3] == unit) + return p; + p = find_descriptor(descstart, desclen, p, USB_DT_CS_INTERFACE, iface, -1); + } + return NULL; +} + +static void usb_audio_parsestreaming(struct usb_audio_state *s, unsigned char *buffer, unsigned int buflen, int asifin, int asifout) +{ + struct usb_device *dev = s->usbdev; + struct usb_audiodev *as; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface_descriptor *alts; + struct usb_interface *iface; + struct audioformat *fp; + unsigned char *fmt, *csep; + unsigned int i, j, k, format; + + if (!(as = kmalloc(sizeof(struct usb_audiodev), GFP_KERNEL))) + return; + memset(as, 0, sizeof(struct usb_audiodev)); + init_waitqueue_head(&as->usbin.dma.wait); + init_waitqueue_head(&as->usbout.dma.wait); + spin_lock_init(&as->lock); + as->state = s; + as->usbin.interface = asifin; + as->usbout.interface = asifout; + /* search for input formats */ + if (asifin >= 0) { + as->usbin.flags = FLG_CONNECTED; + iface = &config->interface[asifin]; + for (i = 0; i < iface->num_altsetting; i++) { + alts = &iface->altsetting[i]; + if (alts->bInterfaceClass != USB_CLASS_AUDIO || alts->bInterfaceSubClass != 2) + continue; + if (alts->bNumEndpoints < 1) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u does not have an endpoint\n", + dev->devnum, asifin, i); + continue; + } + if ((alts->endpoint[0].bmAttributes & 0x03) != 0x01 || + !(alts->endpoint[0].bEndpointAddress & 0x80)) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u first endpoint not isochronous in\n", + dev->devnum, asifin, i); + continue; + } + fmt = find_csinterface_descriptor(buffer, buflen, NULL, AS_GENERAL, asifin, i); + if (!fmt) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", + dev->devnum, asifin, i); + continue; + } + if (fmt[0] < 7 || fmt[6] != 0 || (fmt[5] != 1 && fmt[5] != 2)) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u format not supported\n", + dev->devnum, asifin, i); + continue; + } + format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8); + fmt = find_csinterface_descriptor(buffer, buflen, NULL, FORMAT_TYPE, asifin, i); + if (!fmt) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", + dev->devnum, asifin, i); + continue; + } + if (fmt[0] < 8+3*(fmt[7] ? fmt[7] : 2) || fmt[3] != 1) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not supported\n", + dev->devnum, asifin, i); + continue; + } + if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n", + dev->devnum, asifin, i, fmt[4], fmt[5]); + continue; + } + csep = find_descriptor(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, asifin, i); + if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u no or invalid class specific endpoint descriptor\n", + dev->devnum, asifin, i); + continue; + } + if (as->numfmtin >= MAXFORMATS) + continue; + fp = &as->fmtin[as->numfmtin++]; + if (fmt[5] == 2) + format &= (AFMT_U16_LE | AFMT_S16_LE); + else + format &= (AFMT_U8 | AFMT_S8); + if (fmt[4] == 2) + format |= AFMT_STEREO; + fp->format = format; + fp->altsetting = i; + fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16); + for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) { + k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16); + if (k > fp->sratehi) + fp->sratehi = k; + if (k < fp->sratelo) + fp->sratelo = k; + } + fp->attributes = csep[3]; + printk(KERN_INFO "usbaudio: device %u interface %u altsetting %u: format 0x%08x sratelo %u sratehi %u attributes 0x%02x\n", + dev->devnum, asifin, i, fp->format, fp->sratelo, fp->sratehi, fp->attributes); + } + } + /* search for output formats */ + if (asifout >= 0) { + as->usbout.flags = FLG_CONNECTED; + iface = &config->interface[asifout]; + for (i = 0; i < iface->num_altsetting; i++) { + alts = &iface->altsetting[i]; + if (alts->bInterfaceClass != USB_CLASS_AUDIO || alts->bInterfaceSubClass != 2) + continue; + if (alts->bNumEndpoints < 1) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u does not have an endpoint\n", + dev->devnum, asifout, i); + continue; + } + if ((alts->endpoint[0].bmAttributes & 0x03) != 0x01 || + (alts->endpoint[0].bEndpointAddress & 0x80)) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u first endpoint not isochronous out\n", + dev->devnum, asifout, i); + continue; + } + fmt = find_csinterface_descriptor(buffer, buflen, NULL, AS_GENERAL, asifout, i); + if (!fmt) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", + dev->devnum, asifout, i); + continue; + } + if (fmt[0] < 7 || fmt[6] != 0 || (fmt[5] != 1 && fmt[5] != 2)) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u format not supported\n", + dev->devnum, asifout, i); + continue; + } + format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8); + fmt = find_csinterface_descriptor(buffer, buflen, NULL, FORMAT_TYPE, asifout, i); + if (!fmt) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", + dev->devnum, asifout, i); + continue; + } + if (fmt[0] < 8+3*(fmt[7] ? fmt[7] : 2) || fmt[3] != 1) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not supported\n", + dev->devnum, asifout, i); + continue; + } + if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n", + dev->devnum, asifout, i, fmt[4], fmt[5]); + continue; + } + csep = find_descriptor(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, asifout, i); + if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u no or invalid class specific endpoint descriptor\n", + dev->devnum, asifout, i); + continue; + } + if (as->numfmtout >= MAXFORMATS) + continue; + fp = &as->fmtout[as->numfmtout++]; + if (fmt[5] == 2) + format &= (AFMT_U16_LE | AFMT_S16_LE); + else + format &= (AFMT_U8 | AFMT_S8); + if (fmt[4] == 2) + format |= AFMT_STEREO; + fp->format = format; + fp->altsetting = i; + fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16); + for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) { + k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16); + if (k > fp->sratehi) + fp->sratehi = k; + if (k < fp->sratelo) + fp->sratelo = k; + } + fp->attributes = csep[3]; + printk(KERN_INFO "usbaudio: device %u interface %u altsetting %u: format 0x%08x sratelo %u sratehi %u attributes 0x%02x\n", + dev->devnum, asifout, i, fp->format, fp->sratelo, fp->sratehi, fp->attributes); + } + } + if (as->numfmtin == 0 && as->numfmtout == 0) { + kfree(as); + return; + } + if ((as->dev_audio = register_sound_dsp(&usb_audio_fops, -1)) < 0) { + printk(KERN_ERR "usbaudio: cannot register dsp\n"); + kfree(as); + return; + } + /* everything successful */ + list_add_tail(&as->list, &s->audiolist); +} + +struct consmixstate { + struct usb_audio_state *s; + unsigned char *buffer; + unsigned int buflen; + unsigned int ctrlif; + struct mixerchannel mixch[SOUND_MIXER_NRDEVICES]; + unsigned int nrmixch; + unsigned int mixchmask; + unsigned long unitbitmap[32/sizeof(unsigned long)]; + /* return values */ + unsigned int nrchannels; + unsigned int termtype; + unsigned int chconfig; +}; + +static struct mixerchannel *getmixchannel(struct consmixstate *state, unsigned int nr) +{ + struct mixerchannel *c; + + if (nr >= SOUND_MIXER_NRDEVICES) { + printk(KERN_ERR "usbaudio: invalid OSS mixer channel %u\n", nr); + return NULL; + } + if (!(state->mixchmask & (1 << nr))) { + printk(KERN_WARNING "usbaudio: OSS mixer channel %u already in use\n", nr); + return NULL; + } + c = &state->mixch[state->nrmixch++]; + c->osschannel = nr; + state->mixchmask &= ~(1 << nr); + return c; +} + +static unsigned int getvolchannel(struct consmixstate *state) +{ + unsigned int u; + + if ((state->termtype & 0xff00) == 0x0000 && (state->mixchmask & SOUND_MASK_VOLUME)) + return SOUND_MIXER_VOLUME; + if ((state->termtype & 0xff00) == 0x0100) { + if (state->mixchmask & SOUND_MASK_PCM) + return SOUND_MIXER_PCM; + if (state->mixchmask & SOUND_MASK_ALTPCM) + return SOUND_MIXER_ALTPCM; + } + if ((state->termtype & 0xff00) == 0x0200 && (state->mixchmask & SOUND_MASK_MIC)) + return SOUND_MIXER_MIC; + if ((state->termtype & 0xff00) == 0x0300 && (state->mixchmask & SOUND_MASK_SPEAKER)) + return SOUND_MIXER_SPEAKER; + if ((state->termtype & 0xff00) == 0x0500) { + if (state->mixchmask & SOUND_MASK_PHONEIN) + return SOUND_MIXER_PHONEIN; + if (state->mixchmask & SOUND_MASK_PHONEOUT) + return SOUND_MIXER_PHONEOUT; + } + if (state->termtype >= 0x710 && state->termtype <= 0x711 && (state->mixchmask & SOUND_MASK_RADIO)) + return SOUND_MIXER_RADIO; + if (state->termtype >= 0x709 && state->termtype <= 0x70f && (state->mixchmask & SOUND_MASK_VIDEO)) + return SOUND_MIXER_VIDEO; + u = ffs(state->mixchmask & (SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | SOUND_MASK_LINE3 | + SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 | SOUND_MASK_DIGITAL3)); + return u-1; +} + +static void prepmixch(struct consmixstate *state) +{ + struct usb_device *dev = state->s->usbdev; + struct mixerchannel *ch; + unsigned char buf[2]; + __s16 v1; + unsigned int v2, v3; + + if (!state->nrmixch || state->nrmixch > SOUND_MIXER_NRDEVICES) + return; + ch = &state->mixch[state->nrmixch-1]; + switch (ch->selector) { + case 0: /* mixer unit request */ + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + ch->minval = buf[0] | (buf[1] << 8); + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + ch->maxval = buf[0] | (buf[1] << 8); + v2 = ch->maxval - ch->minval; + if (!v2) + v2 = 1; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + v1 = buf[0] | (buf[1] << 8); + v3 = v1 - ch->minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + ch->value = v3; + if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) { + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + ((ch->chnum + !!(ch->flags & MIXFLG_STEREOIN)) << 8) | (1 + !!(ch->flags & MIXFLG_STEREOOUT)), + state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + v1 = buf[0] | (buf[1] << 8); + v3 = v1 - ch->minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + } + ch->value |= v3 << 8; + break; + + /* various feature unit controls */ + case VOLUME_CONTROL: + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + ch->minval = buf[0] | (buf[1] << 8); + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + ch->maxval = buf[0] | (buf[1] << 8); + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + v1 = buf[0] | (buf[1] << 8); + v2 = ch->maxval - ch->minval; + v3 = v1 - ch->minval; + if (!v2) + v2 = 1; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + ch->value = v3; + if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) { + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + v1 = buf[0] | (buf[1] << 8); + v3 = v1 - ch->minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + } + ch->value |= v3 << 8; + break; + + case BASS_CONTROL: + case MID_CONTROL: + case TREBLE_CONTROL: + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0) + goto err; + ch->minval = buf[0] << 8; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0) + goto err; + ch->maxval = buf[0] << 8; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0) + goto err; + v1 = buf[0] << 8; + v2 = ch->maxval - ch->minval; + v3 = v1 - ch->minval; + if (!v2) + v2 = 1; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + ch->value = v3; + if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) { + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0) + goto err; + v1 = buf[0] << 8; + v3 = v1 - ch->minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + } + ch->value |= v3 << 8; + break; + + default: + goto err; + } + return; + + err: + printk(KERN_ERR "usbaudio: mixer request device %u if %u unit %u ch %u selector %u failed\n", + dev->devnum, state->ctrlif, ch->unitid, ch->chnum, ch->selector); + if (state->nrmixch) + state->nrmixch--; +} + + +static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unitid); + +extern inline int checkmixbmap(unsigned char *bmap, unsigned char flg, unsigned int inidx, unsigned int numoch) +{ + unsigned int idx; + + idx = inidx*numoch; + if (!(bmap[-(idx >> 3)] & (0x80 >> (idx & 7)))) + return 0; + if (!(flg & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) + return 1; + idx = (inidx+!!(flg & MIXFLG_STEREOIN))*numoch+!!(flg & MIXFLG_STEREOOUT); + if (!(bmap[-(idx >> 3)] & (0x80 >> (idx & 7)))) + return 0; + return 1; +} + +static void usb_audio_mixerunit(struct consmixstate *state, unsigned char *mixer) +{ + unsigned int nroutch = mixer[5+mixer[4]]; + unsigned int chidx[SOUND_MIXER_NRDEVICES+1]; + unsigned int termt[SOUND_MIXER_NRDEVICES]; + unsigned char flg = (nroutch >= 2) ? MIXFLG_STEREOOUT : 0; + unsigned char *bmap = &mixer[9+mixer[4]]; + unsigned int bmapsize; + struct mixerchannel *ch; + unsigned int i; + + if (!mixer[4]) { + printk(KERN_ERR "usbaudio: unit %u invalid MIXER_UNIT descriptor\n", mixer[3]); + return; + } + if (mixer[4] > SOUND_MIXER_NRDEVICES) { + printk(KERN_ERR "usbaudio: mixer unit %u: too many input pins\n", mixer[3]); + return; + } + chidx[0] = 0; + for (i = 0; i < mixer[4]; i++) { + usb_audio_recurseunit(state, mixer[5+i]); + chidx[i+1] = chidx[i] + state->nrchannels; + termt[i] = state->termtype; + } + state->termtype = 0; + state->chconfig = mixer[6+mixer[4]] | (mixer[7+mixer[4]] << 8); + bmapsize = (nroutch * chidx[mixer[4]] + 7) >> 3; + bmap += bmapsize - 1; + if (mixer[0] < 10+mixer[4]+bmapsize) { + printk(KERN_ERR "usbaudio: unit %u invalid MIXER_UNIT descriptor (bitmap too small)\n", mixer[3]); + return; + } + for (i = 0; i < mixer[4]; i++) { + state->termtype = termt[i]; + if (chidx[i+1]-chidx[i] >= 2) { + flg |= MIXFLG_STEREOIN; + if (checkmixbmap(bmap, flg, chidx[i], nroutch)) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = mixer[3]; + ch->selector = 0; + ch->chnum = chidx[i]+1; + ch->flags = flg; + prepmixch(state); + } + continue; + } + } + flg &= ~MIXFLG_STEREOIN; + if (checkmixbmap(bmap, flg, chidx[i], nroutch)) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = mixer[3]; + ch->selector = 0; + ch->chnum = chidx[i]+1; + ch->flags = flg; + prepmixch(state); + } + } + } + state->termtype = 0; +} + +static struct mixerchannel *slctsrc_findunit(struct consmixstate *state, __u8 unitid) +{ + unsigned int i; + + for (i = 0; i < state->nrmixch; i++) + if (state->mixch[i].unitid == unitid) + return &state->mixch[i]; + return NULL; +} + +static void usb_audio_selectorunit(struct consmixstate *state, unsigned char *selector) +{ + unsigned int chnum, i, mixch; + struct mixerchannel *mch; + + if (!selector[4]) { + printk(KERN_ERR "usbaudio: unit %u invalid SELECTOR_UNIT descriptor\n", selector[3]); + return; + } + mixch = state->nrmixch; + usb_audio_recurseunit(state, selector[5]); + if (state->nrmixch != mixch) { + mch = &state->mixch[state->nrmixch-1]; + mch->slctunitid = selector[3] | (1 << 8); + } else if ((mch = slctsrc_findunit(state, selector[5]))) { + mch->slctunitid = selector[3] | (1 << 8); + } else { + printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel 1\n", selector[3]); + } + chnum = state->nrchannels; + for (i = 1; i < selector[4]; i++) { + mixch = state->nrmixch; + usb_audio_recurseunit(state, selector[5+i]); + if (chnum != state->nrchannels) { + printk(KERN_ERR "usbaudio: selector unit %u: input pins with varying channel numbers\n", selector[3]); + state->termtype = 0; + state->chconfig = 0; + state->nrchannels = 0; + return; + } + if (state->nrmixch != mixch) { + mch = &state->mixch[state->nrmixch-1]; + mch->slctunitid = selector[3] | ((i + 1) << 8); + } else if ((mch = slctsrc_findunit(state, selector[5+i]))) { + mch->slctunitid = selector[3] | ((i + 1) << 8); + } else { + printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel %u\n", selector[3], i+1); + } + } + state->termtype = 0; + state->chconfig = 0; +} + +/* in the future we might try to handle 3D etc. effect units */ + +static void usb_audio_processingunit(struct consmixstate *state, unsigned char *proc) +{ + unsigned int i; + + for (i = 0; i < proc[6]; i++) + usb_audio_recurseunit(state, proc[7+i]); + state->nrchannels = proc[7+proc[6]]; + state->termtype = 0; + state->chconfig = proc[8+proc[6]] | (proc[9+proc[6]] << 8); +} + +static void usb_audio_featureunit(struct consmixstate *state, unsigned char *ftr) +{ + struct mixerchannel *ch; + unsigned short chftr, mchftr; +#if 0 + struct usb_device *dev = state->s->usbdev; + unsigned char data[1]; +#endif + + usb_audio_recurseunit(state, ftr[4]); + if (state->nrchannels == 0) { + printk(KERN_ERR "usbaudio: feature unit %u source has no channels\n", ftr[3]); + return; + } + if (state->nrchannels > 2) + printk(KERN_WARNING "usbaudio: feature unit %u: OSS mixer interface does not support more than 2 channels\n", ftr[3]); + if (state->nrchannels == 1 && ftr[0] == 7+ftr[5]) { + printk(KERN_WARNING "usbaudio: workaround for broken Philips Camera Microphone descriptor enabled\n"); + mchftr = ftr[6]; + chftr = 0; + } else { + if (ftr[0] < 7+ftr[5]*(1+state->nrchannels)) { + printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", ftr[3]); + return; + } + mchftr = ftr[6]; + chftr = ftr[6+ftr[5]]; + if (state->nrchannels > 1) + chftr &= ftr[6+2*ftr[5]]; + } + /* volume control */ + if (chftr & 2) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = VOLUME_CONTROL; + ch->chnum = 1; + ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0; + prepmixch(state); + } + } else if (mchftr & 2) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = VOLUME_CONTROL; + ch->chnum = 0; + ch->flags = 0; + prepmixch(state); + } + } + /* bass control */ + if (chftr & 4) { + ch = getmixchannel(state, SOUND_MIXER_BASS); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = BASS_CONTROL; + ch->chnum = 1; + ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0; + prepmixch(state); + } + } else if (mchftr & 4) { + ch = getmixchannel(state, SOUND_MIXER_BASS); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = BASS_CONTROL; + ch->chnum = 0; + ch->flags = 0; + prepmixch(state); + } + } + /* treble control */ + if (chftr & 16) { + ch = getmixchannel(state, SOUND_MIXER_TREBLE); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = TREBLE_CONTROL; + ch->chnum = 1; + ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0; + prepmixch(state); + } + } else if (mchftr & 16) { + ch = getmixchannel(state, SOUND_MIXER_TREBLE); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = TREBLE_CONTROL; + ch->chnum = 0; + ch->flags = 0; + prepmixch(state); + } + } +#if 0 + /* if there are mute controls, unmute them */ + /* does not seem to be necessary, and the Dallas chip does not seem to support the "all" channel (255) */ + if ((chftr & 1) || (mchftr & 1)) { + printk(KERN_DEBUG "usbaudio: unmuting feature unit %u interface %u\n", ftr[3], state->ctrlif); + data[0] = 0; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (MUTE_CONTROL << 8) | 0xff, state->ctrlif | (ftr[3] << 8), data, 1, HZ) < 0) + printk(KERN_WARNING "usbaudio: failure to unmute feature unit %u interface %u\n", ftr[3], state->ctrlif); + } +#endif +} + +static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unitid) +{ + unsigned char *p1; + unsigned int i, j; + + if (test_and_set_bit(unitid, &state->unitbitmap)) { + printk(KERN_INFO "usbaudio: mixer path revisits unit %d\n", unitid); + return; + } + p1 = find_audiocontrol_unit(state->buffer, state->buflen, NULL, unitid, state->ctrlif); + if (!p1) { + printk(KERN_ERR "usbaudio: unit %d not found!\n", unitid); + return; + } + state->nrchannels = 0; + state->termtype = 0; + state->chconfig = 0; + switch (p1[2]) { + case INPUT_TERMINAL: + if (p1[0] < 12) { + printk(KERN_ERR "usbaudio: unit %u: invalid INPUT_TERMINAL descriptor\n", unitid); + return; + } + state->nrchannels = p1[7]; + state->termtype = p1[4] | (p1[5] << 8); + state->chconfig = p1[8] | (p1[9] << 8); + return; + + case MIXER_UNIT: + if (p1[0] < 10 || p1[0] < 10+p1[4]) { + printk(KERN_ERR "usbaudio: unit %u: invalid MIXER_UNIT descriptor\n", unitid); + return; + } + usb_audio_mixerunit(state, p1); + return; + + case SELECTOR_UNIT: + if (p1[0] < 6 || p1[0] < 6+p1[4]) { + printk(KERN_ERR "usbaudio: unit %u: invalid SELECTOR_UNIT descriptor\n", unitid); + return; + } + usb_audio_selectorunit(state, p1); + return; + + case FEATURE_UNIT: + if (p1[0] < 7 || p1[0] < 7+p1[5]) { + printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", unitid); + return; + } + usb_audio_featureunit(state, p1); + return; + + case PROCESSING_UNIT: + if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]] || p1[0] < 13+p1[6]+p1[11+p1[6]]+p1[13+p1[6]+p1[11+p1[6]]]) { + printk(KERN_ERR "usbaudio: unit %u: invalid PROCESSING_UNIT descriptor\n", unitid); + return; + } + usb_audio_processingunit(state, p1); + return; + + case EXTENSION_UNIT: + if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]]) { + printk(KERN_ERR "usbaudio: unit %u: invalid EXTENSION_UNIT descriptor\n", unitid); + return; + } + for (j = i = 0; i < p1[6]; i++) { + usb_audio_recurseunit(state, p1[7+i]); + if (!i) + j = state->termtype; + else if (j != state->termtype) + j = 0; + } + state->nrchannels = p1[7+p1[6]]; + state->chconfig = p1[8+p1[6]] | (p1[9+p1[6]] << 8); + state->termtype = j; + return; + + default: + printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]); + return; + } +} + +static void usb_audio_constructmixer(struct usb_audio_state *s, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif, unsigned char *oterm) +{ + struct usb_mixerdev *ms; + struct consmixstate state; + + memset(&state, 0, sizeof(state)); + state.s = s; + state.nrmixch = 0; + state.mixchmask = ~0; + state.buffer = buffer; + state.buflen = buflen; + state.ctrlif = ctrlif; + set_bit(oterm[3], &state.unitbitmap); /* mark terminal ID as visited */ + printk(KERN_INFO "usbaudio: constructing mixer for Terminal %u type 0x%04x\n", + oterm[3], oterm[4] | (oterm[5] << 8)); + usb_audio_recurseunit(&state, oterm[7]); + if (!state.nrmixch) { + printk(KERN_INFO "usbaudio: no mixer controls found for Terminal %u\n", oterm[3]); + return; + } + if (!(ms = kmalloc(sizeof(struct usb_mixerdev)+state.nrmixch*sizeof(struct mixerchannel), GFP_KERNEL))) + return; + memset(ms, 0, sizeof(struct usb_mixerdev)); + memcpy(&ms->ch, &state.mixch, state.nrmixch*sizeof(struct mixerchannel)); + ms->state = s; + ms->iface = ctrlif; + ms->numch = state.nrmixch; + if ((ms->dev_mixer = register_sound_mixer(&usb_mixer_fops, -1)) < 0) { + printk(KERN_ERR "usbaudio: cannot register mixer\n"); + kfree(ms); + return; + } + list_add_tail(&ms->list, &s->mixerlist); +} + +static void *usb_audio_parsecontrol(struct usb_device *dev, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif) +{ + struct usb_audio_state *s; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface *iface; + unsigned char ifin[USB_MAXINTERFACES], ifout[USB_MAXINTERFACES]; + unsigned char *p1; + unsigned int i, j, k, numifin = 0, numifout = 0; + + if (!(s = kmalloc(sizeof(struct usb_audio_state), GFP_KERNEL))) + return NULL; + memset(s, 0, sizeof(struct usb_audio_state)); + INIT_LIST_HEAD(&s->audiolist); + INIT_LIST_HEAD(&s->mixerlist); + s->usbdev = dev; + s->count = 1; + + /* find audiocontrol interface */ + if (!(p1 = find_csinterface_descriptor(buffer, buflen, NULL, HEADER, ctrlif, -1))) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u no HEADER found\n", + dev->devnum, ctrlif); + goto ret; + } + if (p1[0] < 8 + p1[7]) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u HEADER error\n", + dev->devnum, ctrlif); + goto ret; + } + if (!p1[7]) + printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u has no AudioStreaming and MidiStreaming interfaces\n", + dev->devnum, ctrlif); + for (i = 0; i < p1[7]; i++) { + j = p1[8+i]; + if (j >= config->bNumInterfaces) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u does not exist\n", + dev->devnum, ctrlif, j); + continue; + } + iface = &config->interface[j]; + if (iface->altsetting[0].bInterfaceClass != USB_CLASS_AUDIO) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u is not an AudioClass interface\n", + dev->devnum, ctrlif, j); + continue; + } + if (iface->altsetting[0].bInterfaceSubClass == 3) { + printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u interface %u MIDIStreaming not supported\n", + dev->devnum, ctrlif, j); + continue; + } + if (iface->altsetting[0].bInterfaceSubClass != 2) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u invalid AudioClass subtype\n", + dev->devnum, ctrlif, j); + continue; + } + if (iface->num_altsetting == 0) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u has no working interface.\n", dev->devnum, ctrlif); + continue; + } + if (iface->num_altsetting == 1) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u has only 1 altsetting.\n", dev->devnum, ctrlif); + continue; + } + if (iface->altsetting[0].bNumEndpoints > 0) { + /* Check all endpoints; should they all have a bandwidth of 0 ? */ + for (k = 0; k < iface->altsetting[0].bNumEndpoints; k++) { + if (iface->altsetting[0].endpoint[k].wMaxPacketSize > 0) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u endpoint %d does not have 0 bandwidth at alt[0]\n", dev->devnum, ctrlif, k); + break; + } + } + if (k < iface->altsetting[0].bNumEndpoints) + continue; + } + if (iface->altsetting[1].bNumEndpoints < 1) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u has no endpoint\n", + dev->devnum, ctrlif, j); + continue; + } + /* note: this requires the data endpoint to be ep0 and the optional sync + ep to be ep1, which seems to be the case */ + if (iface->altsetting[1].endpoint[0].bEndpointAddress & USB_DIR_IN) { + if (numifin < USB_MAXINTERFACES) { + ifin[numifin++] = j; + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1); + } + } else { + if (numifout < USB_MAXINTERFACES) { + ifout[numifout++] = j; + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1); + } + } + } + printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u has %u input and %u output AudioStreaming interfaces\n", + dev->devnum, ctrlif, numifin, numifout); + for (i = 0; i < numifin && i < numifout; i++) + usb_audio_parsestreaming(s, buffer, buflen, ifin[i], ifout[i]); + for (j = i; j < numifin; j++) + usb_audio_parsestreaming(s, buffer, buflen, ifin[i], -1); + for (j = i; j < numifout; j++) + usb_audio_parsestreaming(s, buffer, buflen, -1, ifout[i]); + /* now walk through all OUTPUT_TERMINAL descriptors to search for mixers */ + p1 = find_csinterface_descriptor(buffer, buflen, NULL, OUTPUT_TERMINAL, ctrlif, -1); + while (p1) { + if (p1[0] >= 9) + usb_audio_constructmixer(s, buffer, buflen, ctrlif, p1); + p1 = find_csinterface_descriptor(buffer, buflen, p1, OUTPUT_TERMINAL, ctrlif, -1); + } + +ret: + if (list_empty(&s->audiolist) && list_empty(&s->mixerlist)) { + kfree(s); + return NULL; + } + /* everything successful */ + down(&open_sem); + list_add_tail(&s->audiodev, &audiodevs); + up(&open_sem); + printk(KERN_DEBUG "usb_audio_parsecontrol: usb_audio_state at %p\n", s); + return s; +} + +/* we only care for the currently active configuration */ + +static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_config_descriptor *config = dev->actconfig; + unsigned char *buffer; + unsigned char buf[8]; + unsigned int i, buflen; + int ret; + +#if 0 + printk(KERN_DEBUG "usbaudio: Probing if %i: IC %x, ISC %x\n", ifnum, + config->interface[ifnum].altsetting[0].bInterfaceClass, + config->interface[ifnum].altsetting[0].bInterfaceSubClass); +#endif + if (config->interface[ifnum].altsetting[0].bInterfaceClass != USB_CLASS_AUDIO || + config->interface[ifnum].altsetting[0].bInterfaceSubClass != 1) { +#if 0 + printk(KERN_DEBUG "usbaudio: vendor id 0x%04x, product id 0x%04x contains no AudioControl interface\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); +#endif + return NULL; + } + /* + * audiocontrol interface found + * find which configuration number is active + */ + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) + if (dev->config+i == config) + goto configfound; + printk(KERN_ERR "usbaudio: cannot find active configuration number of device %d\n", dev->devnum); + return NULL; + + configfound: + if (usb_set_configuration(dev, config->bConfigurationValue) < 0) { + printk(KERN_ERR "usbaudio: set_configuration failed (ConfigValue 0x%x)\n", config->bConfigurationValue); + return NULL; + } + ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buf, 8); + if (ret<0) { + printk(KERN_ERR "usbaudio: cannot get first 8 bytes of config descriptor %d of device %d\n", i, dev->devnum); + return NULL; + } + if (buf[1] != USB_DT_CONFIG || buf[0] < 9) { + printk(KERN_ERR "usbaudio: invalid config descriptor %d of device %d\n", i, dev->devnum); + return NULL; + } + buflen = buf[2] | (buf[3] << 8); + if (!(buffer = kmalloc(buflen, GFP_KERNEL))) + return NULL; + ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buffer, buflen); + if (ret < 0) { + kfree(buffer); + printk(KERN_ERR "usbaudio: cannot get config descriptor %d of device %d\n", i, dev->devnum); + return NULL; + } + return usb_audio_parsecontrol(dev, buffer, buflen, ifnum); +} + + +/* a revoke facility would make things simpler */ + +static void usb_audio_disconnect(struct usb_device *dev, void *ptr) +{ + struct usb_audio_state *s = (struct usb_audio_state *)ptr; + struct list_head *list; + struct usb_audiodev *as; + struct usb_mixerdev *ms; + + /* we get called with -1 for every audiostreaming interface registered */ + if (s == (struct usb_audio_state *)-1) { + printk(KERN_DEBUG "usb_audio_disconnect: called with -1\n"); + return; + } + if (!s->usbdev) { + printk(KERN_DEBUG "usb_audio_disconnect: already called for %p!\n", s); + return; + } + down(&open_sem); + list_del(&s->audiodev); + INIT_LIST_HEAD(&s->audiodev); + s->usbdev = NULL; + /* deregister all audio and mixer devices, so no new processes can open this device */ + for(list = s->audiolist.next; list != &s->audiolist; list = list->next) { + as = list_entry(list, struct usb_audiodev, list); + usbin_disc(as); + usbout_disc(as); + wake_up(&as->usbin.dma.wait); + wake_up(&as->usbout.dma.wait); + if (as->dev_audio >= 0) + unregister_sound_dsp(as->dev_audio); + as->dev_audio = -1; + } + for(list = s->mixerlist.next; list != &s->mixerlist; list = list->next) { + ms = list_entry(list, struct usb_mixerdev, list); + if (ms->dev_mixer >= 0) + unregister_sound_mixer(ms->dev_mixer); + ms->dev_mixer = -1; + } + release(s); + wake_up(&open_wait); +} + +int usb_audio_init(void) +{ + usb_register(&usb_audio_driver); + return 0; +} -void usb_audio_interface(struct usb_interface_descriptor *interface, u8 *data) +#ifdef MODULE +int init_module(void) { + return usb_audio_init(); } -void usb_audio_endpoint(struct usb_endpoint_descriptor *interface, u8 *data) +void cleanup_module(void) { + usb_deregister(&usb_audio_driver); } +#endif diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/audio.h linux-2.2.17/drivers/usb/audio.h --- linux-2.2.17-orig/drivers/usb/audio.h Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/audio.h Sun Sep 24 04:26:23 2000 @@ -0,0 +1,116 @@ +#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 + +#define CS_AUDIO_UNDEFINED 0x20 +#define CS_AUDIO_DEVICE 0x21 +#define CS_AUDIO_CONFIGURATION 0x22 +#define CS_AUDIO_STRING 0x23 +#define CS_AUDIO_INTERFACE 0x24 +#define CS_AUDIO_ENDPOINT 0x25 + +#define HEADER 0x01 +#define INPUT_TERMINAL 0x02 +#define OUTPUT_TERMINAL 0x03 +#define MIXER_UNIT 0x04 +#define SELECTOR_UNIT 0x05 +#define FEATURE_UNIT 0x06 +#define PROCESSING_UNIT 0x07 +#define EXTENSION_UNIT 0x08 + +#define AS_GENERAL 0x01 +#define FORMAT_TYPE 0x02 +#define FORMAT_SPECIFIC 0x03 + +#define EP_GENERAL 0x01 + +#define MAX_CHAN 9 +#define MAX_FREQ 16 +#define MAX_IFACE 8 +#define MAX_FORMAT 8 +#define MAX_ALT 32 /* Sorry, we need quite a few for the Philips webcams */ + +struct usb_audio_terminal +{ + u8 flags; + u8 assoc; + u16 type; /* Mic etc */ + u8 channels; + u8 source; + u16 chancfg; +}; + +struct usb_audio_format +{ + u8 type; + u8 channels; + u8 num_freq; + u8 sfz; + u8 bits; + u16 freq[MAX_FREQ]; +}; + +struct usb_audio_interface +{ + u8 terminal; + u8 delay; + u16 num_formats; + u16 format_type; + u8 flags; + u8 idleconf; /* Idle config */ +#define AU_IFACE_FOUND 1 + struct usb_audio_format format[MAX_FORMAT]; +}; + +struct usb_audio_device +{ + struct list_head list; + u8 mixer; + u8 selector; + void *irq_handle; + u8 num_channels; + u8 num_dsp_iface; + u8 channel_map[MAX_CHAN]; + struct usb_audio_terminal terminal[MAX_CHAN]; + struct usb_audio_interface interface[MAX_IFACE][MAX_ALT]; +}; + + + +/* Audio Class specific Request Codes */ + +#define SET_CUR 0x01 +#define GET_CUR 0x81 +#define SET_MIN 0x02 +#define GET_MIN 0x82 +#define SET_MAX 0x03 +#define GET_MAX 0x83 +#define SET_RES 0x04 +#define GET_RES 0x84 +#define SET_MEM 0x05 +#define GET_MEM 0x85 +#define GET_STAT 0xff + +/* Terminal Control Selectors */ + +#define COPY_PROTECT_CONTROL 0x01 + +/* Feature Unit Control Selectors */ + +#define MUTE_CONTROL 0x01 +#define VOLUME_CONTROL 0x02 +#define BASS_CONTROL 0x03 +#define MID_CONTROL 0x04 +#define TREBLE_CONTROL 0x05 +#define GRAPHIC_EQUALIZER_CONTROL 0x06 +#define AUTOMATIC_GAIN_CONTROL 0x07 +#define DELAY_CONTROL 0x08 +#define BASS_BOOST_CONTROL 0x09 +#define LOUDNESS_CONTROL 0x0a + +/* Endpoint Control Selectors */ + +#define SAMPLING_FREQ_CONTROL 0x01 +#define PITCH_CONTROL 0x02 diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/dabfirmware.h linux-2.2.17/drivers/usb/dabfirmware.h --- linux-2.2.17-orig/drivers/usb/dabfirmware.h Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/dabfirmware.h Sun Sep 24 04:26:23 2000 @@ -0,0 +1,1408 @@ +/* + * dabdata.h - dab usb firmware and bitstream data + */ + +static INTEL_HEX_RECORD firmware[] = { + +{ 2, 0x0000, 0, {0x21,0x57} }, +{ 3, 0x0003, 0, {0x02,0x01,0x66} }, +{ 3, 0x000b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0013, 0, {0x02,0x01,0x66} }, +{ 3, 0x001b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0023, 0, {0x02,0x01,0x66} }, +{ 3, 0x002b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0033, 0, {0x02,0x03,0x0f} }, +{ 3, 0x003b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0043, 0, {0x02,0x01,0x00} }, +{ 3, 0x004b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0053, 0, {0x02,0x01,0x66} }, +{ 3, 0x005b, 0, {0x02,0x04,0xbd} }, +{ 3, 0x0063, 0, {0x02,0x01,0x67} }, +{ 3, 0x0100, 0, {0x02,0x0c,0x5a} }, +{ 3, 0x0104, 0, {0x02,0x01,0xed} }, +{ 3, 0x0108, 0, {0x02,0x02,0x51} }, +{ 3, 0x010c, 0, {0x02,0x02,0x7c} }, +{ 3, 0x0110, 0, {0x02,0x02,0xe4} }, +{ 1, 0x0114, 0, {0x32} }, +{ 1, 0x0118, 0, {0x32} }, +{ 3, 0x011c, 0, {0x02,0x05,0xfd} }, +{ 3, 0x0120, 0, {0x02,0x00,0x00} }, +{ 3, 0x0124, 0, {0x02,0x00,0x00} }, +{ 3, 0x0128, 0, {0x02,0x04,0x3c} }, +{ 3, 0x012c, 0, {0x02,0x04,0x6a} }, +{ 3, 0x0130, 0, {0x02,0x00,0x00} }, +{ 3, 0x0134, 0, {0x02,0x00,0x00} }, +{ 3, 0x0138, 0, {0x02,0x00,0x00} }, +{ 3, 0x013c, 0, {0x02,0x00,0x00} }, +{ 3, 0x0140, 0, {0x02,0x00,0x00} }, +{ 3, 0x0144, 0, {0x02,0x00,0x00} }, +{ 3, 0x0148, 0, {0x02,0x00,0x00} }, +{ 3, 0x014c, 0, {0x02,0x00,0x00} }, +{ 3, 0x0150, 0, {0x02,0x00,0x00} }, +{ 3, 0x0154, 0, {0x02,0x00,0x00} }, +{ 10, 0x0157, 0, {0x75,0x81,0x7f,0xe5,0x82,0x60,0x03,0x02,0x01,0x61} }, +{ 5, 0x0161, 0, {0x12,0x07,0x6f,0x21,0x64} }, +{ 1, 0x0166, 0, {0x32} }, +{ 14, 0x0167, 0, {0xc0,0xd0,0xc0,0x86,0xc0,0x82,0xc0,0x83,0xc0,0xe0,0x90,0x7f,0x97,0xe0} }, +{ 14, 0x0175, 0, {0x44,0x80,0xf0,0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x0183, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x0191, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x90,0x7f,0x97,0xe0} }, +{ 3, 0x019f, 0, {0x55,0x7f,0xf0} }, +{ 14, 0x01a2, 0, {0x90,0x7f,0x9a,0xe0,0x30,0xe4,0x23,0x90,0x7f,0x68,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x01b0, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x01be, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x01cc, 0, {0xe5,0xd8,0xc2,0xe3,0xf5,0xd8,0xd0,0xe0,0xd0,0x83,0xd0,0x82,0xd0,0x86} }, +{ 3, 0x01da, 0, {0xd0,0xd0,0x32} }, +{ 8, 0x01dd, 0, {0x75,0x86,0x00,0x90,0xff,0xc3,0x7c,0x05} }, +{ 7, 0x01e5, 0, {0xa3,0xe5,0x82,0x45,0x83,0x70,0xf9} }, +{ 1, 0x01ec, 0, {0x22} }, +{ 14, 0x01ed, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0xd0} }, +{ 14, 0x01fb, 0, {0x75,0xd0,0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91} }, +{ 13, 0x0209, 0, {0x90,0x88,0x00,0xe0,0xf5,0x41,0x90,0x7f,0xab,0x74,0x02,0xf0,0x90} }, +{ 9, 0x0216, 0, {0x7f,0xab,0x74,0x02,0xf0,0xe5,0x32,0x60,0x21} }, +{ 4, 0x021f, 0, {0x7a,0x00,0x7b,0x00} }, +{ 11, 0x0223, 0, {0xc3,0xea,0x94,0x18,0xeb,0x64,0x80,0x94,0x80,0x50,0x12} }, +{ 14, 0x022e, 0, {0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x0a,0xba,0x00} }, +{ 2, 0x023c, 0, {0x01,0x0b} }, +{ 2, 0x023e, 0, {0x80,0xe3} }, +{ 2, 0x0240, 0, {0xd0,0x86} }, +{ 14, 0x0242, 0, {0xd0,0xd0,0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0} }, +{ 1, 0x0250, 0, {0x32} }, +{ 14, 0x0251, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x025f, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} }, +{ 4, 0x026d, 0, {0x04,0xf0,0xd0,0x86} }, +{ 11, 0x0271, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x027c, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} }, +{ 14, 0x028a, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} }, +{ 13, 0x0298, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} }, +{ 12, 0x02a5, 0, {0x7f,0xab,0x74,0x08,0xf0,0x75,0x6e,0x00,0x75,0x6f,0x02,0x12} }, +{ 6, 0x02b1, 0, {0x11,0x44,0x75,0x70,0x39,0x75} }, +{ 6, 0x02b7, 0, {0x71,0x0c,0x75,0x72,0x02,0x12} }, +{ 12, 0x02bd, 0, {0x11,0x75,0x90,0x7f,0xd6,0xe4,0xf0,0x75,0xd8,0x20,0xd0,0x86} }, +{ 14, 0x02c9, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} }, +{ 13, 0x02d7, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x02e4, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x02f2, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} }, +{ 4, 0x0300, 0, {0x10,0xf0,0xd0,0x86} }, +{ 11, 0x0304, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x030f, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} }, +{ 14, 0x031d, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} }, +{ 12, 0x032b, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0x75,0x6e,0x00,0x75,0x6f,0x02} }, +{ 7, 0x0337, 0, {0x12,0x11,0x44,0x75,0x70,0x40,0x75} }, +{ 6, 0x033e, 0, {0x71,0x0c,0x75,0x72,0x02,0x12} }, +{ 14, 0x0344, 0, {0x11,0x75,0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0} }, +{ 5, 0x0352, 0, {0x75,0xd8,0x10,0xd0,0x86} }, +{ 14, 0x0357, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} }, +{ 13, 0x0365, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 13, 0x0372, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} }, +{ 12, 0x037f, 0, {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x42,0xf0,0x12,0x10,0x1b,0x90} }, +{ 13, 0x038b, 0, {0x7f,0xa6,0xe5,0x43,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40} }, +{ 1, 0x0398, 0, {0xf0} }, +{ 1, 0x0399, 0, {0x22} }, +{ 13, 0x039a, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} }, +{ 12, 0x03a7, 0, {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x44,0xf0,0x12,0x10,0x1b,0x90} }, +{ 12, 0x03b3, 0, {0x7f,0xa6,0xe5,0x45,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa6,0xe5} }, +{ 11, 0x03bf, 0, {0x46,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40,0xf0} }, +{ 1, 0x03ca, 0, {0x22} }, +{ 10, 0x03cb, 0, {0x75,0x44,0x02,0x75,0x45,0x00,0x75,0x46,0x00,0x12} }, +{ 9, 0x03d5, 0, {0x03,0x9a,0x75,0x42,0x03,0x75,0x43,0x00,0x12} }, +{ 2, 0x03de, 0, {0x03,0x72} }, +{ 1, 0x03e0, 0, {0x22} }, +{ 12, 0x03e1, 0, {0x90,0x88,0x00,0xe5,0x36,0xf0,0x90,0x88,0x00,0x74,0x10,0x25} }, +{ 9, 0x03ed, 0, {0x36,0xf0,0x12,0x01,0xdd,0x75,0x42,0x01,0x75} }, +{ 9, 0x03f6, 0, {0x43,0x18,0x12,0x03,0x72,0x75,0x44,0x02,0x75} }, +{ 9, 0x03ff, 0,{0x45,0x00,0x75,0x46,0x00,0x12,0x03,0x9a,0x75} }, +{ 8, 0x0408, 0,{0x42,0x03,0x75,0x43,0x44,0x12,0x03,0x72} }, +{ 1, 0x0410, 0,{0x22} }, +{ 14, 0x0411, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x041f, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} }, +{ 4, 0x042d, 0, {0x02,0xf0,0xd0,0x86} }, +{ 11, 0x0431, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x043c, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x044a, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xa9,0x74} }, +{ 7, 0x0458, 0, {0x04,0xf0,0x75,0x30,0x01,0xd0,0x86} }, +{ 11, 0x045f, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x046a, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x0478, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} }, +{ 7, 0x0486, 0, {0x04,0xf0,0x75,0x31,0x01,0xd0,0x86} }, +{ 11, 0x048d, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x0498, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 12, 0x04a6, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe5,0xf5,0x91,0xd0,0x86} }, +{ 11, 0x04b2, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x04bd, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 12, 0x04cb, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe7,0xf5,0x91,0xd0,0x86} }, +{ 11, 0x04d7, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 12, 0x04e2, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x20,0x90,0x7f,0x96,0xe4,0xf0} }, +{ 1, 0x04ee, 0, {0x22} }, +{ 7, 0x04ef, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x21} }, +{ 1, 0x04f6, 0, {0x22} }, +{ 14, 0x04f7, 0, {0x90,0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xe0,0xfb,0x74,0x80,0x2a,0xfa} }, +{ 14, 0x0505, 0, {0x74,0x80,0x2b,0xfb,0xea,0x03,0x03,0x54,0x3f,0xfc,0xea,0xc4,0x23,0x54} }, +{ 14, 0x0513, 0, {0x1f,0xfa,0x2c,0xfa,0xeb,0x03,0x03,0x54,0x3f,0xfc,0xeb,0xc4,0x23,0x54} }, +{ 11, 0x0521, 0, {0x1f,0xfb,0x2c,0xfb,0x90,0x17,0x0a,0xe0,0xfc,0x60,0x02} }, +{ 2, 0x052c, 0, {0x7a,0x00} }, +{ 7, 0x052e, 0, {0x90,0x17,0x0c,0xe0,0xfc,0x60,0x02} }, +{ 2, 0x0535, 0, {0x7b,0x00} }, +{ 11, 0x0537, 0, {0xea,0x2b,0xfc,0xc3,0x13,0xf5,0x3a,0x75,0x44,0x02,0x8b} }, +{ 7, 0x0542, 0, {0x45,0x8a,0x46,0x12,0x03,0x9a,0x75} }, +{ 9, 0x0549, 0, {0x6e,0x08,0x75,0x6f,0x00,0x12,0x11,0x44,0x75} }, +{ 4, 0x0552, 0, {0x70,0x47,0x75,0x71} }, +{ 8, 0x0556, 0, {0x0c,0x75,0x72,0x02,0x12,0x11,0x75,0x85} }, +{ 5, 0x055e, 0, {0x3a,0x73,0x12,0x11,0xa0} }, +{ 1, 0x0563, 0, {0x22} }, +{ 14, 0x0564, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} }, +{ 14, 0x0572, 0, {0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xef,0xe0,0xfc} }, +{ 14, 0x0580, 0, {0x33,0x95,0xe0,0xfd,0x8c,0x05,0x7c,0x00,0x90,0x7f,0xee,0xe0,0xfe,0x33} }, +{ 14, 0x058e, 0, {0x95,0xe0,0xff,0xec,0x2e,0xfc,0xed,0x3f,0xfd,0x90,0x7f,0xe9,0xe0,0xfe} }, +{ 5, 0x059c, 0, {0xbe,0x01,0x02,0x80,0x03} }, +{ 3, 0x05a1, 0, {0x02,0x05,0xf9} }, +{ 6, 0x05a4, 0, {0xbc,0x01,0x21,0xbd,0x00,0x1e} }, +{ 14, 0x05aa, 0, {0xea,0xc4,0x03,0x54,0xf8,0xfc,0xeb,0x25,0xe0,0xfd,0x2c,0x24,0x00,0xfc} }, +{ 14, 0x05b8, 0, {0xe4,0x34,0x17,0xfd,0x90,0x7e,0xc0,0xe0,0xfe,0x8c,0x82,0x8d,0x83,0xf0} }, +{ 2, 0x05c6, 0, {0x80,0x31} }, +{ 14, 0x05c8, 0, {0xea,0xc4,0x03,0x54,0xf8,0xfa,0xeb,0x25,0xe0,0xfb,0x2a,0xfa,0x24,0x00} }, +{ 14, 0x05d6, 0, {0xfb,0xe4,0x34,0x17,0xfc,0x90,0x7e,0xc0,0xe0,0xfd,0x8b,0x82,0x8c,0x83} }, +{ 14, 0x05e4, 0, {0xf0,0x74,0x01,0x2a,0x24,0x00,0xfa,0xe4,0x34,0x17,0xfb,0x90,0x7e,0xc1} }, +{ 7, 0x05f2, 0, {0xe0,0xfc,0x8a,0x82,0x8b,0x83,0xf0} }, +{ 3, 0x05f9, 0, {0x75,0x38,0x01} }, +{ 1, 0x05fc, 0, {0x22} }, +{ 14, 0x05fd, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} }, +{ 14, 0x060b, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} }, +{ 13, 0x0619, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} }, +{ 13, 0x0626, 0, {0x7f,0xaa,0x74,0x01,0xf0,0x12,0x05,0x64,0x75,0x37,0x00,0xd0,0x86} }, +{ 14, 0x0633, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} }, +{ 13, 0x0641, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x064e, 0, {0x90,0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xee,0xe0} }, +{ 14, 0x065c, 0, {0xfc,0x33,0x95,0xe0,0xfd,0x90,0x7f,0x96,0xe0,0xfe,0x90,0x7f,0x96,0x74} }, +{ 14, 0x066a, 0, {0x80,0x65,0x06,0xf0,0x90,0x7f,0x00,0x74,0x01,0xf0,0xea,0xc4,0x03,0x54} }, +{ 14, 0x0678, 0, {0xf8,0xfe,0xeb,0x25,0xe0,0xfb,0x2e,0xfe,0x24,0x00,0xfb,0xe4,0x34,0x17} }, +{ 14, 0x0686, 0, {0xff,0x8b,0x82,0x8f,0x83,0xe0,0xfb,0x74,0x01,0x2e,0x24,0x00,0xfe,0xe4} }, +{ 14, 0x0694, 0, {0x34,0x17,0xff,0x8e,0x82,0x8f,0x83,0xe0,0xfe,0x90,0x7f,0xe9,0xe0,0xff} }, +{ 3, 0x06a2, 0, {0xbf,0x81,0x0a} }, +{ 10, 0x06a5, 0, {0x90,0x7f,0x00,0xeb,0xf0,0x90,0x7f,0x01,0xee,0xf0} }, +{ 8, 0x06af, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x82,0x1a} }, +{ 3, 0x06b7, 0, {0xba,0x01,0x0c} }, +{ 12, 0x06ba, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} }, +{ 11, 0x06c6, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0xb5,0xf0} }, +{ 8, 0x06d1, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x83,0x1b} }, +{ 3, 0x06d9, 0, {0xba,0x01,0x0d} }, +{ 13, 0x06dc, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} }, +{ 11, 0x06e9, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0x12,0xf0} }, +{ 8, 0x06f4, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x84,0x1c} }, +{ 3, 0x06fc, 0, {0xba,0x01,0x0d} }, +{ 13, 0x06ff, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0c} }, +{ 12, 0x070c, 0, {0x90,0x7f,0x00,0x74,0x80,0xf0,0x90,0x7f,0x01,0x74,0x01,0xf0} }, +{ 5, 0x0718, 0, {0x90,0x7f,0xb5,0xec,0xf0} }, +{ 1, 0x071d, 0, {0x22} }, +{ 12, 0x071e, 0, {0x75,0x36,0x0d,0x90,0x88,0x00,0x74,0x1d,0xf0,0x75,0x6b,0x80} }, +{ 10, 0x072a, 0, {0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} }, +{ 9, 0x0734, 0, {0x6c,0x0f,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} }, +{ 9, 0x073d, 0, {0x6c,0x06,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} }, +{ 7, 0x0746, 0, {0x6c,0x01,0x12,0x10,0xe2,0x7a,0x00} }, +{ 3, 0x074d, 0, {0xba,0xff,0x00} }, +{ 2, 0x0750, 0, {0x50,0x0a} }, +{ 10, 0x0752, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xf1} }, +{ 10, 0x075c, 0, {0x75,0x6b,0x80,0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75} }, +{ 8, 0x0766, 0, {0x6b,0x80,0x75,0x6c,0x0f,0x12,0x10,0xe2} }, +{ 1, 0x076e, 0, {0x22} }, +{ 14, 0x076f, 0, {0x90,0x7f,0xa1,0xe4,0xf0,0x90,0x7f,0xaf,0x74,0x01,0xf0,0x90,0x7f,0x92} }, +{ 14, 0x077d, 0, {0x74,0x02,0xf0,0x75,0x8e,0x31,0x75,0x89,0x21,0x75,0x88,0x00,0x75,0xc8} }, +{ 14, 0x078b, 0, {0x00,0x75,0x8d,0x40,0x75,0x98,0x40,0x75,0xc0,0x40,0x75,0x87,0x00,0x75} }, +{ 9, 0x0799, 0, {0x20,0x00,0x75,0x21,0x00,0x75,0x22,0x00,0x75} }, +{ 5, 0x07a2, 0, {0x23,0x00,0x75,0x47,0x00} }, +{ 7, 0x07a7, 0, {0xc3,0xe5,0x47,0x94,0x20,0x50,0x11} }, +{ 13, 0x07ae, 0, {0xe5,0x47,0x24,0x00,0xf5,0x82,0xe4,0x34,0x17,0xf5,0x83,0xe4,0xf0} }, +{ 4, 0x07bb, 0, {0x05,0x47,0x80,0xe8} }, +{ 9, 0x07bf, 0, {0xe4,0xf5,0x40,0xf5,0x3f,0xe4,0xf5,0x3c,0xf5} }, +{ 7, 0x07c8, 0, {0x3b,0xe4,0xf5,0x3e,0xf5,0x3d,0x75} }, +{ 11, 0x07cf, 0, {0x32,0x00,0x75,0x37,0x00,0x75,0x39,0x00,0x90,0x7f,0x93} }, +{ 14, 0x07da, 0, {0x74,0x3c,0xf0,0x90,0x7f,0x9c,0x74,0xff,0xf0,0x90,0x7f,0x96,0x74,0x80} }, +{ 14, 0x07e8, 0, {0xf0,0x90,0x7f,0x94,0x74,0x70,0xf0,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} }, +{ 14, 0x07f6, 0, {0x7f,0x97,0xe4,0xf0,0x90,0x7f,0x95,0x74,0xc2,0xf0,0x90,0x7f,0x98,0x74} }, +{ 14, 0x0804, 0, {0x28,0xf0,0x90,0x7f,0x9e,0x74,0x28,0xf0,0x90,0x7f,0xf0,0xe4,0xf0,0x90} }, +{ 14, 0x0812, 0, {0x7f,0xf1,0xe4,0xf0,0x90,0x7f,0xf2,0xe4,0xf0,0x90,0x7f,0xf3,0xe4,0xf0} }, +{ 14, 0x0820, 0, {0x90,0x7f,0xf4,0xe4,0xf0,0x90,0x7f,0xf5,0xe4,0xf0,0x90,0x7f,0xf6,0xe4} }, +{ 14, 0x082e, 0, {0xf0,0x90,0x7f,0xf7,0xe4,0xf0,0x90,0x7f,0xf8,0xe4,0xf0,0x90,0x7f,0xf9} }, +{ 14, 0x083c, 0, {0x74,0x38,0xf0,0x90,0x7f,0xfa,0x74,0xa0,0xf0,0x90,0x7f,0xfb,0x74,0xa0} }, +{ 14, 0x084a, 0, {0xf0,0x90,0x7f,0xfc,0x74,0xa0,0xf0,0x90,0x7f,0xfd,0x74,0xa0,0xf0,0x90} }, +{ 14, 0x0858, 0, {0x7f,0xfe,0x74,0xa0,0xf0,0x90,0x7f,0xff,0x74,0xa0,0xf0,0x90,0x7f,0xe0} }, +{ 14, 0x0866, 0, {0x74,0x03,0xf0,0x90,0x7f,0xe1,0x74,0x01,0xf0,0x90,0x7f,0xdd,0x74,0x80} }, +{ 11, 0x0874, 0, {0xf0,0x12,0x12,0x43,0x12,0x07,0x1e,0x7a,0x00,0x7b,0x00} }, +{ 9, 0x087f, 0, {0xc3,0xea,0x94,0x1e,0xeb,0x94,0x00,0x50,0x17} }, +{ 12, 0x0888, 0, {0x90,0x88,0x00,0xe0,0xf5,0x47,0x90,0x88,0x0b,0xe0,0xf5,0x47} }, +{ 9, 0x0894, 0, {0x90,0x7f,0x68,0xf0,0x0a,0xba,0x00,0x01,0x0b} }, +{ 2, 0x089d, 0, {0x80,0xe0} }, +{ 12, 0x089f, 0, {0x12,0x03,0xe1,0x90,0x7f,0xd6,0xe4,0xf0,0x7a,0x00,0x7b,0x00} }, +{ 13, 0x08ab, 0, {0x8a,0x04,0x8b,0x05,0xc3,0xea,0x94,0xe0,0xeb,0x94,0x2e,0x50,0x1a} }, +{ 14, 0x08b8, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} }, +{ 10, 0x08c6, 0, {0x04,0xd0,0x03,0xd0,0x02,0x0a,0xba,0x00,0x01,0x0b} }, +{ 2, 0x08d0, 0, {0x80,0xd9} }, +{ 13, 0x08d2, 0, {0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0,0x90} }, +{ 14, 0x08df, 0, {0x7f,0xde,0x74,0x05,0xf0,0x90,0x7f,0xdf,0x74,0x05,0xf0,0x90,0x7f,0xac} }, +{ 14, 0x08ed, 0, {0xe4,0xf0,0x90,0x7f,0xad,0x74,0x05,0xf0,0x75,0xa8,0x80,0x75,0xf8,0x10} }, +{ 13, 0x08fb, 0, {0x90,0x7f,0xae,0x74,0x0b,0xf0,0x90,0x7f,0xe2,0x74,0x88,0xf0,0x90} }, +{ 12, 0x0908, 0, {0x7f,0xab,0x74,0x08,0xf0,0x75,0xe8,0x11,0x75,0x32,0x01,0x75} }, +{ 12, 0x0914, 0, {0x31,0x00,0x75,0x30,0x00,0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7} }, +{ 10, 0x0920, 0, {0xd0,0x05,0xd0,0x04,0x75,0x34,0x00,0x75,0x35,0x01} }, +{ 13, 0x092a, 0, {0x90,0x7f,0xae,0x74,0x03,0xf0,0x8c,0x02,0xba,0x00,0x02,0x80,0x03} }, +{ 3, 0x0937, 0, {0x02,0x0a,0x3f} }, +{ 12, 0x093a, 0, {0x85,0x33,0x34,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90,0x7f,0x97} }, +{ 14, 0x0946, 0, {0x74,0x08,0xf0,0x90,0x7f,0x9d,0x74,0x88,0xf0,0x90,0x7f,0x9a,0xe0,0xfa} }, +{ 12, 0x0954, 0, {0x74,0x05,0x5a,0xf5,0x33,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} }, +{ 13, 0x0960, 0, {0x7f,0x97,0x74,0x02,0xf0,0x90,0x7f,0x9d,0x74,0x82,0xf0,0xe5,0x33} }, +{ 13, 0x096d, 0, {0x25,0xe0,0xfa,0x90,0x7f,0x9a,0xe0,0x54,0x05,0xfb,0x4a,0xf5,0x33} }, +{ 2, 0x097a, 0, {0x60,0x0c} }, +{ 12, 0x097c, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x4a,0xf0} }, +{ 11, 0x0988, 0, {0x75,0x6e,0x00,0x75,0x6f,0x00,0xc0,0x04,0xc0,0x05,0x12} }, +{ 14, 0x0993, 0, {0x11,0x44,0xd0,0x05,0xd0,0x04,0x90,0x17,0x13,0xe0,0xfa,0x74,0x80,0x2a} }, +{ 6, 0x09a1, 0, {0xfa,0xe5,0x33,0xb4,0x04,0x29} }, +{ 3, 0x09a7, 0, {0xba,0xa0,0x00} }, +{ 2, 0x09aa, 0, {0x50,0x24} }, +{ 13, 0x09ac, 0, {0x90,0x17,0x13,0xe0,0x04,0xfb,0x0b,0x90,0x17,0x13,0xeb,0xf0,0x90} }, +{ 14, 0x09b9, 0, {0x17,0x13,0xe0,0xfb,0x90,0x17,0x15,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05} }, +{ 9, 0x09c7, 0, {0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02} }, +{ 5, 0x09d0, 0, {0xe5,0x33,0xb4,0x02,0x26} }, +{ 6, 0x09d5, 0, {0xc3,0x74,0x04,0x9a,0x50,0x20} }, +{ 13, 0x09db, 0, {0x90,0x17,0x13,0xe0,0xfa,0x1a,0x1a,0x90,0x17,0x13,0xea,0xf0,0x90} }, +{ 13, 0x09e8, 0, {0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xf0,0xc0,0x04,0xc0,0x05,0x12} }, +{ 6, 0x09f5, 0, {0x04,0xf7,0xd0,0x05,0xd0,0x04} }, +{ 5, 0x09fb, 0, {0xe5,0x33,0xb4,0x08,0x1d} }, +{ 4, 0x0a00, 0, {0xe5,0x34,0x70,0x19} }, +{ 10, 0x0a04, 0, {0x74,0x01,0x25,0x35,0x54,0x0f,0xf5,0x35,0x85,0x35} }, +{ 12, 0x0a0e, 0, {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} }, +{ 3, 0x0a1a, 0, {0x05,0xd0,0x04} }, +{ 5, 0x0a1d, 0, {0xe5,0x33,0xb4,0x01,0x1d} }, +{ 4, 0x0a22, 0, {0xe5,0x34,0x70,0x19} }, +{ 10, 0x0a26, 0, {0xe5,0x35,0x24,0xff,0x54,0x0f,0xf5,0x35,0x85,0x35} }, +{ 12, 0x0a30, 0, {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} }, +{ 3, 0x0a3c, 0, {0x05,0xd0,0x04} }, +{ 14, 0x0a3f, 0, {0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0,0x04,0x90,0x7f,0x96} }, +{ 14, 0x0a4d, 0, {0xe0,0xfa,0x90,0x7f,0x96,0x74,0x7f,0x5a,0xf0,0x90,0x7f,0x97,0x74,0x08} }, +{ 10, 0x0a5b, 0, {0xf0,0xc3,0xec,0x94,0x00,0xed,0x94,0x02,0x40,0x08} }, +{ 8, 0x0a65, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x20,0xe6,0x08} }, +{ 8, 0x0a6d, 0, {0xc3,0xe4,0x9c,0x74,0x08,0x9d,0x50,0x13} }, +{ 14, 0x0a75, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x40,0x65,0x02,0xf0,0x7c} }, +{ 5, 0x0a83, 0, {0x00,0x7d,0x00,0x80,0x05} }, +{ 5, 0x0a88, 0, {0x0c,0xbc,0x00,0x01,0x0d} }, +{ 5, 0x0a8d, 0, {0xe5,0x38,0xb4,0x01,0x0e} }, +{ 13, 0x0a92, 0, {0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0x75,0x38} }, +{ 1, 0x0a9f, 0, {0x00} }, +{ 7, 0x0aa0, 0, {0xe5,0x31,0x70,0x03,0x02,0x09,0x2a} }, +{ 10, 0x0aa7, 0, {0x90,0x7f,0xc9,0xe0,0xfa,0x70,0x03,0x02,0x0c,0x2d} }, +{ 14, 0x0ab1, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} }, +{ 9, 0x0abf, 0, {0x7d,0xc0,0xe0,0xfa,0xba,0x2c,0x02,0x80,0x03} }, +{ 3, 0x0ac8, 0, {0x02,0x0b,0x36} }, +{ 5, 0x0acb, 0, {0x75,0x32,0x00,0x7b,0x00} }, +{ 3, 0x0ad0, 0, {0xbb,0x64,0x00} }, +{ 2, 0x0ad3, 0, {0x50,0x1c} }, +{ 14, 0x0ad5, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} }, +{ 13, 0x0ae3, 0, {0x04,0xd0,0x03,0xd0,0x02,0x90,0x88,0x0f,0xe0,0xf5,0x47,0x0b,0x80} }, +{ 1, 0x0af0, 0, {0xdf} }, +{ 13, 0x0af1, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x07,0x1e,0x12,0x03,0xe1,0x12} }, +{ 12, 0x0afe, 0, {0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x75,0x6e,0x00,0x75} }, +{ 13, 0x0b0a, 0, {0x6f,0x01,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0x44,0xd0,0x05} }, +{ 9, 0x0b17, 0, {0xd0,0x04,0xd0,0x02,0x75,0x70,0x4d,0x75,0x71} }, +{ 11, 0x0b20, 0, {0x0c,0x75,0x72,0x02,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} }, +{ 11, 0x0b2b, 0, {0x11,0x75,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x02,0x0c,0x2d} }, +{ 3, 0x0b36, 0, {0xba,0x2a,0x3b} }, +{ 13, 0x0b39, 0, {0x90,0x7f,0x98,0x74,0x20,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} }, +{ 14, 0x0b46, 0, {0x01,0xdd,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x90,0x7f,0x98,0x74,0x28,0xf0} }, +{ 2, 0x0b54, 0, {0x7b,0x00} }, +{ 3, 0x0b56, 0, {0xbb,0x0a,0x00} }, +{ 5, 0x0b59, 0, {0x40,0x03,0x02,0x0c,0x2d} }, +{ 14, 0x0b5e, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} }, +{ 8, 0x0b6c, 0, {0x04,0xd0,0x03,0xd0,0x02,0x0b,0x80,0xe2} }, +{ 3, 0x0b74, 0, {0xba,0x2b,0x1a} }, +{ 8, 0x0b77, 0, {0x90,0x7f,0xc9,0xe0,0xfb,0xbb,0x40,0x12} }, +{ 14, 0x0b7f, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x12,0x05,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 4, 0x0b8d, 0, {0x02,0x02,0x0c,0x2d} }, +{ 3, 0x0b91, 0, {0xba,0x10,0x1f} }, +{ 14, 0x0b94, 0, {0x90,0x7f,0x96,0xe0,0xfb,0x90,0x7f,0x96,0x74,0x80,0x65,0x03,0xf0,0xc0} }, +{ 14, 0x0ba2, 0, {0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x3d,0xd0,0x05,0xd0,0x04,0xd0,0x02} }, +{ 3, 0x0bb0, 0, {0x02,0x0c,0x2d} }, +{ 3, 0x0bb3, 0, {0xba,0x11,0x12} }, +{ 14, 0x0bb6, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x6a,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 4, 0x0bc4, 0, {0x02,0x02,0x0c,0x2d} }, +{ 3, 0x0bc8, 0, {0xba,0x12,0x12} }, +{ 14, 0x0bcb, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x8f,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 4, 0x0bd9, 0, {0x02,0x02,0x0c,0x2d} }, +{ 3, 0x0bdd, 0, {0xba,0x13,0x0b} }, +{ 11, 0x0be0, 0, {0x90,0x7d,0xc1,0xe0,0xfb,0x90,0x88,0x00,0xf0,0x80,0x42} }, +{ 3, 0x0beb, 0, {0xba,0x14,0x11} }, +{ 14, 0x0bee, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0xdd,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 3, 0x0bfc, 0, {0x02,0x80,0x2e} }, +{ 3, 0x0bff, 0, {0xba,0x15,0x1d} }, +{ 12, 0x0c02, 0, {0x90,0x7d,0xc1,0xe0,0xf5,0x75,0x90,0x7d,0xc2,0xe0,0xf5,0x76} }, +{ 14, 0x0c0e, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 3, 0x0c1c, 0, {0x02,0x80,0x0e} }, +{ 3, 0x0c1f, 0, {0xba,0x16,0x0b} }, +{ 11, 0x0c22, 0, {0xc0,0x04,0xc0,0x05,0x12,0x13,0xa3,0xd0,0x05,0xd0,0x04} }, +{ 11, 0x0c2d, 0, {0x90,0x7f,0xc9,0xe4,0xf0,0x75,0x31,0x00,0x02,0x09,0x2a} }, +{ 1, 0x0c38, 0, {0x22} }, +{ 7, 0x0c39, 0, {0x53,0x55,0x50,0x45,0x4e,0x44,0x00} }, +{ 7, 0x0c40, 0, {0x52,0x45,0x53,0x55,0x4d,0x45,0x00} }, +{ 6, 0x0c47, 0, {0x20,0x56,0x6f,0x6c,0x20,0x00} }, +{ 13, 0x0c4d, 0, {0x44,0x41,0x42,0x55,0x53,0x42,0x20,0x76,0x31,0x2e,0x30,0x30,0x00} }, +{ 14, 0x0c5a, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} }, +{ 14, 0x0c68, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} }, +{ 13, 0x0c76, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} }, +{ 14, 0x0c83, 0, {0x7f,0xab,0x74,0x01,0xf0,0x90,0x7f,0xe8,0xe0,0xfa,0x90,0x7f,0xe9,0xe0} }, +{ 6, 0x0c91, 0, {0xfb,0xbb,0x00,0x02,0x80,0x03} }, +{ 3, 0x0c97, 0, {0x02,0x0d,0x38} }, +{ 3, 0x0c9a, 0, {0xba,0x80,0x14} }, +{ 14, 0x0c9d, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5} }, +{ 6, 0x0cab, 0, {0x74,0x02,0xf0,0x02,0x0e,0xcd} }, +{ 5, 0x0cb1, 0, {0xba,0x82,0x02,0x80,0x03} }, +{ 3, 0x0cb6, 0, {0x02,0x0d,0x1d} }, +{ 8, 0x0cb9, 0, {0x90,0x7f,0xec,0xe0,0xfc,0xbc,0x01,0x00} }, +{ 2, 0x0cc1, 0, {0x40,0x21} }, +{ 6, 0x0cc3, 0, {0xc3,0x74,0x07,0x9c,0x40,0x1b} }, +{ 14, 0x0cc9, 0, {0xec,0x24,0xff,0x25,0xe0,0xfd,0x24,0xc6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} }, +{ 13, 0x0cd7, 0, {0x83,0xe0,0xfd,0x53,0x05,0x01,0x90,0x7f,0x00,0xed,0xf0,0x80,0x2b} }, +{ 3, 0x0ce4, 0, {0xbc,0x81,0x00} }, +{ 2, 0x0ce7, 0, {0x40,0x21} }, +{ 6, 0x0ce9, 0, {0xc3,0x74,0x87,0x9c,0x40,0x1b} }, +{ 14, 0x0cef, 0, {0xec,0x24,0x7f,0x25,0xe0,0xfc,0x24,0xb6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} }, +{ 13, 0x0cfd, 0, {0x83,0xe0,0xfc,0x53,0x04,0x01,0x90,0x7f,0x00,0xec,0xf0,0x80,0x05} }, +{ 5, 0x0d0a, 0, {0x90,0x7f,0x00,0xe4,0xf0} }, +{ 14, 0x0d0f, 0, {0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74,0x02,0xf0,0x02,0x0e,0xcd} }, +{ 5, 0x0d1d, 0, {0xba,0x81,0x02,0x80,0x03} }, +{ 3, 0x0d22, 0, {0x02,0x0e,0xc5} }, +{ 14, 0x0d25, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74} }, +{ 5, 0x0d33, 0, {0x02,0xf0,0x02,0x0e,0xcd} }, +{ 3, 0x0d38, 0, {0xbb,0x01,0x2d} }, +{ 6, 0x0d3b, 0, {0xba,0x00,0x03,0x02,0x0e,0xcd} }, +{ 3, 0x0d41, 0, {0xba,0x02,0x11} }, +{ 13, 0x0d44, 0, {0x75,0x59,0x00,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} }, +{ 4, 0x0d51, 0, {0x02,0x02,0x0e,0xcd} }, +{ 5, 0x0d55, 0, {0xba,0x21,0x02,0x80,0x03} }, +{ 3, 0x0d5a, 0, {0x02,0x0e,0xcd} }, +{ 11, 0x0d5d, 0, {0x75,0x37,0x01,0x90,0x7f,0xc5,0xe4,0xf0,0x02,0x0e,0xcd} }, +{ 3, 0x0d68, 0, {0xbb,0x03,0x1f} }, +{ 6, 0x0d6b, 0, {0xba,0x00,0x03,0x02,0x0e,0xcd} }, +{ 5, 0x0d71, 0, {0xba,0x02,0x02,0x80,0x03} }, +{ 3, 0x0d76, 0, {0x02,0x0e,0xcd} }, +{ 13, 0x0d79, 0, {0x75,0x59,0x01,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} }, +{ 4, 0x0d86, 0, {0x02,0x02,0x0e,0xcd} }, +{ 3, 0x0d8a, 0, {0xbb,0x06,0x54} }, +{ 5, 0x0d8d, 0, {0xba,0x80,0x02,0x80,0x03} }, +{ 3, 0x0d92, 0, {0x02,0x0e,0xc5} }, +{ 8, 0x0d95, 0, {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x01,0x15} }, +{ 12, 0x0d9d, 0, {0x7c,0xfb,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} }, +{ 9, 0x0da9, 0, {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} }, +{ 10, 0x0db2, 0, {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x02,0x02,0x80,0x03} }, +{ 3, 0x0dbc, 0, {0x02,0x0e,0xc5} }, +{ 10, 0x0dbf, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x02,0x80,0x03} }, +{ 3, 0x0dc9, 0, {0x02,0x0e,0xc5} }, +{ 12, 0x0dcc, 0, {0x7c,0x3b,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} }, +{ 9, 0x0dd8, 0, {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} }, +{ 6, 0x0de1, 0, {0xbb,0x07,0x03,0x02,0x0e,0xc5} }, +{ 3, 0x0de7, 0, {0xbb,0x08,0x10} }, +{ 13, 0x0dea, 0, {0xac,0x48,0x90,0x7f,0x00,0xec,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} }, +{ 3, 0x0df7, 0, {0x02,0x0e,0xcd} }, +{ 3, 0x0dfa, 0, {0xbb,0x09,0x31} }, +{ 5, 0x0dfd, 0, {0xba,0x00,0x02,0x80,0x03} }, +{ 3, 0x0e02, 0, {0x02,0x0e,0xc5} }, +{ 14, 0x0e05, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xc3,0x74,0x01,0x9c,0x50,0x03,0x02,0x0e,0xc5} }, +{ 8, 0x0e13, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x0a} }, +{ 10, 0x0e1b, 0, {0x90,0x17,0x21,0xe4,0xf0,0x90,0x17,0x22,0xe4,0xf0} }, +{ 9, 0x0e25, 0, {0x90,0x7f,0xea,0xe0,0xf5,0x48,0x02,0x0e,0xcd} }, +{ 3, 0x0e2e, 0, {0xbb,0x0a,0x27} }, +{ 5, 0x0e31, 0, {0xba,0x81,0x02,0x80,0x03} }, +{ 3, 0x0e36, 0, {0x02,0x0e,0xc5} }, +{ 14, 0x0e39, 0, {0x90,0x7f,0xec,0xe0,0xfa,0x24,0x20,0xfa,0xe4,0x34,0x17,0xfc,0x8a,0x82} }, +{ 14, 0x0e47, 0, {0x8c,0x83,0xe0,0xfa,0x90,0x7f,0x00,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} }, +{ 3, 0x0e55, 0, {0x02,0x0e,0xcd} }, +{ 5, 0x0e58, 0, {0xbb,0x0b,0x02,0x80,0x03} }, +{ 3, 0x0e5d, 0, {0x02,0x0e,0xa9} }, +{ 13, 0x0e60, 0, {0x90,0x17,0x20,0xe4,0xf0,0x90,0x7f,0xec,0xe0,0xfa,0xba,0x01,0x1a} }, +{ 8, 0x0e6d, 0, {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x12} }, +{ 14, 0x0e75, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x90,0x17,0x21,0xf0,0xc0,0x03,0x12,0x04,0xe2} }, +{ 4, 0x0e83, 0, {0xd0,0x03,0x80,0x46} }, +{ 8, 0x0e87, 0, {0x90,0x7f,0xec,0xe0,0xfa,0xba,0x02,0x3e} }, +{ 8, 0x0e8f, 0, {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x36} }, +{ 13, 0x0e97, 0, {0xc0,0x03,0x12,0x04,0xef,0xd0,0x03,0x90,0x7f,0xea,0xe0,0xfa,0x90} }, +{ 5, 0x0ea4, 0, {0x17,0x22,0xf0,0x80,0x24} }, +{ 5, 0x0ea9, 0, {0xbb,0x12,0x02,0x80,0x17} }, +{ 5, 0x0eae, 0, {0xbb,0x81,0x02,0x80,0x0d} }, +{ 5, 0x0eb3, 0, {0xbb,0x83,0x02,0x80,0x08} }, +{ 5, 0x0eb8, 0, {0xbb,0x82,0x02,0x80,0x03} }, +{ 3, 0x0ebd, 0, {0xbb,0x84,0x05} }, +{ 5, 0x0ec0, 0, {0x12,0x06,0x4e,0x80,0x08} }, +{ 8, 0x0ec5, 0, {0x90,0x7f,0xb4,0x74,0x03,0xf0,0x80,0x06} }, +{ 6, 0x0ecd, 0, {0x90,0x7f,0xb4,0x74,0x02,0xf0} }, +{ 2, 0x0ed3, 0, {0xd0,0x86} }, +{ 14, 0x0ed5, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} }, +{ 13, 0x0ee3, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 11, 0x0ef0, 0, {0x90,0x7f,0xec,0xe0,0xf5,0x5a,0xc3,0x94,0x01,0x40,0x1d} }, +{ 7, 0x0efb, 0, {0xc3,0x74,0x07,0x95,0x5a,0x40,0x16} }, +{ 13, 0x0f02, 0, {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xc6,0xf5,0x82,0xe4,0x34} }, +{ 9, 0x0f0f, 0, {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0,0x80,0x22} }, +{ 7, 0x0f18, 0, {0xc3,0xe5,0x5a,0x94,0x81,0x40,0x1b} }, +{ 7, 0x0f1f, 0, {0xc3,0x74,0x87,0x95,0x5a,0x40,0x14} }, +{ 13, 0x0f26, 0, {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xb6,0xf5,0x82,0xe4,0x34} }, +{ 7, 0x0f33, 0, {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0} }, +{ 1, 0x0f3a, 0, {0x22} }, +{ 14, 0x0f3b, 0, {0x09,0x02,0xba,0x00,0x03,0x01,0x00,0x40,0x00,0x09,0x04,0x00,0x00,0x00} }, +{ 14, 0x0f49, 0, {0x01,0x01,0x00,0x00,0x09,0x24,0x01,0x00,0x01,0x3d,0x00,0x01,0x01,0x0c} }, +{ 14, 0x0f57, 0, {0x24,0x02,0x01,0x10,0x07,0x00,0x02,0x03,0x00,0x00,0x00,0x0d,0x24,0x06} }, +{ 14, 0x0f65, 0, {0x03,0x01,0x02,0x15,0x00,0x03,0x00,0x03,0x00,0x00,0x09,0x24,0x03,0x02} }, +{ 14, 0x0f73, 0, {0x01,0x01,0x00,0x01,0x00,0x09,0x24,0x03,0x04,0x02,0x03,0x00,0x03,0x00} }, +{ 14, 0x0f81, 0, {0x09,0x24,0x03,0x05,0x03,0x06,0x00,0x01,0x00,0x09,0x04,0x01,0x00,0x00} }, +{ 14, 0x0f8f, 0, {0x01,0x02,0x00,0x00,0x09,0x04,0x01,0x01,0x01,0x01,0x02,0x00,0x00,0x07} }, +{ 14, 0x0f9d, 0, {0x24,0x01,0x02,0x01,0x01,0x00,0x0b,0x24,0x02,0x01,0x02,0x02,0x10,0x01} }, +{ 14, 0x0fab, 0, {0x80,0xbb,0x00,0x09,0x05,0x88,0x05,0x00,0x01,0x01,0x00,0x00,0x07,0x25} }, +{ 14, 0x0fb9, 0, {0x01,0x00,0x00,0x00,0x00,0x09,0x04,0x02,0x00,0x02,0x00,0x00,0x00,0x00} }, +{ 14, 0x0fc7, 0, {0x07,0x05,0x82,0x02,0x40,0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00} }, +{ 14, 0x0fd5, 0, {0x09,0x04,0x02,0x01,0x03,0x00,0x00,0x00,0x00,0x07,0x05,0x82,0x02,0x40} }, +{ 14, 0x0fe3, 0, {0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00,0x09,0x05,0x89,0x05,0xa0} }, +{ 10, 0x0ff1, 0, {0x01,0x01,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00} }, +{ 14, 0x0ffb, 0, {0x12,0x01,0x00,0x01,0x00,0x00,0x00,0x40,0x47,0x05,0x99,0x99,0x00,0x01} }, +{ 14, 0x1009, 0, {0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x02,0xba} }, +{ 4, 0x1017, 0, {0x00,0x03,0x01,0x00} }, +{ 2, 0x101b, 0, {0x7a,0x00} }, +{ 3, 0x101d, 0, {0xba,0x05,0x00} }, +{ 2, 0x1020, 0, {0x50,0x17} }, +{ 8, 0x1022, 0, {0x90,0x7f,0xa5,0xe0,0xfb,0x30,0xe0,0x05} }, +{ 5, 0x102a, 0, {0x90,0x00,0x01,0x80,0x0d} }, +{ 10, 0x102f, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xe4} }, +{ 3, 0x1039, 0, {0x90,0x00,0x01} }, +{ 1, 0x103c, 0, {0x22} }, +{ 14, 0x103d, 0, {0x90,0x7d,0xc1,0xe0,0xf9,0xa3,0xe0,0xfa,0xa3,0xe0,0xfb,0x7c,0x00,0x7d} }, +{ 4, 0x104b, 0, {0x7e,0xeb,0x60,0x12} }, +{ 14, 0x104f, 0, {0x89,0x82,0x8a,0x83,0xe0,0xa3,0xa9,0x82,0xaa,0x83,0x8c,0x82,0x8d,0x83} }, +{ 4, 0x105d, 0, {0xf0,0x0c,0xdb,0xee} }, +{ 8, 0x1061, 0, {0x90,0x7d,0xc3,0xe0,0x90,0x7f,0xb9,0xf0} }, +{ 1, 0x1069, 0, {0x22} }, +{ 14, 0x106a, 0, {0x90,0x7d,0xc1,0xe0,0xf9,0xa3,0xe0,0xfa,0xa3,0xe0,0xfb,0x7c,0xc4,0x7d} }, +{ 4, 0x1078, 0, {0x7d,0xeb,0x60,0xe5} }, +{ 14, 0x107c, 0, {0x8c,0x82,0x8d,0x83,0xe0,0x0c,0x89,0x82,0x8a,0x83,0xf0,0xa3,0xa9,0x82} }, +{ 4, 0x108a, 0, {0xaa,0x83,0xdb,0xee} }, +{ 1, 0x108e, 0, {0x22} }, +{ 14, 0x108f, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x05,0x86,0x90,0x7d,0xc1,0xe0,0x05,0x86} }, +{ 14, 0x109d, 0, {0xa3,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa6,0x05,0x86,0xa3,0xa3,0xe0,0xf9} }, +{ 5, 0x10ab, 0, {0x60,0x16,0xa3,0x05,0x86} }, +{ 13, 0x10b0, 0, {0x90,0x7f,0xa6,0x05,0x86,0xe0,0xa3,0x05,0x86,0xf0,0xc0,0x01,0x12} }, +{ 6, 0x10bd, 0, {0x10,0x1b,0xd0,0x01,0xd9,0xed} }, +{ 6, 0x10c3, 0, {0x90,0x7f,0xa5,0x74,0x40,0xf0} }, +{ 1, 0x10c9, 0, {0x22} }, +{ 8, 0x10ca, 0, {0x90,0x88,0x02,0x74,0x01,0xf0,0x7a,0x00} }, +{ 3, 0x10d2, 0, {0xba,0xff,0x00} }, +{ 2, 0x10d5, 0, {0x50,0x0a} }, +{ 10, 0x10d7, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xf1} }, +{ 1, 0x10e1, 0, {0x22} }, +{ 5, 0x10e2, 0, {0xe5,0x6b,0xb4,0xc0,0x08} }, +{ 8, 0x10e7, 0, {0x90,0x88,0x03,0xe5,0x6c,0xf0,0x80,0x06} }, +{ 6, 0x10ef, 0, {0x90,0x88,0x02,0xe5,0x6c,0xf0} }, +{ 4, 0x10f5, 0, {0x7a,0x00,0x7b,0x00} }, +{ 11, 0x10f9, 0, {0xc3,0xea,0x94,0x32,0xeb,0x64,0x80,0x94,0x80,0x50,0x07} }, +{ 5, 0x1104, 0, {0x0a,0xba,0x00,0x01,0x0b} }, +{ 2, 0x1109, 0, {0x80,0xee} }, +{ 1, 0x110b, 0, {0x22} }, +{ 10, 0x110c, 0, {0x90,0x88,0x03,0xe5,0x6d,0xf0,0x05,0x39,0x7a,0x00} }, +{ 3, 0x1116, 0, {0xba,0x28,0x00} }, +{ 2, 0x1119, 0, {0x50,0x03} }, +{ 3, 0x111b, 0, {0x0a,0x80,0xf8} }, +{ 5, 0x111e, 0, {0xe5,0x39,0xb4,0x10,0x08} }, +{ 8, 0x1123, 0, {0x90,0x88,0x02,0x74,0xc0,0xf0,0x80,0x0e} }, +{ 5, 0x112b, 0, {0xe5,0x39,0xb4,0x20,0x09} }, +{ 9, 0x1130, 0, {0x90,0x88,0x02,0x74,0x80,0xf0,0x75,0x39,0x00} }, +{ 2, 0x1139, 0, {0x7a,0x00} }, +{ 3, 0x113b, 0, {0xba,0x28,0x00} }, +{ 2, 0x113e, 0, {0x50,0x03} }, +{ 3, 0x1140, 0, {0x0a,0x80,0xf8} }, +{ 1, 0x1143, 0, {0x22} }, +{ 4, 0x1144, 0, {0xe5,0x6f,0x60,0x02} }, +{ 2, 0x1148, 0, {0x80,0x07} }, +{ 7, 0x114a, 0, {0x7a,0x00,0x75,0x39,0x00,0x80,0x05} }, +{ 5, 0x1151, 0, {0x7a,0x40,0x75,0x39,0x10} }, +{ 9, 0x1156, 0, {0xe5,0x6e,0x2a,0xfa,0xe5,0x6e,0x25,0x39,0xf5} }, +{ 10, 0x115f, 0, {0x39,0x90,0x88,0x02,0x74,0x80,0x2a,0xf0,0x7a,0x00} }, +{ 8, 0x1169, 0, {0xc3,0xea,0x64,0x80,0x94,0xa8,0x50,0x03} }, +{ 3, 0x1171, 0, {0x0a,0x80,0xf5} }, +{ 1, 0x1174, 0, {0x22} }, +{ 6, 0x1175, 0, {0xaa,0x70,0xab,0x71,0xac,0x72} }, +{ 12, 0x117b, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x12,0x14,0xee,0xfd,0x60,0x18} }, +{ 13, 0x1187, 0, {0x8d,0x6d,0xc0,0x02,0xc0,0x03,0xc0,0x04,0x12,0x11,0x0c,0xd0,0x04} }, +{ 9, 0x1194, 0, {0xd0,0x03,0xd0,0x02,0x0a,0xba,0x00,0x01,0x0b} }, +{ 2, 0x119d, 0, {0x80,0xdc} }, +{ 1, 0x119f, 0, {0x22} }, +{ 13, 0x11a0, 0, {0xe5,0x73,0xc4,0x54,0x0f,0xfa,0x53,0x02,0x0f,0xc3,0x74,0x09,0x9a} }, +{ 2, 0x11ad, 0, {0x50,0x06} }, +{ 6, 0x11af, 0, {0x74,0x37,0x2a,0xfb,0x80,0x04} }, +{ 4, 0x11b5, 0, {0x74,0x30,0x2a,0xfb} }, +{ 12, 0x11b9, 0, {0x8b,0x6d,0xc0,0x03,0x12,0x11,0x0c,0xd0,0x03,0xaa,0x73,0x53} }, +{ 8, 0x11c5, 0, {0x02,0x0f,0xc3,0x74,0x09,0x9a,0x50,0x06} }, +{ 6, 0x11cd, 0, {0x74,0x37,0x2a,0xfb,0x80,0x04} }, +{ 4, 0x11d3, 0, {0x74,0x30,0x2a,0xfb} }, +{ 5, 0x11d7, 0, {0x8b,0x6d,0x12,0x11,0x0c} }, +{ 1, 0x11dc, 0, {0x22} }, +{ 7, 0x11dd, 0, {0x90,0x7d,0xc3,0xe0,0xfa,0x60,0x0f} }, +{ 12, 0x11e4, 0, {0x90,0x7d,0xc1,0xe0,0xf5,0x6e,0x90,0x7d,0xc2,0xe0,0xf5,0x6f} }, +{ 3, 0x11f0, 0, {0x12,0x11,0x44} }, +{ 12, 0x11f3, 0, {0x90,0x7d,0xff,0xe4,0xf0,0x75,0x70,0xc4,0x75,0x71,0x7d,0x75} }, +{ 5, 0x11ff, 0, {0x72,0x01,0x12,0x11,0x75} }, +{ 1, 0x1204, 0, {0x22} }, +{ 2, 0x1205, 0, {0x7a,0x04} }, +{ 3, 0x1207, 0, {0xba,0x40,0x00} }, +{ 2, 0x120a, 0, {0x50,0x36} }, +{ 14, 0x120c, 0, {0xea,0x24,0xc0,0xf5,0x82,0xe4,0x34,0x7d,0xf5,0x83,0xe0,0xfb,0x7c,0x00} }, +{ 3, 0x121a, 0, {0xbc,0x08,0x00} }, +{ 2, 0x121d, 0, {0x50,0x20} }, +{ 6, 0x121f, 0, {0x8b,0x05,0xed,0x30,0xe7,0x0b} }, +{ 11, 0x1225, 0, {0x90,0x7f,0x96,0x74,0x42,0xf0,0x74,0xc3,0xf0,0x80,0x08} }, +{ 8, 0x1230, 0, {0x90,0x7f,0x96,0xe4,0xf0,0x74,0x81,0xf0} }, +{ 7, 0x1238, 0, {0xeb,0x25,0xe0,0xfb,0x0c,0x80,0xdb} }, +{ 3, 0x123f, 0, {0x0a,0x80,0xc5} }, +{ 1, 0x1242, 0, {0x22} }, +{ 4, 0x1243, 0, {0x7a,0x00,0x7b,0xef} }, +{ 3, 0x1247, 0, {0xba,0x10,0x00} }, +{ 2, 0x124a, 0, {0x50,0x20} }, +{ 14, 0x124c, 0, {0x74,0x11,0x2b,0xfb,0x24,0x00,0xfc,0xe4,0x34,0x18,0xfd,0x8c,0x82,0x8d} }, +{ 14, 0x125a, 0, {0x83,0xe4,0xf0,0xea,0x24,0x00,0xf5,0x82,0xe4,0x34,0x19,0xf5,0x83,0xe4} }, +{ 4, 0x1268, 0, {0xf0,0x0a,0x80,0xdb} }, +{ 1, 0x126c, 0, {0x22} }, +{ 14, 0x126d, 0, {0x74,0xf8,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 14, 0x127b, 0, {0x74,0xf9,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 14, 0x1289, 0, {0x74,0xfa,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 14, 0x1297, 0, {0x74,0xfb,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 14, 0x12a5, 0, {0x74,0xff,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 1, 0x12b3, 0, {0x22} }, +{ 14, 0x12b4, 0, {0x12,0x03,0xcb,0x12,0x12,0x6d,0x7a,0xc0,0x7b,0x87,0x7c,0x01,0x74,0x01} }, +{ 14, 0x12c2, 0, {0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x74} }, +{ 14, 0x12d0, 0, {0x01,0x12,0x14,0xbf,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e} }, +{ 14, 0x12de, 0, {0x83,0x8f,0xf0,0x74,0x06,0x12,0x14,0xbf,0x74,0x01,0x2a,0xfd,0xe4,0x3b} }, +{ 14, 0x12ec, 0, {0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83,0x8c,0xf0,0xe4,0x12,0x14,0xbf,0x74} }, +{ 14, 0x12fa, 0, {0x01,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0} }, +{ 14, 0x1308, 0, {0x74,0x0b,0x12,0x14,0xbf,0x74,0x01,0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07} }, +{ 14, 0x1316, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x74,0x08,0x12,0x14,0xbf,0x74,0x01,0x2d} }, +{ 14, 0x1324, 0, {0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0,0x74,0x01} }, +{ 14, 0x1332, 0, {0x12,0x14,0xbf,0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83} }, +{ 14, 0x1340, 0, {0x8c,0xf0,0xe4,0x12,0x14,0xbf,0x74,0x01,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f} }, +{ 14, 0x134e, 0, {0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0,0x74,0x03,0x12,0x14,0xbf,0x7d,0x00} }, +{ 3, 0x135c, 0, {0xbd,0x06,0x00} }, +{ 2, 0x135f, 0, {0x50,0x12} }, +{ 11, 0x1361, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x0a,0xba,0x00,0x01,0x0b} }, +{ 7, 0x136c, 0, {0xe4,0x12,0x14,0xbf,0x0d,0x80,0xe9} }, +{ 13, 0x1373, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0xe5,0x74,0x12,0x14,0xbf,0x74,0xf9} }, +{ 14, 0x1380, 0, {0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0x74,0x0f,0xf0,0x74} }, +{ 14, 0x138e, 0, {0xfe,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0x74,0x01,0xf0} }, +{ 6, 0x139c, 0, {0x12,0x03,0xe1,0x12,0x04,0xf7} }, +{ 1, 0x13a2, 0, {0x22} }, +{ 13, 0x13a3, 0, {0x90,0x7d,0xc1,0xe0,0xfa,0x24,0x00,0xfb,0xe4,0x34,0x19,0xfc,0x90} }, +{ 14, 0x13b0, 0, {0x7d,0xc2,0xe0,0xfd,0x8b,0x82,0x8c,0x83,0xf0,0x75,0xf0,0x11,0xea,0xa4} }, +{ 3, 0x13be, 0, {0xfa,0x7b,0x00} }, +{ 3, 0x13c1, 0, {0xbb,0x10,0x00} }, +{ 2, 0x13c4, 0, {0x50,0x24} }, +{ 14, 0x13c6, 0, {0xea,0x24,0x00,0xfc,0xe4,0x34,0x18,0xfd,0xeb,0x2c,0xfc,0xe4,0x3d,0xfd} }, +{ 14, 0x13d4, 0, {0x74,0x04,0x2b,0x24,0xc0,0xf5,0x82,0xe4,0x34,0x7d,0xf5,0x83,0xe0,0xfe} }, +{ 8, 0x13e2, 0, {0x8c,0x82,0x8d,0x83,0xf0,0x0b,0x80,0xd7} }, +{ 14, 0x13ea, 0, {0xea,0x24,0x00,0xfa,0xe4,0x34,0x18,0xfb,0x74,0x10,0x2a,0xf5,0x82,0xe4} }, +{ 5, 0x13f8, 0, {0x3b,0xf5,0x83,0xe4,0xf0} }, +{ 1, 0x13fd, 0, {0x22} }, +{ 4, 0x13fe, 0, {0xe5,0x76,0x60,0x02} }, +{ 2, 0x1402, 0, {0x80,0x16} }, +{ 12, 0x1404, 0, {0x74,0x0f,0x55,0x75,0xfa,0x8a,0x75,0x24,0x00,0xf5,0x82,0xe4} }, +{ 10, 0x1410, 0, {0x34,0x19,0xf5,0x83,0xe0,0xf5,0x74,0x12,0x12,0xb4} }, +{ 10, 0x141a, 0, {0x12,0x10,0xca,0x75,0x6e,0x00,0x75,0x6f,0x00,0x12} }, +{ 6, 0x1424, 0, {0x11,0x44,0x75,0x70,0xb9,0x75} }, +{ 6, 0x142a, 0, {0x71,0x14,0x75,0x72,0x02,0x12} }, +{ 11, 0x1430, 0, {0x11,0x75,0xe5,0x76,0xb4,0x02,0x04,0x74,0x01,0x80,0x01} }, +{ 1, 0x143b, 0, {0xe4} }, +{ 3, 0x143c, 0, {0xfa,0x70,0x0f} }, +{ 12, 0x143f, 0, {0x74,0x01,0x25,0x75,0xf5,0x73,0xc0,0x02,0x12,0x11,0xa0,0xd0} }, +{ 3, 0x144b, 0, {0x02,0x80,0x0a} }, +{ 10, 0x144e, 0, {0x85,0x75,0x73,0xc0,0x02,0x12,0x11,0xa0,0xd0,0x02} }, +{ 12, 0x1458, 0, {0x75,0x6e,0x00,0x75,0x6f,0x01,0xc0,0x02,0x12,0x11,0x44,0xd0} }, +{ 4, 0x1464, 0, {0x02,0xea,0x70,0x1a} }, +{ 13, 0x1468, 0, {0x75,0xf0,0x11,0xe5,0x75,0xa4,0xfa,0x24,0x00,0xfa,0xe4,0x34,0x18} }, +{ 9, 0x1475, 0, {0xfb,0x8a,0x70,0x8b,0x71,0x75,0x72,0x01,0x12} }, +{ 4, 0x147e, 0, {0x11,0x75,0x80,0x36} }, +{ 2, 0x1482, 0, {0x7a,0x00} }, +{ 3, 0x1484, 0, {0xba,0x10,0x00} }, +{ 2, 0x1487, 0, {0x50,0x2f} }, +{ 13, 0x1489, 0, {0xea,0x24,0x00,0xf5,0x82,0xe4,0x34,0x19,0xf5,0x83,0xe0,0xfb,0xe5} }, +{ 4, 0x1496, 0, {0x75,0xb5,0x03,0x1b} }, +{ 14, 0x149a, 0, {0x75,0xf0,0x11,0xea,0xa4,0xfb,0x24,0x00,0xfb,0xe4,0x34,0x18,0xfc,0x8b} }, +{ 9, 0x14a8, 0, {0x70,0x8c,0x71,0x75,0x72,0x01,0xc0,0x02,0x12} }, +{ 4, 0x14b1, 0, {0x11,0x75,0xd0,0x02} }, +{ 3, 0x14b5, 0, {0x0a,0x80,0xcc} }, +{ 1, 0x14b8, 0, {0x22} }, +{ 6, 0x14b9, 0, {0x50,0x72,0x6f,0x67,0x20,0x00} }, +{ 14, 0x14bf, 0, {0xc8,0xc0,0xe0,0xc8,0xc0,0xe0,0xe5,0xf0,0x60,0x0b,0x14,0x60,0x0f,0x14} }, +{ 7, 0x14cd, 0, {0x60,0x11,0x14,0x60,0x12,0x80,0x15} }, +{ 7, 0x14d4, 0, {0xd0,0xe0,0xa8,0x82,0xf6,0x80,0x0e} }, +{ 5, 0x14db, 0, {0xd0,0xe0,0xf0,0x80,0x09} }, +{ 4, 0x14e0, 0, {0xd0,0xe0,0x80,0x05} }, +{ 5, 0x14e4, 0, {0xd0,0xe0,0xa8,0x82,0xf2} }, +{ 4, 0x14e9, 0, {0xc8,0xd0,0xe0,0xc8} }, +{ 1, 0x14ed, 0, {0x22} }, +{ 14, 0x14ee, 0, {0xc8,0xc0,0xe0,0xe5,0xf0,0x60,0x0d,0x14,0x60,0x0f,0x14,0x60,0x0f,0x14} }, +{ 6, 0x14fc, 0, {0x60,0x10,0x74,0xff,0x80,0x0f} }, +{ 5, 0x1502, 0, {0xa8,0x82,0xe6,0x80,0x0a} }, +{ 3, 0x1507, 0, {0xe0,0x80,0x07} }, +{ 4, 0x150a, 0, {0xe4,0x93,0x80,0x03} }, +{ 3, 0x150e, 0, {0xa8,0x82,0xe2} }, +{ 4, 0x1511, 0, {0xf8,0xd0,0xe0,0xc8} }, +{ 1, 0x1515, 0, {0x22} }, +{ 0, 0x0000, 1, {0} } + +}; + +static unsigned char bitstream[] = { + +0x00,0x09,0x0F,0xF0,0x0F,0xF0,0x0F,0xF0, 0x0F,0xF0,0x00,0x00,0x01,0x61,0x00,0x0D, +0x64,0x61,0x62,0x75,0x73,0x62,0x74,0x72, 0x2E,0x6E,0x63,0x64,0x00,0x62,0x00,0x0B, +0x73,0x31,0x30,0x78,0x6C,0x76,0x71,0x31, 0x30,0x30,0x00,0x63,0x00,0x0B,0x31,0x39, +0x39,0x39,0x2F,0x30,0x39,0x2F,0x32,0x34, 0x00,0x64,0x00,0x09,0x31,0x30,0x3A,0x34, +0x32,0x3A,0x34,0x36,0x00,0x65,0x00,0x00, 0x2E,0xC0,0xFF,0x20,0x17,0x5F,0x9F,0x5B, +0xFE,0xFB,0xBB,0xB7,0xBB,0xBB,0xFB,0xBF, 0xAF,0xEF,0xFB,0xDF,0xB7,0xFB,0xFB,0x7F, +0xBF,0xB7,0xEF,0xF2,0xFF,0xFB,0xFE,0xFF, 0xFF,0xEF,0xFF,0xFE,0xFF,0xBF,0xFF,0xFF, +0xFF,0xFF,0xAF,0xFF,0xFA,0xFF,0xFF,0xFF, 0xC9,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFB,0xFF,0xA3,0xFF,0xFB, +0xFE,0xFF,0xBF,0xEF,0xE3,0xFE,0xFF,0xBF, 0xE3,0xFE,0xFF,0xBF,0x6F,0xFB,0xF6,0xFF, +0xBF,0xFF,0x47,0xFF,0xFF,0x9F,0xEE,0xF9, 0xFE,0xCF,0x9F,0xEF,0xFB,0xCF,0x9B,0xEE, +0xF8,0xFE,0xEF,0x8F,0xEE,0xFB,0xFE,0x0B, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xBF,0xFF,0xFF,0xFB,0xFF,0xFF, 0xBF,0xFF,0xFF,0xFC,0x17,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xFB,0xFF,0xFF,0x7F,0xFF,0xFF, +0xFC,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x5F,0xFF, 0xFF,0xFD,0xFF,0xFF,0xDB,0xFF,0xFD,0xFF, +0x77,0xFF,0xFD,0xFF,0xFF,0xDF,0xFE,0xFD, 0xFF,0xFF,0xF2,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFD,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE1, +0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xE3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF, +0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x67,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0x7F,0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xDF,0xFF,0xFF,0xFF,0x2F,0xFF, +0xF3,0xFD,0xFF,0x7F,0xDE,0xF7,0xFD,0xFF, 0x7F,0xF7,0x7D,0xFF,0x7F,0xDF,0xF7,0xBD, +0xFF,0x7F,0xFF,0x1F,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xEF,0xFB, +0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFF, 0x3F,0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F, +0x9F,0xE7,0xFA,0x7F,0x9F,0xE7,0xF9,0xFE, 0x7F,0x9F,0xE7,0xFF,0xFC,0x7F,0xBF,0xBF, +0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xB7, 0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, +0xFF,0xE0,0xFD,0xF9,0xFE,0x7F,0x9F,0xE7, 0xF9,0xFE,0x7F,0x9D,0xF9,0xFE,0x7D,0x9D, +0xE7,0xF9,0xFE,0x7F,0x9F,0xED,0xED,0xFF, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F, +0xDF,0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF, 0x7F,0xDF,0xFF,0x9B,0xFF,0xEF,0xFB,0xFE, +0xFB,0xBF,0xEF,0xBB,0xFE,0xFF,0xAF,0xBB, 0xBE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFF, +0xB7,0xBF,0xDB,0xF6,0xBD,0xBF,0x6B,0xDB, 0xF6,0xF9,0xBF,0x5B,0xD6,0xF9,0xBF,0x6F, +0xDB,0xF6,0xFD,0xBF,0xFF,0x0E,0xFF,0xFF, 0xFF,0xFF,0x5F,0xFF,0xF7,0xFF,0xFF,0x7F, +0xF7,0xBD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xDF,0x9F,0xFF,0xFF,0xFF,0xFE,0xFF, +0xFF,0xEF,0xFE,0xFE,0xFF,0xFF,0x77,0xFF, 0xFB,0xFB,0xFF,0xFF,0xFF,0xFF,0xF8,0x3F, +0xFF,0xFD,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xF4,0x7F,0xFF,0xFE,0xFD, 0xBE,0xFF,0xDF,0xFE,0xFF,0xFF,0xEF,0x7F, +0xFF,0xCF,0xFF,0xCF,0xFF,0xFF,0xFF,0xDF, 0xE6,0xFF,0xFF,0x7F,0xDF,0xF7,0xDD,0x7F, +0x7F,0xDF,0xF7,0xFF,0x7F,0xDF,0xD7,0xFD, 0xFF,0x7F,0xDF,0xF7,0xFF,0xCD,0xFF,0xF2, +0xFF,0xFF,0x4F,0x7F,0xF4,0xFF,0xFF,0xFF, 0xE7,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xBB,0xFF,0xEF,0xFF,0xFE,0xFF, 0xFF,0xFF,0xEF,0xFF,0xFF,0xEF,0xFF,0xFB, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x65, 0xEF,0xFF,0xFF,0x7F,0xFF,0xFD,0xEF,0xFF, +0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xCF,0xDF,0xFE,0xFF, +0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,0xF3,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFE,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xBF,0xFF, 0xFF,0xFF,0xE3,0x7F,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xEF,0xEB,0xFF,0xFE,0xBF,0xFF, 0xEB,0xFF,0xFC,0x7F,0xFF,0xFF,0xFF,0xEE, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDD,0xFF, 0xD6,0xFF,0xFD,0xBF,0xFF,0xFB,0xFF,0xFE, +0xFD,0xFF,0xFF,0xFD,0xEF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xDE,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xBF,0xFF,0xFD,0xFF,0x7F,0xBF, 0xFF,0x5F,0xDF,0xFF,0xFF,0xBF,0x77,0xFF, +0xFF,0xFF,0x7F,0xD7,0xFF,0xFF,0xFF,0xFF, 0xFF,0xC3,0xFF,0xFF,0xFF,0xFF,0xDF,0xEF, +0xFF,0xFF,0xFE,0xFB,0xFF,0xFF,0xDF,0xBF, 0xFF,0xFF,0xFF,0xFF,0xED,0xFF,0xB7,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xAF,0x7F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xDF,0xBF,0xDF,0xF3,0xFD,0xFB,0xFF,0x5B, +0xFD,0xFF,0xBF,0xEF,0xF7,0xFF,0xFF,0x7D, 0xFF,0xFF,0xFF,0xFF,0xF8,0x3B,0xFF,0xBF, +0x6F,0xFF,0xFE,0xFF,0xBF,0xFF,0xEB,0x7D, 0xFF,0xEF,0xFB,0xFE,0xFF,0xFF,0xFF,0xFF, +0xFF,0xF2,0x7F,0xFC,0xFF,0x3F,0xDF,0xED, 0xFE,0xFF,0xFF,0xFF,0xFF,0xEF,0x5F,0xF7, +0xB5,0xFF,0xEF,0xFF,0xFF,0xFF,0xE0,0x3F, 0x9F,0x9E,0xFF,0xFF,0xEF,0xFF,0xDF,0xFF, +0xBF,0x5F,0xBF,0xCF,0xF3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x69,0xAF,0x33,0xFD,0xFF, +0xFB,0xFF,0xFF,0xFF,0xFF,0xFC,0xFF,0x7F, 0xD9,0xFF,0xDF,0xFF,0xFF,0xFF,0xFF,0xF5, +0xA3,0xDF,0x6E,0xDE,0xFF,0xFF,0xBD,0xFF, 0xFF,0xFE,0xFF,0xFF,0xFF,0xFE,0xE7,0xFD, +0xFF,0xFF,0xFF,0xF9,0xEF,0xC6,0xFE,0xB7, 0xAD,0xE5,0xF9,0xFF,0xFF,0xFF,0xCF,0xFF, +0xFF,0xFF,0xCD,0xFB,0x7F,0xFF,0xFF,0xFF, 0xF9,0xF6,0x0F,0xDF,0xEC,0xCF,0x7F,0xFF, +0xFB,0x7F,0xFF,0xFF,0xFF,0xFD,0xFF,0xFE, 0xF9,0xFD,0x7F,0xFF,0x7F,0xFF,0xF9,0x5B, +0xFF,0x73,0xDC,0xFD,0x7B,0xDF,0xFF,0xFF, 0xFF,0x7B,0xFF,0xFF,0xF7,0x53,0xD6,0xFF, +0xFF,0xFF,0xFF,0xD8,0x9F,0xFE,0xFF,0xEF, 0x7F,0xEE,0xFF,0xFF,0xFF,0xFB,0xED,0xED, +0xFD,0xFF,0xFE,0xFF,0xFF,0xFB,0x7F,0xFF, 0xE2,0x7F,0xFF,0x6F,0xD8,0x57,0xF7,0xFF, +0xFF,0xFF,0xDF,0xFF,0xE8,0xFF,0xFF,0xFD, 0xFF,0xFF,0xFC,0x7F,0xFF,0xE4,0xFF,0xFB, +0xEF,0xFB,0xFE,0xDF,0xB7,0xED,0xFF,0xFE, 0xDF,0x7F,0xFF,0xFE,0x7F,0xB7,0xFF,0xFF, +0xFF,0xFF,0x89,0xFF,0xFF,0xCF,0xF3,0xFE, 0x7F,0xFF,0xEF,0xFF,0xFE,0x7E,0x7F,0xFB, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF1, 0xFF,0xEB,0x7A,0xD5,0xBF,0x6F,0xDB,0xBE, +0xFD,0xB7,0xD8,0xF6,0xE5,0xBF,0x6F,0xFB, 0xFE,0xF5,0xBD,0x7E,0x06,0xFF,0xDF,0xF7, +0xFB,0xF6,0xFF,0x3F,0xFF,0xDB,0xFF,0xFF, 0x6F,0xFB,0xF7,0xFF,0xFF,0xFF,0xFB,0xFE, +0xF7,0xAF,0xFF,0xB7,0xED,0xEF,0xF7,0xFE, 0xFF,0xFF,0xDF,0xFF,0xFE,0xFF,0xEF,0xFF, +0xFF,0xFF,0xFF,0xBF,0xF7,0xFC,0x1F,0xEE, 0xFB,0xFE,0xBD,0xFF,0x7F,0x5F,0xD7,0xFD, +0xFB,0x43,0xFF,0xFF,0xFD,0xFF,0x5F,0xFF, 0xF7,0xFF,0xF9,0x3F,0xFF,0xCF,0xF3,0xFD, +0xF7,0x7E,0xEF,0xA7,0xF9,0xFE,0x8F,0xA7, 0xE9,0xF3,0x7E,0x9F,0xFB,0xF8,0xFF,0xFF, +0x3F,0xFD,0x7F,0x5F,0xDF,0xFD,0xFF,0xFF, 0x5F,0xFF,0xFD,0x5F,0xFF,0xFF,0x7F,0xFD, +0x7F,0xFD,0x9F,0xFF,0xE0,0xFF,0xFA,0xF8, 0xBE,0x6F,0x9F,0xE6,0xF8,0xBE,0x3F,0x9A, +0xF9,0xBE,0x6F,0x9F,0xE2,0xF9,0xFE,0x6F, 0x9F,0xF9,0xFF,0xF5,0xFD,0x7F,0xCF,0xDF, +0xFD,0xFD,0x7F,0xFF,0xF5,0xFF,0xFF,0xFF, 0xF7,0xF5,0xFD,0x0F,0xDB,0xFF,0xD3,0xFF, +0xEB,0xFA,0xFF,0xFF,0xBF,0xFF,0xFA,0xFF, 0xFF,0xCB,0xFB,0xFE,0xFF,0xFF,0xEB,0xFA, +0xFE,0xFF,0xFF,0xB7,0xFF,0xFF,0xFF,0xFF, 0xBF,0xFF,0xDF,0xF5,0xFF,0xFF,0xD7,0xFF, +0xFF,0xFF,0xDF,0xD7,0xF5,0xFF,0x7F,0xFE, 0x4F,0xFF,0xFD,0xFF,0x7F,0x7F,0xFF,0xAD, +0xEB,0xFB,0xFF,0xAD,0xFF,0xFF,0xFF,0xFF, 0xAF,0xEB,0xFB,0xFF,0xFC,0x0D,0xFF,0xFF, +0xDF,0xD2,0xFD,0xFF,0xFF,0xFD,0xF6,0xFF, 0xFF,0x7F,0xFF,0xFF,0x1F,0xFF,0xFF,0xFF, +0xFF,0xFB,0x3F,0x7D,0xEB,0x32,0xFE,0xBF, 0x2F,0xEB,0xFA,0xAE,0xBD,0xE0,0xFA,0x7E, +0xBF,0xAD,0xEB,0xFA,0xFE,0xBF,0xF5,0x7F, 0xFF,0xDE,0xFE,0xE3,0xFB,0xFF,0xFF,0xFF, +0xDF,0xEF,0x4F,0xDF,0xFF,0x7F,0xDF,0xFF, 0xF7,0xFF,0xFF,0xF8,0x7F,0xFF,0xFF,0xEF, +0xFB,0xFF,0xFF,0xFF,0xEF,0xFF,0xFF,0xDF, 0xED,0xFB,0xDF,0xFF,0xBF,0xFF,0xFF,0xFF, +0x81,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF, 0xFF,0xFF,0xFE,0xDD,0xFE,0xEF,0xFD,0xFF, +0xFF,0xFB,0xFE,0xF7,0xFF,0x93,0xFD,0xFB, 0x7E,0xFF,0xFE,0x87,0xE9,0xFF,0x7F,0xB3, +0x9F,0xFE,0xFE,0xFF,0xAF,0xFD,0xFE,0x7E, 0x3F,0xFE,0x67,0xFF,0xFF,0xF7,0xFF,0xFF, +0xFC,0xF7,0xDF,0xFD,0xFF,0x7F,0xFF,0xFF, 0x7F,0x6D,0xFF,0xFF,0xFE,0xFF,0xFF,0x2F, +0xFF,0xBF,0xFF,0xFF,0xEE,0xFF,0xBE,0xFF, 0xFF,0xFE,0xFF,0xEF,0xFF,0xFF,0xFE,0xFF, +0xEF,0xFF,0xFF,0xFA,0x5F,0xFF,0xFF,0xFB, 0xFF,0xFF,0xEF,0xFF,0xFB,0xFE,0xFD,0xFF, +0xFE,0xFF,0xFB,0xFF,0xFF,0xFF,0x7F,0xFF, 0xFE,0xBF,0xDF,0xFF,0xFB,0xFF,0xFF,0xF7, +0xFC,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF2,0x7F,0xFF, +0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF, 0xF3,0xFF,0xFF,0xFF,0xEF,0xFB,0xFF,0xFF, +0xFF,0xDF,0xE2,0xFF,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFB,0xE7,0xFF,0xFD, +0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xED, 0xEF,0xFD,0xFF,0xFF,0xDF,0xD7,0xF5,0xFD, +0x7F,0x5D,0xFD,0xFF,0x7F,0xDF,0x97,0xF4, 0xFD,0x7B,0x5F,0xFF,0xC9,0xFF,0xFB,0xFE, +0xFF,0xBF,0xFF,0x5F,0xFF,0xFF,0xF7,0xFF, 0xEF,0xFD,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xF7,0xFF,0xD7,0xFD,0x7D,0x7F,0xFF, 0xFF,0xFF,0xFF,0xEF,0xDF,0xF7,0xFD,0xFF, +0xBB,0xFF,0xFF,0x7F,0xFF,0xFE,0xE3,0xFF, 0xF9,0xFE,0x7F,0xBF,0xEF,0xFB,0xFE,0xFF, +0xBF,0xF9,0xFE,0xFF,0x9F,0xEF,0xF9,0xFE, 0xFF,0xBF,0xF3,0xDA,0xFF,0x37,0xCD,0xF3, +0x7C,0xDF,0x37,0xCD,0xF3,0x7F,0x37,0xCD, 0xF3,0x7C,0xDF,0x37,0xCC,0xF3,0x7F,0x5A, +0xBD,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,0xFD, 0xBF,0x6F,0xDE,0xFD,0xBF,0x6F,0xDB,0xF6, +0xFD,0xBF,0x6F,0xFE,0xF1,0x6F,0xEB,0x7A, 0xDE,0xB7,0xAD,0xEB,0x7A,0xDE,0xB7,0xAF, +0x7A,0xDE,0xB7,0xAD,0xEB,0x7A,0xDE,0xB7, 0xFF,0x7E,0xFF,0xFE,0xCD,0xB3,0x6C,0xDB, +0x36,0xCD,0xB3,0x6C,0xDE,0xCD,0xB3,0x6C, 0xDB,0x36,0xCD,0xB3,0x6C,0xDF,0xC9,0xBF, +0xF7,0xBD,0xEF,0x7A,0x9E,0xA7,0xA9,0xEA, 0x7A,0xB7,0xBD,0xEA,0x7B,0xDE,0xA7,0xBD, +0xCA,0x72,0x8D,0x91,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFE,0xF7,0xEF,0xFB, +0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFE, 0x87,0xFF,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6, +0xFD,0xBF,0x6F,0xF6,0xFD,0xBF,0x6F,0xDB, 0xF6,0xFD,0xBF,0x6F,0xFE,0x4F,0xFF,0xBF, +0xEF,0xBB,0xEE,0xFB,0xBE,0xEF,0xBB,0xEF, 0xBE,0xEF,0xBB,0xEE,0xFB,0xBE,0xEF,0xBB, +0xEF,0xFC,0x5F,0xFF,0xFF,0xFF,0x3F,0xCF, 0xF3,0xFC,0xFF,0x3F,0xCF,0xFC,0xFF,0x3F, +0xCF,0xF3,0xFC,0xFF,0x3F,0xCF,0xFD,0x9F, 0xFE,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF, +0xEB,0xFE,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF, 0xAF,0xEB,0xFF,0xE1,0x6F,0xFD,0xFF,0x7F, +0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFD,0xFF, 0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF, +0x7A,0xBF,0xFB,0xFE,0xDF,0xB7,0xED,0xFB, 0x7E,0xDF,0xB7,0xFB,0x7E,0xDF,0xB7,0xED, +0xFB,0x7E,0xDF,0xB7,0xFF,0xC9,0xFF,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, +0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEE, 0xFB,0xFE,0xBB,0xFF,0xFE,0xFF,0xBF,0xEF, +0xFB,0xFE,0xFF,0xBF,0xEF,0xFE,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0x3F,0xCF,0xFF,0xE7, +0xFE,0xFF,0xF5,0xFD,0x77,0x5D,0xD7,0x35, 0xDD,0x77,0xD7,0xF5,0xCD,0x7B,0x5D,0xD7, +0xF5,0xDD,0x77,0xFE,0x27,0xFF,0xFF,0x8B, 0xE2,0xF8,0xBE,0x2F,0x8B,0xE2,0xF9,0xAF, +0x8B,0xE2,0xF8,0xBE,0x2F,0x8B,0xE2,0xF9, 0xFE,0x1F,0xFF,0x5F,0xD7,0xF5,0xFD,0x7F, +0x5F,0xD7,0xF5,0xFF,0x5F,0xD7,0xF5,0xFD, 0x7F,0x5F,0xD7,0xF5,0xFF,0xFA,0x3F,0xFE, +0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,0xEB, 0xEC,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF, +0xEB,0xFF,0xFE,0x7F,0xFD,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE6, 0xFF,0xFA,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF, +0xF7,0xFC,0xFF,0xDF,0xF7,0xFD,0xFF,0x7F, 0xDF,0xF7,0xFD,0xFF,0xF5,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0x02,0xFF,0xFE,0xBF,0xAB,0xEB,0xFA, 0xBE,0xBF,0x23,0xEB,0xDE,0x1F,0xAF,0xEA, +0xFA,0xFE,0xAF,0xAF,0xEB,0xFD,0x97,0xFF, 0xF3,0xFC,0x7B,0x1F,0xCF,0xF1,0xFC,0x7F, +0x1F,0xF1,0xFC,0x77,0x1F,0xCD,0xF1,0xFC, 0xFF,0x1F,0xFE,0x87,0xFF,0xAF,0xEF,0xFA, +0xFE,0xFF,0xAF,0xEF,0xFA,0xFD,0xBF,0x2B, 0xFB,0x7E,0xBF,0xBF,0xEB,0xFB,0xFB,0xFB, +0xDF,0xFF,0xFB,0xF7,0xFF,0xFF,0x7F,0xF7, 0xF7,0xFF,0xFD,0xDF,0xFE,0xFC,0xDF,0xFF, +0xDF,0xFF,0xFD,0xFF,0xDA,0xBF,0xFF,0xBB, 0xEF,0xFB,0xF9,0xFF,0xBE,0xEF,0xFB,0xFB, +0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xF7,0x7F,0xFD,0xD7,0xFF,0xFF,0x7F, +0xFF,0xFF,0xFF,0xFE,0xF7,0xFF,0xFE,0xFF, 0xF7,0xFF,0xFF,0x7F,0xFF,0xFF,0xEC,0xFF, +0xFF,0xFE,0xDF,0xBF,0xFF,0xFB,0xFE,0xFF, 0xBB,0x68,0xAE,0x1F,0xAE,0xFB,0xFB,0xFF, +0xFF,0xBF,0xFF,0xD5,0xFF,0x7F,0xFF,0xFF, 0xF7,0xFE,0xFE,0xFF,0xBF,0xEF,0x9F,0xFD, +0x7F,0xFF,0xCB,0xFF,0xFF,0xDF,0xFF,0xFF, 0xBB,0xF7,0xBF,0xFF,0xFF,0xFF,0xFF,0xDF, +0xFF,0xBF,0xFB,0xFF,0xFF,0xFF,0xDE,0x3F, 0xFF,0xFF,0xFF,0xFF,0xFF,0xA7,0xFF,0xFF, +0xFF,0xFF,0xEF,0xFF,0x7F,0xFB,0xFD,0xFB, 0x7F,0xFF,0xFF,0xFF,0xFF,0xCF,0xF3,0x7C, +0xFF,0x7F,0x8D,0x7F,0xFF,0xFF,0xFF,0xFF, 0xFB,0xFF,0xF7,0xFB,0xFE,0xFD,0xFF,0xFF, +0xFF,0xFF,0xF7,0xFD,0xFF,0x7F,0xFD,0x1F, 0xFD,0xFF,0xFF,0xFF,0xFF,0xBF,0xDF,0xFF, +0xFF,0xFE,0x5C,0xFF,0x6D,0xFF,0x7F,0xAB, 0xE7,0xF1,0xFF,0xFD,0x9F,0xFF,0xFF,0xAD, +0xEB,0x7A,0x3F,0x1F,0xFF,0xFF,0xFE,0xBF, 0xAF,0xF3,0xDE,0xF5,0xFF,0x8F,0xFB,0xDF, +0xE6,0x7F,0xFF,0xDF,0xF3,0xFD,0xFF,0x7E, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xF7,0xF3, +0x7F,0xDF,0xF7,0xEF,0xFF,0xF6,0x3F,0x9F, 0xDF,0xFF,0xFF,0xEE,0xFF,0xFF,0xEF,0xFB, +0xFF,0xFF,0xF9,0xFB,0xFE,0x4F,0xBF,0xEF, 0xBB,0xFF,0x69,0xAF,0xAF,0xFC,0xFF,0x3F, +0xDD,0xFF,0xFC,0xBF,0x8F,0xFF,0xFD,0xF3, 0xBF,0xED,0x9E,0xFC,0xBF,0x6F,0xF5,0xD3, +0xDF,0xFF,0xDB,0xD6,0xF5,0xEF,0xFD,0xFE, 0xFF,0xB9,0xFF,0x1F,0xD2,0xA9,0xAF,0xFF, +0xDB,0xF7,0xBF,0xEF,0x46,0xFF,0xFF,0xAD, 0xEB,0x7A,0xDF,0xEF,0xF7,0xFF,0x7F,0xF7, +0x9F,0xED,0xFF,0x7F,0xFF,0xAD,0xEB,0x7F, 0xF5,0x6F,0xFF,0xFD,0xFB,0xD6,0xF4,0xF7, +0xFB,0xF9,0x7E,0x7F,0xFF,0x5F,0xC2,0xFE, 0xBF,0xFD,0xFB,0x33,0xDF,0xF9,0x5B,0xFF, +0xFF,0xDD,0x67,0x7D,0xCF,0xEF,0xDB,0xEC, 0xFF,0x77,0xDD,0xF7,0xFD,0xFF,0xFF,0xDE, +0xA7,0xBF,0xD4,0x9F,0xFF,0xFF,0xBF,0xEF, 0xFE,0xFF,0xDF,0xEF,0xBB,0xFF,0xFF,0xEF, +0xEB,0xFA,0xFF,0xEF,0xBD,0xFB,0xFF,0xE2, 0x7F,0xFF,0xDF,0xDF,0xF7,0xFD,0xBF,0xBB, +0x73,0xF7,0xFD,0x7F,0xDF,0xDE,0xF7,0xBF, 0xEA,0xDB,0xF6,0xFF,0xD6,0xFF,0xFF,0x66, +0xFF,0xBE,0xFF,0xBF,0x6B,0xD9,0xF6,0xDF, 0xFF,0xFB,0x7E,0x7F,0xB7,0x7E,0xFF,0xFE, +0xFF,0xCD,0xFF,0xFE,0x7F,0xFF,0xFC,0xFD, 0x3F,0xFB,0xFB,0xF7,0xFF,0xFF,0xFB,0xF6, +0x7D,0xFE,0x7F,0xFF,0xFC,0xFF,0xB9,0xFF, 0xF9,0xFA,0xFE,0xBF,0xAF,0x5B,0xD6,0xED, +0xAD,0x7B,0xF6,0xF9,0xBF,0xEF,0xF8,0xFA, 0xFE,0xBF,0xFE,0xE6,0xFF,0xFF,0xF7,0xFD, +0xFF,0x7F,0xBF,0xEF,0xF3,0xFF,0xFF,0x6F, 0xF7,0xFE,0xFF,0xFF,0xF7,0xFD,0xFE,0xF7, +0xEF,0xFF,0xFB,0xEF,0xFB,0x7E,0xDE,0xFE, 0xFF,0xBF,0xFF,0xFE,0xFF,0xFF,0xFB,0xFF, +0xFF,0xEF,0xFB,0x6F,0xFC,0x1F,0xFE,0xE7, 0xFF,0xFF,0xFF,0xEF,0xFF,0xD3,0xB4,0xBB, +0xFF,0xFF,0xFD,0xBF,0x6F,0xE3,0xFE,0xFF, 0xBF,0xFC,0xBF,0xF7,0xCF,0xF7,0xFD,0xFF, +0x2F,0xDF,0xAB,0xEA,0xFF,0xDF,0xE7,0xEA, 0x9A,0xAF,0xEF,0xFB,0xFE,0xFF,0xF5,0x3F, +0xFD,0x7E,0xFF,0xD7,0xF5,0xFB,0xFF,0xFD, 0xF7,0xFF,0x7F,0xFE,0xF7,0xFD,0xFF,0xD7, +0xFF,0xD7,0x7F,0xEE,0x7F,0xFA,0x79,0xFE, 0x2F,0x8B,0xE6,0xF9,0xFE,0x3F,0x9E,0xF9, +0xBE,0x2F,0x0B,0xE7,0xF9,0xFE,0x2F,0x9F, 0xFD,0xFF,0xFE,0x7D,0x7F,0x5F,0xD7,0xFF, +0xFF,0x7F,0xFF,0xFD,0xFF,0x7F,0x5F,0x97, 0xFF,0xFD,0x7F,0x5F,0xFF,0xE3,0xFF,0xFF, +0xFA,0xFE,0xBF,0xAF,0xFB,0xFB,0xFF,0xFF, 0xCF,0xEB,0xFE,0xBF,0xAF,0xFF,0xFA,0xFE, +0xBF,0xFF,0x87,0xFF,0xFF,0xF5,0xFF,0xFF, 0xFF,0xFF,0xFD,0xFF,0x7F,0xFF,0xFF,0xFF, +0xFB,0xFF,0xFF,0xF5,0xFF,0xFF,0xFE,0x0F, 0xFF,0xFD,0xEB,0xFF,0xFF,0xF7,0xFF,0xEF, +0x7B,0xDF,0xFE,0xFF,0xFF,0xDF,0xF7,0xFD, 0xEB,0x7F,0xDF,0xFF,0x5F,0xFF,0xFF,0xFF, +0xFF,0xFD,0xBF,0xFF,0x7E,0xFA,0xBF,0xC7, 0xDB,0xF7,0xBD,0x3F,0xFB,0xFF,0xF6,0xFF, +0xFA,0xAF,0xFF,0xEB,0xFA,0xFE,0x3F,0x2F, 0xEA,0xFA,0x3E,0xAD,0xC9,0xBA,0xF6,0xAD, +0xAF,0xEB,0xFA,0xF6,0xBF,0xFE,0x7F,0xFF, 0xFF,0xFD,0xFF,0xF1,0x7F,0x3F,0xCF,0xF1, +0xEF,0xFF,0x7F,0xFF,0xBC,0xDF,0xDF,0xF7, 0xDD,0xFF,0xE0,0x7F,0xFF,0xFF,0xFE,0xFF, +0xFA,0xEC,0xBB,0x7F,0x5F,0xFF,0xFB,0xEC, 0xFF,0xEF,0xB7,0xFF,0xF7,0xFF,0xFF,0xB5, +0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xEE,0xDF, 0x5F,0xDF,0xDE,0xFF,0xAE,0xE7,0x77,0xFF, +0xFF,0xDF,0xF7,0xFF,0xE3,0xFF,0xFA,0xBB, 0xFE,0xFF,0xAF,0xFD,0xFB,0xFE,0xBF,0xAB, +0xF9,0xFE,0xFF,0xBF,0x7F,0xBF,0xFE,0xBD, 0xFE,0xD7,0xFF,0x9F,0xFD,0xFF,0xBE,0xEF, +0xFF,0xEE,0xFD,0xBB,0x5B,0xEF,0xFF,0x7F, 0xEF,0xFF,0xEF,0xFF,0x7F,0xFF,0x4F,0xFF, +0xEF,0xFB,0xBC,0xFC,0xFF,0xFF,0xFF,0xFE, 0xFE,0xFD,0xFA,0xFE,0xFB,0xFF,0xFD,0xF3, +0xFB,0xFF,0xF8,0x5F,0xFF,0xFF,0xD7,0xF5, 0xFD,0xDF,0xEF,0xFF,0xF3,0xDC,0x5F,0xCE, +0xF5,0xBD,0xFF,0xFF,0xD7,0xFF,0xFF,0xF9, 0x3F,0xFF,0xDF,0xF7,0xFF,0xFE,0xFF,0xFD, +0xFF,0xFB,0xFF,0xF7,0xB9,0x7D,0xFE,0xDF, 0xFF,0xFF,0xFF,0xFF,0xF9,0x7F,0xFF,0xFE, +0xFF,0xFF,0x7F,0xFF,0xFE,0xFF,0xFF,0xF7, 0xF6,0xFF,0xBF,0xF1,0xF8,0xFF,0xFF,0xFF, +0xFF,0xE0,0xFF,0xFF,0xFF,0xFF,0xF9,0xFF, 0xFF,0xFF,0xFF,0xFF,0xEF,0xEF,0xFF,0xFF, +0x9B,0xFB,0x7F,0xFF,0xFF,0xFF,0xC1,0xFF, 0xDF,0xFF,0x3F,0x5F,0xD7,0xBF,0xEF,0xBB, +0xDE,0xEE,0xFF,0x7F,0xDF,0xFF,0xFE,0xF5, 0x7F,0xDF,0xFF,0x99,0xFF,0xFF,0xFA,0xFF, +0xBF,0xFD,0xEB,0x7A,0xFF,0xB7,0xFE,0xFE, 0xFF,0xFF,0xEF,0xFF,0xFF,0xFD,0xBF,0xFF, +0x97,0xFF,0xFD,0xF7,0xFF,0x7F,0xF7,0xFF, 0xFF,0xFD,0x5F,0xFE,0xF3,0xF9,0xDF,0xDF, +0xFF,0xFF,0xFC,0xFF,0xFF,0x83,0xFF,0xFF, 0xFE,0xFF,0x9E,0xEC,0xFB,0xEE,0xFF,0x9F, +0xBF,0xEF,0xFF,0xFE,0xED,0x7B,0xFF,0xFF, 0xFF,0xF1,0x5A,0xFF,0xFF,0xFD,0xFF,0x7C, +0x69,0x3B,0xDF,0xFF,0x7F,0x1F,0xDF,0xFF, 0xFD,0xBA,0xFF,0xFF,0xFB,0xFF,0x5B,0xBD, +0xFF,0xFF,0xFF,0xFF,0xD7,0xB6,0xED,0xE9, 0xFF,0xD6,0xBD,0x6F,0x5F,0xFB,0xFF,0xEF, +0xFF,0x5F,0xFE,0xF6,0x6F,0xFF,0xFF,0xFF, 0xFF,0xF7,0xEB,0x7A,0xDF,0xFF,0x9F,0x7F, +0x7F,0xFF,0xB7,0xFF,0xFF,0xFE,0xDF,0xFF, 0x6C,0xFF,0xFB,0xFF,0xBB,0x6F,0xEB,0xFE, +0xCC,0xF7,0xA5,0xFA,0x5C,0xF5,0x75,0xBB, 0xB7,0xDF,0xFE,0x6F,0x5F,0xC5,0xBF,0xFD, +0x7B,0xFE,0xFF,0x95,0xE7,0x29,0xCF,0x4F, 0xF5,0x91,0xEE,0x6B,0xDF,0xEF,0xFD,0x54, +0xF5,0xBD,0xB1,0xFF,0xEF,0xEE,0xFB,0xBE, 0xBF,0xAF,0xFE,0xDE,0xBD,0x6F,0xDA,0xF2, +0xFF,0xAF,0xBE,0xFF,0xFF,0xFD,0x7E,0xA7, 0xFF,0xF7,0xFF,0xBF,0xEF,0x7B,0xF6,0xFD, +0xBD,0x4A,0xF2,0x85,0x85,0xBF,0x5B,0xFE, 0xB5,0xFD,0xFA,0xFF,0x4F,0xFF,0xFE,0xDF, +0xFF,0xED,0xFF,0xBF,0xFF,0xBF,0x7F,0xFE, 0xFF,0xB7,0x6D,0xFF,0xF7,0xBF,0xBF,0xEF, +0xFD,0x1F,0xFF,0xFE,0x7D,0xFF,0x67,0xFF, 0xFF,0xFF,0x3F,0x7F,0xFE,0xBF,0xFF,0xE7, +0xDF,0xE7,0xFF,0xEF,0x6B,0xFC,0x1F,0xFF, 0xBF,0xEF,0xFB,0xFE,0xDE,0xBF,0xAF,0xFA, +0xFF,0xB6,0xEF,0xF9,0xFE,0xFF,0x8F,0xEF, 0xDB,0xEF,0xAB,0x6F,0xFB,0xFE,0xFF,0xFF, +0xEF,0xFD,0xFF,0x7F,0xFF,0xFF,0xDE,0xFF, 0xFF,0xEF,0xFF,0xFF,0xFF,0x3F,0xFF,0x6C, +0xFF,0xBF,0xFB,0xFF,0xFE,0xFF,0xFB,0xFE, 0xDF,0xFF,0xFF,0xEF,0xFF,0xFF,0xBF,0xFF, +0xFF,0xFE,0xFB,0xFF,0xD5,0x7F,0xFF,0xFF, 0xEF,0xFB,0xFF,0xFF,0xBF,0xEF,0x43,0xB5, +0xFD,0x6F,0xCF,0xD6,0xBE,0x3F,0x7F,0xDB, 0xFE,0xC3,0xFF,0xFD,0xFF,0xAF,0xEB,0xFB, +0xFC,0xFF,0x3E,0xEF,0xE8,0xFA,0xBD,0xCD, 0xAA,0xFE,0xFE,0x7D,0xCF,0xFF,0xB7,0xFF, +0xF7,0xFF,0xFF,0xFF,0xFD,0xFF,0x75,0xCD, 0x52,0xD7,0xFD,0xFB,0xF7,0xDD,0xFB,0xEF, +0xEB,0xFF,0xFF,0x4F,0xFF,0xBF,0x9F,0xE7, 0xF9,0xFC,0x7F,0x8B,0xC3,0xF9,0xAF,0x8F, +0xE7,0xE9,0xBE,0x7F,0x9F,0xE6,0xF9,0xFC, 0x5F,0xFF,0xFF,0xF7,0xFD,0xFF,0x7A,0x5F, +0xD7,0xED,0xFF,0xFF,0xD7,0xFF,0xDD,0x7F, 0xE7,0xFF,0xFC,0xFF,0xFC,0x3F,0xFF,0xFF, +0xFF,0xFB,0xFF,0xFE,0xBF,0xAF,0xFF,0xFD, 0xFF,0xEF,0xFF,0xEB,0xFF,0xFF,0xFF,0xFF, +0xFF,0xF7,0x7F,0xFF,0x7F,0xDF,0xFF,0xFD, 0xFD,0x7F,0xFE,0xF7,0xFD,0x7F,0xDF,0xFF, +0xFD,0xFF,0xFF,0xDF,0xFB,0xFF,0xEE,0xFF, 0xFB,0xFF,0xF7,0xFD,0xFF,0x7A,0xDF,0xF5, +0xFD,0xFA,0xDF,0xF7,0xFC,0xFF,0x7F,0xDF, 0xBF,0xED,0xFF,0xC9,0xFF,0xDF,0xFF,0xBF, +0x2F,0xFB,0xFF,0xBC,0xAD,0xFF,0xF7,0xFF, 0xFF,0xEF,0xD3,0xFF,0x7D,0xBF,0x6F,0xFF, +0xFA,0xFF,0xFE,0xBF,0xAE,0xEA,0xFA,0xBE, 0xAD,0xA5,0xEB,0xCE,0xBF,0xA7,0xEB,0x5A, +0xDE,0xBD,0xAF,0x6B,0xFD,0x57,0xFF,0xFF, 0xF4,0x7F,0x1F,0x7F,0xFD,0xFF,0x7F,0x36, +0xF0,0xDF,0x79,0xFF,0xFF,0xFF,0xF7,0xFD, 0xBF,0xFF,0x87,0xFF,0xFB,0xF3,0xFC,0xFF, +0xFF,0xFF,0xFF,0x7E,0xFF,0xBF,0xDF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xBF,0xF8,0x9F, +0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFD, 0xF7,0xFC,0xBD,0xFF,0xFE,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFB,0xF9,0xBF,0xFF,0xFF,0xEB, 0xE2,0xFE,0xFF,0xBF,0xEF,0xA9,0xBA,0x2F, +0xEB,0xF9,0xFE,0x77,0xDF,0xF7,0xFF,0xFF, 0xF9,0x7F,0xFF,0xFF,0x7F,0xEF,0xD7,0xFF, +0xFD,0xFF,0xFB,0xF5,0xFF,0xBF,0x6F,0xDF, 0xFF,0xFF,0xFD,0xFF,0xFF,0xF0,0xFF,0xFF, +0xFF,0x3F,0xCF,0xFF,0xBA,0xEE,0x9B,0xBF, 0xEE,0xD7,0xFE,0xCD,0xEF,0xFF,0xDF,0xBF, +0xFF,0xFF,0xC5,0xFF,0xFF,0xFD,0x7F,0x4F, 0xFD,0xF6,0xD9,0xFF,0x4F,0xD6,0xFD,0xBF, +0x6E,0xFF,0xFF,0xF4,0x7F,0xFF,0x7F,0x8B, 0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xF9,0xFE, +0x37,0xFF,0xD9,0xFB,0xF5,0xAF,0xFD,0xFF, 0xFF,0xFB,0xFF,0xFF,0x07,0xFF,0xFF,0xFF, +0xFB,0xF7,0xFF,0xFD,0xFF,0x7C,0xFA,0x7E, 0x4F,0xFC,0xDF,0x1D,0xC7,0xFF,0xFF,0xFF, +0xFF,0xAE,0xFF,0xFF,0xFF,0xFF,0xFD,0xFB, 0xFF,0xFF,0xFE,0xFE,0xFC,0xFF,0x7F,0x7F, +0xBF,0xEF,0xFE,0xFF,0xFF,0xFF,0x5F,0xFD, 0xFF,0xFF,0xFF,0xFD,0x6F,0x5A,0xD7,0x7B, +0xBE,0x5F,0xFE,0x39,0xFF,0xF7,0xFF,0xF7, 0xFD,0xFE,0xAA,0x1F,0xFF,0xFF,0xFF,0xFF, +0xFE,0xFE,0xAB,0xAF,0xFD,0xFE,0xBF,0xFF, 0xF7,0xFF,0x7F,0xFE,0x8F,0xE3,0xFB,0xEE, +0x7F,0xFF,0xFF,0xFF,0xFF,0xEB,0xFB,0xFF, 0xFD,0xBF,0xEF,0xDF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFB,0xE4,0x3F,0xFF,0xDF, 0xFF,0xFF,0xFF,0xFF,0xF3,0xEF,0xBB,0xFB, +0xBF,0xEF,0xBB,0xFF,0xD7,0xBF,0xFF,0xFF, 0xFF,0x29,0xAF,0xF7,0xFF,0xFF,0xFB,0xFF, +0xFB,0xE6,0xFF,0x0F,0xFB,0x3F,0xDF,0x0F, 0xFF,0xAF,0xFF,0xFF,0xFF,0xF5,0xC3,0xDF, +0x5F,0xFF,0xFF,0xFF,0xFE,0x6B,0xCA,0xBE, 0xBC,0xFF,0x9F,0xF2,0xBF,0xFF,0xFE,0xFA, +0xFF,0xFF,0xEF,0x16,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFC,0xDF,0x97,0xFD,0x79,0xFF,0x37, +0xE7,0x7F,0xFF,0xFF,0xB5,0xFF,0xFF,0xF6, 0x2F,0xFF,0xFD,0xFB,0xFE,0xFF,0xFF,0xFD, +0x5F,0x57,0x5F,0xFF,0xDB,0x52,0xDF,0xFF, 0xFD,0xBF,0xFF,0xFF,0xFC,0xDB,0xFF,0x7B, +0xB5,0xFD,0x7F,0xFF,0x71,0x9C,0x6E,0xFF, 0xF6,0x35,0xA5,0x9B,0xFF,0xFF,0xFD,0xFF, +0xFF,0xDB,0x9E,0x7F,0xFE,0xEF,0xFB,0xFF, 0xFF,0xBD,0xEF,0xFF,0xDE,0xB7,0xF9,0x4B, +0xFF,0xF5,0xEF,0xFF,0xFF,0xFF,0xE8,0x7E, 0xFF,0xEA,0xDF,0xF7,0xFF,0xFD,0x69,0x5B, +0xFC,0x9F,0xEF,0x78,0xD6,0xFF,0xEB,0xEF, 0xFF,0xFF,0xFF,0xE8,0xFF,0xFF,0xED,0xFF, +0xFF,0xFF,0xFF,0xE3,0xF9,0xF6,0xBF,0xFF, 0xFF,0xFE,0xDF,0xFF,0x7F,0xFF,0xFF,0xFF, +0xD1,0xFF,0xFF,0xE7,0xFF,0xFF,0xFF,0xFF, 0xE7,0xF9,0xFF,0xBF,0x7F,0xD9,0xFF,0xFD, +0xFE,0x7F,0xFF,0xFE,0xFF,0xF9,0xFF,0xFB, 0xD6,0xDF,0xBF,0xEF,0x5B,0xD6,0xFF,0xBF, +0xFB,0xF6,0xFF,0xBF,0xEF,0xF8,0xF6,0xDD, 0xBE,0xFE,0x16,0xFF,0xBF,0xEF,0xFF,0xFE, +0xFF,0xBF,0xEF,0xFF,0xFF,0xFF,0x6F,0xFB, 0xFF,0xFF,0xFF,0x6F,0xF3,0xFF,0xF7,0xEF, +0xFB,0xFF,0xBF,0xFF,0xEF,0xFE,0xFF,0xBF, 0xFF,0xFF,0xFF,0xBE,0xBF,0xFF,0xEF,0xFF, +0x7F,0xEF,0xFF,0xFD,0x17,0xFB,0x7B,0xFF, 0xFF,0xFD,0x7F,0xDB,0xF6,0xF4,0x7F,0xFA, +0xFE,0xF5,0xBF,0xEB,0xE3,0xF7,0xFF,0xFF, 0xE9,0xBF,0xFF,0xAF,0xF7,0xFD,0xF3,0x7E, +0x8F,0xA3,0xEA,0xFF,0xCB,0xF3,0xEE,0xFF, 0xBF,0xEF,0xF7,0xF9,0xFF,0xFE,0x7F,0xFF, +0xFF,0xFF,0xFF,0xF5,0xFB,0xF6,0xFF,0xF5, 0x2F,0xFE,0xFB,0xD7,0xBF,0xFF,0xBE,0xDF, +0x9F,0xFF,0xF0,0xFF,0xFF,0xF9,0xFE,0x7F, 0x8F,0xA3,0xF8,0xFE,0x6F,0x9F,0xF9,0xF6, +0x2F,0x9F,0xE7,0xF9,0xFE,0x2F,0x9F,0xE1, 0xFF,0xFF,0xFF,0x7F,0xDF,0xF7,0xF5,0xFD, +0x7F,0x7F,0xF5,0xFF,0x9F,0x5F,0xFB,0xFE, 0xFF,0x7F,0xFF,0xFF,0xCB,0xFF,0xFF,0xFB, +0xFE,0xFF,0xBF,0xAF,0xFB,0xFE,0xFF,0xDF, 0xFE,0xFE,0xBF,0xF7,0xFF,0xFF,0xFF,0xFF, +0xFF,0xC7,0xFF,0xFF,0xFD,0xFF,0x7F,0xDD, 0xF7,0xFD,0xFF,0xFF,0xD7,0xFF,0xFD,0x7F, +0xFF,0xFB,0xFD,0xFF,0xFF,0xFE,0xEF,0x7F, 0xFD,0xEF,0xFB,0xFE,0xFB,0xFD,0xFF,0x7F, +0xDF,0xFD,0xFF,0x7A,0xDF,0xF7,0xFD,0xFF, 0xFF,0xFF,0xFF,0x1F,0xFF,0xFF,0xD3,0xF7, +0xFF,0xFF,0x6F,0xDB,0xFF,0xFF,0xEF,0xCB, 0xF4,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, +0x29,0xFF,0xE8,0xDA,0x76,0x9F,0xAF,0x6A, 0xDA,0xFE,0x35,0xEB,0xDA,0xD6,0xBF,0xAB, +0xEB,0x7A,0xDE,0xBF,0xD7,0x7F,0xFF,0xFE, 0xFF,0xBF,0xEF,0xFD,0xDF,0x77,0xBF,0xFD, +0x37,0xEF,0xFF,0xEF,0xFF,0x3F,0xFF,0xFF, 0xFF,0xFE,0x7F,0xFF,0xFF,0xFF,0xF7,0x7E, +0xDF,0xFF,0xFF,0xFF,0xFA,0xB7,0x7F,0xFF, 0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0x89,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x9F,0xFB,0xFF,0xFF,0xFF,0xE7,0xFF, +0xFF,0xFF,0xFF,0xAA,0xFF,0xAB,0xFB,0xFA, 0xEF,0xBF,0xFF,0xDF,0xFA,0x7B,0xB9,0xFE, +0xFE,0xFF,0xFD,0xFF,0xF7,0xFE,0x3F,0xFF, 0xB7,0xFF,0xF7,0xEE,0xFF,0x7F,0xEF,0xFF, +0xFF,0x7F,0xFF,0x1F,0xFB,0xFF,0xBF,0xFB, 0xFE,0xFF,0xBD,0xFF,0xFF,0x2F,0xFF,0xBF, +0xFF,0x7F,0xDF,0xFA,0xFF,0xFF,0xFC,0xEE, 0xF5,0xF3,0xBE,0xFB,0x0F,0xEF,0xF3,0xBE, +0xEF,0xFC,0x5F,0xFF,0x5A,0xFF,0xF7,0xDF, 0xFF,0xFF,0xFE,0xD5,0xFC,0x5F,0xFB,0xF2, +0xFF,0xFF,0x2F,0xBB,0xF3,0xFF,0xFF,0xBF, 0xFF,0xEF,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF, +0xBF,0xFF,0xFF,0xFD,0x7B,0xFF,0xDF,0xB9, 0xFF,0xFB,0xFF,0xD8,0x7F,0xFF,0xFF,0xFF, +0xFB,0xFF,0xFC,0x7F,0x1F,0xBF,0xE0,0xDF, 0xF7,0xEF,0xFF,0xFD,0x7F,0xFE,0xDF,0xFF, +0xE0,0xFF,0xFF,0xFD,0xEF,0xFB,0xFF,0xFE, 0xF7,0xDF,0xFF,0xEB,0x5F,0xFF,0xF7,0xFF, +0xFF,0xFF,0xFF,0xBF,0xFF,0xFD,0xFF,0xFD, 0xFF,0xFF,0xFF,0xF7,0xFD,0xFF,0x3B,0xDC, +0xFD,0x6D,0x7B,0x5F,0x57,0xF5,0xFD,0x7F, 0x5F,0xFF,0xB1,0xFF,0xEB,0xFF,0xFF,0xFF, +0xFB,0xFB,0xFE,0xFF,0xBF,0xFB,0xBE,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFF,0xAF,0xFE,0xF7, +0xDF,0xDF,0xFF,0xFF,0xFF,0x7F,0xCF,0xF3, 0xF8,0xFF,0xD7,0xFB,0xFF,0x5F,0xBF,0xF7, +0xFB,0xFF,0x7F,0xFE,0x23,0xFF,0xFF,0xFE, 0x7F,0xF3,0xFF,0xFB,0xFE,0xFF,0xFF,0xF3, +0xFF,0xFF,0xF5,0xF9,0xFF,0x3F,0xFF,0xFF, 0xF0,0x9A,0xFF,0xBE,0x7F,0xFF,0xFC,0xF9, +0xFF,0xFD,0xAF,0xEB,0xFE,0xBF,0xFF,0xCF, 0xF3,0xFE,0x7F,0xFF,0xFF,0x5B,0xBD,0xFF, +0xBC,0xEB,0xFF,0xD7,0xD4,0xAF,0xAF,0xFD, 0xFF,0xCF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, +0xFD,0xFE,0xFF,0x6F,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFD,0x7F,0x5E,0xFD,0xBF,0xDB,0xF6, +0xFD,0xBF,0x6F,0xFB,0xEE,0xFD,0xFF,0x7A, 0xFF,0xFA,0xFB,0xFF,0x3F,0xFB,0xB7,0x5F, +0xD6,0xF7,0x1F,0x71,0xDC,0x77,0x1D,0xC7, 0x31,0xDC,0x77,0xDF,0xF9,0xBF,0xF5,0x5B, +0xF4,0xD7,0x9D,0xAE,0xFF,0xBF,0xFD,0xBF, 0xDB,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,0xFE, +0x3D,0x81,0xFF,0xEB,0xFE,0xFE,0xFE,0xFF, 0xEB,0x7A,0xDF,0x7D,0x77,0x7D,0xF5,0x79, +0xDF,0x57,0xDD,0xF5,0x7D,0x7E,0xE6,0xFF, 0xD6,0x3F,0xBF,0x7F,0xFF,0xD4,0xF5,0x3F, +0xBF,0xFB,0xBE,0xEF,0xB3,0xEE,0xFB,0x9E, 0xEF,0xBB,0xFE,0x8B,0xFF,0xFE,0xDF,0xB7, +0xED,0xFF,0xF7,0xFD,0xFE,0xFF,0xEF,0xBB, 0xEE,0xFF,0xBE,0xEF,0xBB,0xEE,0xEB,0xFC, +0x1F,0xFF,0xFF,0xFD,0xFF,0xE7,0xFF,0xF7, 0xFD,0xFF,0xEF,0xFE,0xFF,0xBF,0xEF,0xFB, +0xFE,0xFF,0xBF,0xEB,0xFA,0x1F,0xFF,0xB7, 0xEF,0x5B,0xFE,0xFF,0xAF,0xEB,0xDD,0xE7, +0xDE,0x77,0x9D,0xE7,0x79,0xDE,0x77,0x9D, 0xBF,0xE6,0x6F,0xFF,0xFE,0xFF,0xBF,0xEF, +0xFB,0xFE,0xFD,0xBF,0x6F,0xF6,0xFD,0xBF, 0x6F,0xDB,0xF6,0xFD,0xBF,0xFF,0x7E,0xFF, +0xFF,0xFB,0xFE,0xFE,0xFF,0xEF,0xFB,0xFD, 0xEF,0x7E,0xF7,0xBD,0xEF,0x7B,0xDE,0xF7, +0xBD,0xEF,0xFF,0xD5,0xFF,0xBF,0xFF,0xEF, 0xFE,0xFF,0xFC,0x3F,0x0F,0xE7,0xFE,0x7F, +0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7,0xFE, 0xF3,0xFF,0xFE,0xDF,0xAD,0xDF,0x67,0xEE, +0xFB,0xBF,0xEF,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFE,0xFF,0xBF,0xEF,0xFF,0x23,0xFF,0xFF, +0xFF,0xFF,0x7F,0xFF,0xF3,0xBC,0xDB,0xFE, 0xFB,0xFF,0xFB,0xBE,0xF7,0xFB,0xFF,0x7F, +0xDF,0xFF,0xCF,0xFB,0xFF,0x9F,0xE3,0xF9, 0xBE,0x3F,0x8F,0xE7,0x79,0xFF,0x9D,0xE7, +0xF9,0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x5F, 0xFF,0xCF,0xF7,0xFF,0xFF,0xFF,0xDF,0xF7, +0xFE,0x7F,0xE7,0xF9,0xFE,0x7F,0xFF,0xFF, 0xFB,0xFE,0xFF,0xFF,0xBF,0xFF,0xBF,0xBF, +0xFF,0xFE,0xFF,0xBF,0xEF,0xFF,0xFD,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFD,0xFF, +0xFF,0x3F,0xFF,0xBF,0xFF,0xF7,0xFF,0xFF, 0x7F,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xE8,0xEF,0xFF, 0x5F,0xF7,0xBF,0xF9,0xFE,0xDF,0xB7,0xFD, +0xFF,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0xDD,0xFF,0xF2,0xFF,0xBF,0xFF, +0xFF,0xBF,0xFF,0xFF,0x2F,0xF2,0xFF,0xBF, 0x2F,0x7B,0xD2,0xF7,0xBF,0x2F,0xFF,0xBB, +0xFF,0xEE,0x8F,0xAF,0xEB,0xFA,0xFE,0x3F, 0xA7,0x69,0xCE,0x8F,0xA4,0xEA,0xFA,0xEE, +0xB7,0xAE,0xEB,0xFD,0xC7,0xFF,0xF7,0xF7, 0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x3E,0xF3, +0x74,0xFF,0x3F,0x4F,0xFF,0xE7,0xFF,0x3F, 0xFE,0xA7,0xFF,0xFF,0xDF,0xF7,0xB7,0xFF, +0xF7,0xFF,0xBA,0xEF,0x37,0xEB,0xFB,0xFE, 0xBF,0xFB,0xFE,0xF3,0xFF,0xF9,0xDF,0xFF, +0xBF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF, 0xFD,0xDF,0xFF,0xFD,0xFF,0xFF,0xFB,0xFE, +0xFD,0xFF,0xFB,0xBF,0xFE,0x3F,0xED,0xFF, 0xDF,0xBE,0x3D,0xA7,0xFB,0xFA,0x3F,0xE6, +0xE1,0xFE,0xFE,0x3F,0xEF,0xE3,0xDF,0xF5, 0x7F,0xFE,0xFF,0x7E,0xFF,0xFF,0xFF,0xFF, +0xEF,0x6F,0xF6,0xFF,0x7D,0xEF,0xD7,0xDE, 0xFF,0x7D,0xEF,0xFF,0xF2,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0x7B,0xDE,0xFB,0xE6,0xEE, 0xEF,0x37,0x6E,0xF3,0x7E,0xEB,0x37,0xEF, +0xFF,0xC1,0xFF,0xFE,0xFF,0xF7,0xEF,0xFF, 0xFF,0xFF,0xBF,0x3F,0xD2,0xDF,0xBF,0x2F, +0x7B,0xE2,0xFF,0xFE,0x3B,0xBD,0xDB,0xFF, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFE, +0xFF,0xFB,0xFF,0xFF,0xBF,0xFF,0xFB,0xDF, 0xFF,0xBF,0xFF,0xB7,0xFF,0xFF,0xBF,0xEF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF, 0x7F,0xFF,0x1F,0xEF,0xF1,0xFD,0xFF,0xF6, +0xAF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF, 0xFF,0xFF,0xFE,0x9F,0xFF,0xFF,0xFF,0x77, +0xEF,0xF7,0xFB,0xFF,0xFE,0x5F,0xFF,0xFF, 0xBF,0xCF,0xFB,0xF7,0xDD,0xF7,0xF5,0xFF, +0x5F,0xD5,0xF5,0xFD,0x7F,0x5F,0xD7,0xF5, 0xFF,0xFB,0x0F,0xFF,0xFF,0xA9,0xEA,0x7A, +0xFF,0xAF,0x8F,0xFE,0xDF,0xAF,0xEF,0xFB, 0xFE,0xFF,0xBF,0xEF,0xFB,0xDF,0xE5,0x5F, +0xFF,0xFF,0xFF,0xFF,0xFF,0xBD,0x57,0xFF, 0xFF,0x6F,0x77,0xBF,0xF7,0xFB,0xFF,0x7F, +0xBF,0xF7,0xFF,0xFC,0xBF,0xFF,0x9F,0xFF, 0xFF,0xEF,0xFF,0xFE,0xFF,0xFF,0xFF,0x1F, +0xCF,0xFF,0xFC,0xFF,0xFF,0xFF,0xFF,0xFB, 0x65,0xAF,0xF3,0x7C,0xFF,0x3F,0xDF,0xFF, +0xFD,0xE9,0xFE,0x7F,0xE7,0xFF,0xFE,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFD,0xE3,0xDF,0xFB, +0xDB,0xF6,0xFD,0xEF,0x5B,0xFB,0xFF,0xDF, 0xFC,0xFF,0x3F,0xDF,0xF3,0xFD,0xFF,0x7F, +0xDF,0xEF,0x66,0xFF,0xDF,0xAD,0xEB,0x7A, 0xDE,0xF7,0xF7,0xE7,0xD9,0xFD,0x9F,0x67, +0xD9,0xF6,0x7D,0x9F,0xE7,0xDF,0xF5,0x47, 0xFD,0x65,0x5B,0xD6,0xF4,0xFE,0xFF,0xEF, +0xFF,0x6D,0xF6,0xDD,0xB7,0x6D,0xDB,0x76, 0xDC,0xB7,0x7D,0xFA,0x9B,0xF6,0x6D,0x9D, +0x67,0x59,0xDF,0xF7,0xDD,0xFF,0xEB,0xFE, 0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,0xE3, +0xD1,0x9F,0xFF,0xBD,0xBF,0xEF,0xFE,0xF7, 0xBF,0xBF,0xF7,0xD7,0x7F,0xDD,0xF7,0x9D, +0xDF,0x7F,0xDF,0xF7,0xFF,0xE0,0x7F,0xFD, 0xC1,0xDF,0xF7,0xFD,0xC7,0x7F,0x7F,0xFB, +0xFF,0xBB,0xEC,0xFB,0x3E,0xFF,0xBF,0xEC, 0xFB,0xFF,0xD8,0x7F,0xBF,0x6C,0xFF,0xBE, +0xFF,0xBF,0xED,0xFF,0xEF,0xFE,0xFB,0xBF, 0xEF,0xFB,0xFE,0xFF,0xBF,0xEE,0xFF,0xC5, +0xFF,0xAF,0x6F,0xFF,0xFC,0xFD,0x3F,0xE7, 0xFF,0xFE,0xFF,0xEF,0xFB,0xFE,0xFF,0xBF, +0xEF,0xFB,0xFE,0xBF,0x89,0xFE,0xFA,0xBA, 0xFE,0xBF,0xAF,0xFB,0xF6,0xF5,0xD9,0x7D, +0x97,0x65,0xD9,0x74,0x5D,0x97,0x65,0xD3, 0xFE,0xD6,0xFF,0xBF,0xF7,0xFD,0xFF,0x7F, +0xBF,0xCF,0xFB,0xFE,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFF,0xF6,0x8F,0xFB, +0xFF,0xEF,0xFB,0x7E,0xDB,0xFE,0xFF,0xBE, 0xEF,0xEE,0xFB,0xBE,0xEF,0xBB,0xEE,0xFB, +0xBE,0xFF,0xFF,0xDF,0xFF,0x43,0xFF,0xFF, 0xFB,0xEF,0x5F,0xB7,0xFE,0x7F,0xE7,0xF9, +0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0xF9, 0xBF,0xFE,0xAF,0x77,0xFD,0xFF,0x2F,0xAF, +0xA7,0xFE,0xFF,0xEF,0xFB,0xFE,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0xF1,0x7F,0xEF,0xDF, +0xFF,0x97,0xF5,0xEF,0xFF,0xDF,0xFF,0xFF, 0xBF,0xFF,0xBF,0xFF,0xFF,0xFE,0xFF,0xFF, +0xFF,0xE0,0xFF,0xFF,0xF9,0xFE,0x2F,0x8B, 0xE3,0xF8,0xBE,0x77,0x9F,0xF9,0xDA,0x77, +0x9D,0xE7,0x79,0xDE,0x77,0x9F,0xDD,0xFF, 0xFD,0xFD,0x7F,0x5F,0xD7,0xFD,0xFF,0x7F, +0xE7,0xFE,0x7F,0x97,0xE7,0xFB,0xFE,0xFF, 0xBF,0xEF,0xFF,0xAB,0xFF,0xEF,0xFA,0xFE, +0xBF,0xAF,0xFF,0xFA,0xFF,0xFF,0xDF,0xFF, 0xFB,0xFF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF, +0x67,0xFF,0xF7,0xF5,0xFF,0xFF,0xFF,0xDF, 0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF,0xBD, 0xEB,0xFF,0xFF,0xF7,0xAD,0xEB,0xFF,0xDF, +0xFD,0xFF,0x3F,0xDF,0xF7,0xFD,0xFF,0x7F, 0xDF,0xFF,0x5F,0xFF,0xF7,0xFF,0xFF,0xFD, +0xBF,0xFF,0xCB,0xF4,0xFF,0x7F,0xD3,0xF7, 0xFD,0x3F,0x7F,0xD3,0xF7,0xFF,0xFC,0x3F, +0xFF,0xEA,0xFA,0xBE,0xAF,0xAB,0xEB,0xBA, 0xF4,0x95,0x6B,0x52,0xD4,0xAD,0x2F,0x4A, +0xD2,0xF6,0xBF,0xD2,0x7F,0xF7,0x3F,0xFF, 0xFF,0xF3,0x7F,0xFF,0xFF,0xF7,0xFF,0xBA, +0xDF,0xFB,0xFD,0xFF,0xBF,0xFF,0xFB,0xFF, 0xF8,0x7F,0xEA,0xFF,0xFE,0xFE,0xDF,0xFF, +0xF7,0xFF,0x7F,0xBB,0xFF,0xFF,0xBF,0xDF, 0xFB,0xFF,0xFF,0xBF,0xFF,0xB1,0x7F,0xFF, +0xFB,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF, 0xCF,0xFE,0xFF,0xFF,0xEF,0xFF,0xF7,0xFF, +0xFF,0xFF,0xF1,0xFF,0x69,0xBE,0xFA,0xBF, 0xAF,0xE2,0xFF,0xFE,0xFD,0xAF,0xF3,0xFE, +0xFF,0xBF,0xEF,0xFB,0xFC,0xFF,0xFF,0x07, 0xFD,0x95,0xDB,0xDF,0x7F,0xDF,0xAF,0xFF, +0xF7,0xAF,0x36,0xFE,0xBF,0x65,0xEB,0xF6, 0xFE,0x9F,0x6F,0xFE,0x07,0xFF,0xCF,0xFF, +0xF8,0xFE,0xFF,0xCF,0xFF,0xF6,0xFA,0xE7, 0xFB,0xFE,0xFF,0xBB,0xED,0xF9,0xFF,0xFF, +0xFF,0x5F,0xFF,0xFF,0xFF,0x75,0xFF,0xEF, 0x7E,0xFD,0xE0,0xE8,0x5E,0xD3,0xE5,0xF9, +0x3E,0x5F,0xD7,0xF7,0xFF,0xFA,0x2F,0xFB, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0x7F, +0x7F,0xD7,0xF5,0x7D,0x5F,0x57,0xD5,0xF5, 0xEF,0xFF,0xF3,0x7F,0xFC,0x7F,0xFF,0xC7, +0xF1,0xFF,0xFF,0x1F,0xCF,0xB0,0xFF,0x3F, 0xCF,0xF3,0xFC,0xFF,0x3F,0xCE,0xFF,0xE4, +0xFF,0xDF,0x7F,0xFE,0xF7,0xBB,0xFF,0xFF, 0xDF,0xEF,0xEE,0xFF,0xBF,0xEF,0xFB,0xFE, +0xBF,0xBF,0xEF,0xFF,0xD1,0xFF,0xFF,0xFF, 0xFD,0xFB,0xFF,0xFD,0xFF,0xFB,0x9F,0xE9, +0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0xBF, 0xFF,0xB3,0xFF,0xFF,0xF7,0xFF,0xFF,0xAF, +0xF7,0xFF,0xB6,0x3F,0xEB,0xFA,0xFE,0xBF, 0xAF,0xEB,0xFA,0xFE,0xBF,0xFE,0xA7,0xFF, +0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xFF,0xFF, 0xFE,0x9F,0xF7,0xF9,0xFF,0x7F,0x9F,0xE7, +0xFF,0xFF,0xFE,0xAF,0x6F,0xFF,0xFF,0xFF, 0x9F,0xFF,0xDF,0xFF,0x7D,0x5F,0xDD,0xFF, +0xFB,0xBF,0xE7,0xBB,0xFF,0xFB,0xDF,0x6D, 0x5F,0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xEB,0xF7,0xFF,0xE7,0xEF,0xF7,0xFF,0xFF, 0x7F,0xFF,0xF7,0xFF,0xFC,0x8F,0xFF,0xEF, +0xFD,0xFE,0xFF,0xBE,0xF4,0xF2,0x7D,0xD7, 0xCF,0xFF,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xCF,0x6B,0xFF,0xBF,0x3F,0xFB,0xF2, 0xFC,0x7F,0xEB,0xFF,0x9F,0xFA,0xFF,0xFF, +0x3F,0xFF,0xF3,0xFF,0xFF,0xFD,0x70,0xF7, 0xFF,0xFF,0xBF,0xFF,0xFB,0xD7,0xFE,0xF5, +0x77,0xFF,0x15,0xDD,0x77,0xFD,0xFF,0x7F, 0xDF,0xF7,0xFB,0xCD,0xBF,0xFF,0xFD,0xFF, +0xFF,0xDF,0x37,0xCD,0xF9,0xEC,0xFE,0xEF, 0xBB,0xF4,0xFB,0x3F,0x4F,0xB3,0xFF,0xFD, +0xCB,0xFF,0xE9,0x7E,0x54,0x9F,0xE5,0x4B, 0xB7,0xFF,0xDD,0x7D,0xC7,0x71,0xDD,0x77, +0x5D,0xD7,0x75,0xCD,0x7F,0xD6,0xFF,0xD3, 0xF6,0xF9,0x3F,0x6D,0x95,0xAF,0x7F,0xFE, +0xFF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFE,0xF6,0xC7,0xFF,0xAD,0x7B,0xCA,0xFF, +0xBF,0xBF,0xEF,0xFD,0xE3,0xDF,0xB7,0xED, 0xFB,0x7E,0xDF,0x37,0xED,0xE3,0xFB,0xDF, +0xFF,0x52,0x5C,0x15,0xFD,0xCF,0x7F,0xDF, 0xFE,0xEF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEC, +0x7B,0xFE,0xFF,0xFE,0x3E,0x7F,0xDA,0xF7, 0xFD,0xFF,0x7F,0xFF,0xFF,0xFB,0xEF,0xBB, +0x6F,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFF, 0xF7,0x7D,0xFF,0xD8,0xFF,0xFD,0xBF,0x7F, +0xFB,0xFF,0xFF,0x9F,0xFB,0xFE,0x7F,0x9F, 0xE7,0xF9,0xFE,0x7F,0x9F,0xEA,0x7F,0xF6, +0xBF,0xBD,0x6A,0x5A,0xF6,0xE5,0xBF,0x77, 0x5F,0x6D,0xDD,0x77,0x5D,0xD7,0x75,0xDD, +0x77,0xFF,0xA5,0xBF,0xCF,0xFB,0xFF,0xFF, 0xBF,0xCF,0xFB,0xFD,0xFF,0xBF,0xF3,0xFE, +0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFD,0xAB, 0xFF,0xBF,0xBF,0xFF,0xFB,0xFF,0x7F,0xEF, +0xFF,0xBE,0xFB,0xEE,0xFB,0xBE,0xEF,0xBB, 0xEE,0xFB,0xBF,0xFF,0xB5,0xFF,0xD0,0xBC, +0xFD,0x2F,0x4B,0xF7,0xFF,0xFF,0x9F,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x9F, +0xFA,0x8F,0xFD,0xAB,0xFA,0xDA,0xBF,0xAF, 0xB3,0xFD,0xFF,0xBF,0xFB,0xFE,0xFF,0xBF, +0xEF,0xFB,0xFE,0xF7,0xBF,0xFF,0x9F,0xFF, 0x77,0xF7,0xBD,0xFD,0x77,0xDF,0xFF,0x7E, +0xDF,0xED,0xBB,0xFE,0xFF,0xBE,0xEF,0xFB, 0xFE,0xFF,0xFA,0x3F,0xFF,0xBE,0x6F,0x8F, +0xE6,0xF9,0xFE,0x7F,0x9F,0xC7,0xFE,0x7F, 0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7,0xFB, +0x7F,0xFF,0x7F,0xCF,0xFF,0xFD,0xFF,0xFF, 0xDF,0xFB,0xAF,0xBF,0xEF,0xFF,0xFE,0xFF, +0x9F,0xEF,0xFB,0xFF,0xFC,0xFF,0xFB,0xFE, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xF7, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF5,0xFF,0xFF,0xFF,0x3F,0xDF,0xF7, +0xFF,0xFF,0x7F,0xEF,0xFE,0xFF,0xBF,0xFF, 0xFB,0xFF,0xFF,0xBF,0xEF,0xFF,0xB3,0x7F, +0xFF,0x7B,0x5E,0xF7,0xFD,0xFF,0x7B,0x7F, 0xF7,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F, +0xDF,0xF7,0xFF,0x17,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xDD,0xF6,0xFC,0xBF,0xCB,0xF2, +0xBC,0xBF,0x2F,0xCB,0xF2,0xFC,0xBF,0xFE, 0x8F,0xFF,0xFA,0x7E,0xBF,0xA7,0xEB,0xDA, +0xFC,0xBF,0xAF,0x7A,0xFE,0xBF,0xAF,0xEA, 0xFA,0xFE,0xBF,0xAF,0xF4,0xDF,0xFE,0xFF, +0xF3,0x3C,0x7F,0x3E,0xFF,0xCF,0xF8,0xBF, 0x8F,0xE3,0xF8,0xFE,0x3F,0x8F,0xE7,0xE8, +0xFF,0xFC,0x9F,0xFF,0xFF,0xCF,0xEB,0xB3, 0xE7,0xFB,0x7B,0xF3,0xFE,0xFF,0xCF,0xDB, +0xFB,0xFB,0xBF,0x6F,0x6F,0xDF,0xEC,0x7F, 0xFF,0xFF,0xF7,0xFD,0xFD,0xFF,0xFF,0xFF, +0xFF,0xB2,0xBF,0xFF,0xDE,0xFD,0xBD,0xEF, 0xFB,0xF6,0xDF,0xEA,0xE7,0xDB,0xFE,0xBB, +0xFF,0xEB,0xFB,0xBF,0x9F,0x8F,0xE8,0xFE, 0x3F,0x8F,0xA3,0xF8,0xFE,0x3F,0x8F,0xFF, +0xF8,0x7E,0xFD,0xFD,0x7F,0xFF,0xFB,0xCD, 0xFF,0xFD,0xFF,0x5F,0xEF,0xFD,0xFF,0xFF, +0xDF,0xF7,0xFD,0xFF,0xBE,0x90,0xFF,0xFF, 0xEE,0xFF,0x3F,0xBF,0xF3,0xBB,0xFE,0xB7, +0xAB,0xFA,0xFE,0xAF,0xAD,0xEA,0xFA,0xDE, 0xAB,0xFF,0x63,0xFF,0xFE,0xF2,0xFF,0xB3, +0xFF,0xDF,0xEE,0x7D,0xFF,0x03,0xF1,0xF4, 0x3F,0x1F,0xC3,0xF1,0xEC,0x7F,0xFE,0x6F, +0xFF,0xFB,0xFB,0xFF,0x9F,0xFF,0xBF,0xFF, 0x7B,0x5F,0xFD,0xFF,0xDF,0xF7,0xFD,0xFD, +0x7F,0x7F,0xDF,0xFE,0xCF,0xFB,0xFF,0xFF, 0xAF,0xFB,0xFF,0x1F,0xEF,0xA5,0xFD,0xBF, +0xDF,0xFB,0x7D,0xFF,0xBF,0xDF,0xFB,0xFF, 0xFD,0x3B,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, +0xAF,0xF3,0xFF,0xFB,0x7F,0xBF,0xD7,0xFB, 0xBF,0x7F,0xBB,0xF7,0xFF,0xF8,0x7F,0xFF, +0xFA,0x5F,0xD7,0xFF,0xDF,0x7F,0xEF,0xFF, 0xFF,0x7F,0xDB,0xF7,0xFD,0xFF,0x7F,0xDF, +0xB7,0xFB,0xEC,0xFF,0xFF,0xF7,0xBF,0xEF, 0xFD,0xFC,0xFB,0xFF,0xEF,0xF0,0xFE,0x3F, +0x8F,0xE3,0xF8,0xFE,0x3F,0x8F,0xEF,0x8D, 0xFF,0xFF,0xEF,0x7F,0xBF,0xFF,0xFB,0xFF, +0xDB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xEF,0xD8,0xFF,0x2E,0x7F, +0xBE,0xEF,0xFE,0x6E,0xFF,0xBF,0xF9,0xFF, 0xFF,0xF3,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFC,0x66,0xBE,0x47,0xF3,0x7F,0xDF,0xFE, 0x87,0x9F,0xFF,0xFF,0xFF,0xFF,0xE7,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xD6,0x6F,0x7C, 0xFB,0x4F,0xD2,0xFF,0xFD,0x2B,0xFE,0xFF, +0xFF,0xFD,0x5F,0xD7,0xD5,0xF5,0x7D,0xFF, 0xFF,0xFF,0xBF,0x9B,0xFF,0xFF,0xDF,0xB7, +0xFF,0xFF,0xDF,0xFF,0x3F,0xCF,0xFE,0x7F, 0xBF,0xEF,0xFB,0xFC,0xFF,0x3F,0xFF,0xD9, +0xBF,0xFE,0x97,0xEC,0x8F,0xB7,0xFE,0x9B, 0x7D,0xFD,0xB7,0xDD,0x77,0x1D,0xC7,0x71, +0xDD,0x77,0x5D,0xD7,0xF3,0x6F,0xFD,0x3F, 0x73,0xDD,0xAF,0xFD,0x7A,0xFF,0xFF,0xAF, +0xFE,0xFD,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF, 0xEF,0x66,0x7F,0xFF,0xFF,0xBF,0xBF,0xFF, +0xFB,0xFF,0xF7,0xDF,0xFD,0xFB,0x7D,0xDF, 0xB7,0xCD,0xF3,0x7C,0x5F,0x3F,0x91,0x3F, +0xFF,0x3D,0xEF,0x7B,0xFF,0xFC,0xFF,0xCA, 0xEF,0xFE,0xFF,0xBD,0xEF,0xFB,0x1E,0xE7, +0xBB,0xEC,0x7F,0xB3,0xFF,0xFD,0x9F,0xFF, 0xFF,0xFE,0xFF,0xFF,0x7F,0xBF,0xFB,0xFE, +0xFF,0xBF,0xEF,0xFB,0xEE,0xFB,0xBF,0xDF, 0x67,0xFF,0xFF,0xBF,0xEF,0xDB,0xFF,0xBC, +0xFE,0x7F,0xFB,0xFF,0x9F,0xEF,0xF9,0xFE, 0x7F,0x9F,0xE7,0xF9,0xFE,0x87,0xFF,0xEE, +0xFB,0xBE,0xE5,0xBF,0xEF,0xF9,0xD7,0x65, 0xF7,0xDD,0xE7,0x7D,0xDF,0x77,0x5D,0xD7, +0x7F,0xF8,0x9B,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xFF,0xBF,0xEF,0xFB,0xFF,0x7F,0xCF, +0xF3,0xFC,0xFF,0xBF,0xEF,0xFF,0xDB,0x3F, 0xEF,0xFB,0xFE,0xFF,0xDF,0xFF,0xFE,0xFB, +0xBB,0xEF,0xBF,0xEF,0xBB,0xEE,0xFB,0xBE, 0xEF,0xBB,0xFF,0xFC,0x7F,0xFD,0x3B,0x5B, +0xD6,0xE5,0xFD,0x4F,0xC3,0xFB,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFF, +0xB4,0xFF,0xFA,0xBC,0x8F,0xB2,0xE9,0xD2, 0x2E,0xCF,0xFB,0xFF,0xBF,0xEF,0xFB,0xFE, +0xFF,0xBF,0xEF,0xFB,0xFF,0xEC,0xFF,0xFD, 0xFD,0x7F,0xDF,0xF7,0xE4,0xDF,0x5F,0xFF, +0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xC3,0xFF,0xEF,0xE6,0xF8,0xFE, +0x3F,0x8B,0x83,0xF9,0xFE,0x7F,0xE7,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x17, +0xFD,0xFF,0xFF,0xFF,0x7F,0x5F,0xF7,0x2C, 0xFF,0xFF,0xFF,0xFE,0x7F,0xFF,0xE7,0xF9, +0xFE,0x7F,0x9F,0xFE,0x2F,0xFF,0xFF,0xEF, 0xFF,0xFE,0xBF,0xEF,0xAD,0xFF,0xFF,0x7F, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFE,0xDF,0xFF,0xDF,0xFF,0xFD,0xFD,0x7F, +0xDF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFA,0x3F,0xFE, +0xF7,0xFD,0xEF,0x7A,0xFF,0xB1,0xBD,0xFF, 0x7F,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7,0xFD, +0xFF,0x7F,0xF3,0x27,0xFF,0xDF,0xFF,0xDD, 0xFF,0xFC,0x9B,0xFF,0xCB,0xFC,0xBF,0x2F, +0xCB,0xF2,0xFC,0xBF,0x2F,0xC9,0xFF,0xDE, 0xFF,0xDF,0xAF,0xEB,0xDA,0xFE,0xBB,0xAF, +0xEB,0xF8,0xF7,0xAF,0xE8,0xFA,0xFE,0xBF, 0xAF,0xEB,0xF2,0xFF,0xFD,0xFF,0xFF,0xEF, +0xBD,0xD7,0xBF,0xFF,0xFF,0xDE,0x8F,0xB8, 0xDE,0x37,0x8D,0xA3,0x78,0xDA,0x3F,0x8F, +0xFF,0xA1,0xFF,0xFF,0xFB,0xFB,0xFF,0xFF, 0xFF,0xFF,0xA7,0xBD,0xFB,0x76,0xFD,0xBF, +0xEF,0xDB,0xFE,0xBB,0xBF,0xFE,0x27,0x7F, 0xFF,0xFE,0xFE,0xFD,0xF5,0xFF,0xEF,0xF5, +0xDF,0x1F,0xE7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0xFF,0xCD,0xFD,0xAE,0xFF,0xFA, +0x3E,0x3F,0xAB,0xFD,0xF8,0x7E,0x8F,0xE3, 0xF8,0xFE,0x3E,0x8F,0xE3,0xF8,0xFF,0xFE, +0x1F,0xEF,0xDF,0xBF,0xFE,0xDE,0xDF,0xD9, 0xFF,0xDF,0xBC,0xFF,0xFF,0x7F,0xFF,0xEF, +0xFD,0x7F,0xDF,0xF7,0xF9,0x3F,0xFE,0xFF, 0xFF,0x6F,0xFE,0xDE,0xBF,0xF7,0xED,0xEA, +0xFD,0x8F,0x83,0xF8,0xEA,0x3F,0x8F,0xEF, 0xFF,0xF4,0x7F,0xFF,0xEF,0xEF,0x7B,0xF3, +0xF1,0x5F,0xFF,0xFF,0xF1,0x3B,0x7F,0xDF, 0xF7,0xFD,0xFF,0xFF,0xFF,0xFF,0xE0,0xFF, +0xFF,0xFF,0xF7,0xFF,0x6F,0xFF,0x7F,0xFF, 0xFF,0xF7,0xDE,0xF7,0xBF,0xEF,0xFB,0xF7, +0xFD,0xFF,0xFF,0xF5,0xFA,0xFF,0xFF,0xFB, 0xE7,0xFF,0xF3,0xF8,0x7F,0xF3,0xDF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F,0xEF, 0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, +0xFF,0x7F,0xFF,0x9F,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xCF,0xFF,0x37,0xFF,0xFF, +0x7F,0xDF,0x77,0x5D,0xE7,0xFC,0xFF,0xBF, 0xF7,0xF5,0xFB,0xFF,0xFF,0xD7,0xF5,0xFB, +0xFF,0xFF,0x45,0xFD,0x7F,0xEA,0xFD,0xBE, 0xBF,0xDF,0xF7,0xFF,0xFF,0xDB,0xFB,0xFE, +0xFF,0xBF,0xEF,0xFF,0xFF,0xFF,0xFB,0x5F, 0x7F,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFE,0xFF,0xEF,0xFD,0xFF,0x7F,0xDF, 0xFF,0xEF,0xFB,0xF8,0x0F,0xF3,0xFF,0xF9, +0x2E,0xFB,0xFE,0xFC,0xF3,0xEF,0xFF,0xFF, 0xBF,0xFF,0xFB,0xE7,0xFF,0xFE,0x7E,0xFF, +0xC0,0x6B,0xCF,0xFF,0x34,0xDF,0xF1,0xFD, 0xFF,0xEF,0xFF,0xFF,0xFF,0xDF,0xF7,0xFD, +0xCF,0x7F,0x9C,0xFD,0xFD,0x6C,0xF7,0xFF, 0xF6,0xFD,0xEB,0x2B,0x9F,0xFF,0xFC,0xFE, +0x7E,0xFF,0xFF,0xFF,0xFF,0xD7,0xF3,0xF7, 0xFF,0xFB,0xE1,0xBF,0xFF,0xEB,0x7A,0xDE, +0xD7,0xFB,0xFF,0xF9,0xFE,0xFF,0xFF,0xF3, 0xDE,0x7F,0xFD,0xE7,0x7F,0xFF,0xFD,0xBB, +0xFF,0xFF,0x7E,0xCC,0xF6,0xAF,0x5F,0x7F, 0xFE,0xF4,0x7D,0xF7,0xFD,0xBB,0x6E,0xDB, +0xB7,0xFF,0xF7,0xDF,0x66,0xFF,0xFF,0xF7, 0x3D,0xCF,0xDE,0xBD,0xFF,0xFF,0xDE,0xDB, +0x8D,0xF7,0x7E,0xDF,0xB7,0xEF,0x7F,0xFF, 0xF6,0x87,0xFF,0xFF,0xEF,0xFE,0xDE,0xBF, +0xFF,0xFF,0xFF,0xBB,0xEF,0xFD,0xFF,0x7B, 0xDE,0xF7,0x3F,0xFF,0xBF,0xFB,0xDB,0xFF, +0xF2,0xB6,0xFD,0xBD,0x7F,0xE7,0xFF,0xFF, 0xFF,0x6F,0xF7,0xFF,0xFF,0xFF,0xFE,0x77, +0xFF,0xBF,0xF8,0xAF,0xFF,0xDF,0xBF,0xFF, 0xBF,0x7F,0xFB,0xFF,0xFF,0xFF,0xDB,0xFE, +0xFF,0xBF,0xFF,0xFA,0xFF,0xFD,0xFF,0xF6, 0x7F,0xFF,0x9F,0xFF,0xFF,0x3F,0xEF,0xF8, +0xEE,0x7E,0x9F,0xBA,0xFE,0xBF,0x8F,0xEF, 0xFE,0xFE,0xF9,0xFF,0xFA,0x7F,0xFE,0x7E, +0xBF,0xAF,0xFB,0x96,0xFD,0x9F,0xEF,0x5E, 0x65,0xBE,0xEF,0x5B,0xB6,0xFF,0xBE,0xE3, +0xFF,0xB5,0xBF,0xFF,0xFD,0xFF,0x7F,0xFF, 0xEF,0xDF,0xFE,0xFF,0xBF,0xFB,0xFE,0xFF, +0xBF,0xCF,0xFF,0xFF,0xFF,0xFD,0x9B,0xFF, 0xFE,0xFB,0xFE,0xDF,0xFF,0x7F,0xFF,0xF7, +0xFE,0xFF,0xDF,0xFB,0xFB,0xFE,0xFF,0xFF, 0xFF,0xFF,0xFF,0xB7,0xFE,0xFA,0xFF,0xAB, +0xEF,0xFF,0xFD,0xB5,0x7B,0x7F,0xFB,0xF7, 0xFD,0xFF,0xFF,0xDD,0xFF,0xEF,0x8F,0xFF, +0x2F,0xFF,0xFB,0x7C,0xFF,0x3F,0xDF,0x73, 0xEB,0xFE,0x3F,0xFF,0xEF,0xFB,0xFE,0xFF, +0xEF,0xFD,0xFF,0xBF,0xFD,0x0F,0xFF,0xFF, 0xFF,0xF5,0xF9,0xFF,0x7F,0xD7,0xFD,0xFF, +0xDF,0xFF,0xF7,0xFB,0xFF,0x7F,0xBF,0xFF, 0xFF,0xF0,0x9F,0xFF,0xFE,0x7F,0x8B,0xE3, +0xF9,0xDE,0x27,0x9B,0xE6,0xBE,0x7F,0x9B, 0xC3,0xF8,0xDE,0x7F,0x9D,0xE7,0xFE,0x7F, +0xFF,0xFF,0x5F,0xD7,0xFF,0xFF,0xFF,0x4F, 0xFB,0xFF,0xFF,0x7F,0xFF,0xAF,0xFF,0x9F, +0x7F,0xFB,0xFF,0xE8,0xFF,0xFF,0xFE,0xBF, 0xAF,0xFF,0xFF,0xFE,0xBF,0xEF,0xF7,0xFF, +0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF, 0xFC,0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xFF, +0xFD,0x3F,0xCF,0xFF,0xFF,0xFF,0xFF,0xF7, 0xFF,0xFD,0x7F,0xFF,0xFF,0x93,0xFF,0xFF, +0x7A,0xDF,0xF7,0xFF,0xFF,0x7B,0x7F,0xB7, 0xEF,0xFF,0xFF,0xFD,0xBF,0xFD,0xFB,0xFF, +0xF7,0xFF,0xD7,0xFF,0xFF,0xFF,0xFC,0x9F, 0x6F,0xCB,0xFF,0xF4,0xBB,0xDF,0xD6,0xFD, +0xBF,0x2F,0xD3,0xF7,0xFF,0xDF,0xFF,0xCF, 0xFF,0xFA,0xBE,0xBD,0xAF,0x6A,0xDA,0xBE, +0xBB,0xAB,0x3A,0xBE,0x2D,0xAE,0xEB,0xDA, 0xF6,0x3F,0xAD,0xF5,0xDD,0xFF,0xCF,0xF1, +0xFF,0xF9,0x7F,0xFF,0x73,0xFE,0xFF,0xCF, 0xC3,0xF4,0xF7,0x2F,0xF3,0xFF,0xFC,0xFF, +0x7C,0x1F,0xFF,0x3F,0x4F,0xFF,0x7E,0xFF, 0xEF,0xBD,0xF6,0xFE,0xFF,0x2B,0xEF,0xDC, +0xFB,0xFD,0xFF,0xFB,0xFF,0xEA,0x7B,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFB,0xF7,0xDF,0xFF, +0xE3,0x7D,0xFF,0xB7,0xFF,0xBF,0xFF,0xFF, 0xDF,0xFF,0xF8,0xFF,0xBF,0xFF,0xBF,0xEB, +0xE7,0xFA,0xFE,0x3D,0xBF,0xE9,0xFC,0xBF, 0xFF,0xFA,0xFB,0xFE,0xFF,0xFF,0xFF,0xD9, +0xFF,0xFF,0xFF,0xF6,0x7F,0xFF,0xF6,0x7D, 0xFF,0xDF,0xCF,0xFD,0xBF,0xFB,0xEF,0x7E, +0xFF,0x7F,0xFF,0xFF,0xD3,0xFF,0xFD,0xFB, 0xFF,0xFB,0xFF,0xFF,0xFF,0xEF,0xFF,0xBF, +0xFE,0xFF,0xF7,0xEF,0xFF,0xFF,0xFF,0xFB, 0xFF,0x87,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF, +0x7B,0xFE,0xFF,0xFE,0x3B,0xF7,0xF7,0xFF, 0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF, +0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xFF,0xF7, 0xFF,0xFF,0xAD,0xFF,0xFE,0xF7,0xFF,0xFF, +0x5F,0xFF,0xFF,0xDF,0xFF,0xFD,0xFF,0xF5, 0xFF,0xDF,0xFF,0xBD,0xFF,0xE9,0xFF,0xC7, +0xF3,0xFF,0xFF,0xF7,0xFF,0xF3,0xFF,0xF8, 0x3B,0xFF,0xFF,0x7B,0xDF,0xBF,0xFB,0xEF, +0xFB,0xFF,0xFB,0xF7,0xF7,0xBB,0xFF,0xFF, 0xFF,0xFF,0xFB,0xFF,0xFE,0x7F,0xF3,0x7F, +0x5E,0xB7,0xBF,0xFD,0x7F,0xFF,0xF9,0x7F, 0xFB,0xFF,0xEB,0xFD,0x7F,0x7F,0xFF,0xEF, +0xFB,0xE0,0x3F,0xFE,0xBF,0xBF,0xDF,0xFF, 0x7E,0xFF,0xF7,0xFF,0xFF,0xFE,0xBF,0xFF, +0xDB,0x78,0xFF,0xFF,0xFF,0xEE,0xA1,0xBF, 0xF5,0xDE,0xFB,0xF7,0xFF,0xFB,0xFF,0xFF, +0xFF,0xFF,0xFB,0xFF,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xEF,0xF0,0xFF,0xFF,0xFF,0xF3, +0xF7,0xFF,0xEF,0xFF,0xE7,0xCF,0xFF,0xFB, 0xFF,0xEF,0xFF,0xFF,0x9F,0x9F,0xEF,0xFC, +0x16,0xBF,0xFE,0xF3,0xE4,0xFF,0xFF,0xC6, 0xFF,0xE7,0xFF,0xFF,0xFD,0xFF,0xBF,0xFF, +0xFF,0x3F,0xFF,0xBF,0xD6,0xAF,0x7F,0xFE, 0x6B,0x7E,0x7F,0xFF,0xAF,0xFF,0xFF,0xBF, +0xFF,0x5F,0xFF,0xFE,0xFF,0xFF,0xFE,0xFF, 0xFF,0xBD,0xDB,0xFF,0xFE,0x5F,0xF2,0xFF, +0xFF,0x5F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xEF,0x7F,0xFF,0xFF,0xFF,0xFF,0xDE,0xBF, +0xFF,0xFF,0xEF,0xFB,0x77,0xFE,0xBD,0x7F, 0x5F,0xFF,0xFF,0xFF,0xDF,0x6F,0xED,0xFF, +0xFD,0xFF,0x7F,0xFD,0x6F,0xFF,0xFF,0x77, 0xDA,0xCF,0xFD,0x5F,0xFF,0xBF,0xFF,0xFF, +0xDF,0x7F,0xFF,0xFB,0xFF,0xFF,0xFF,0xFF, 0x66,0x7F,0xFF,0xFE,0xBF,0xE7,0xBF,0xFA, +0xFF,0xFE,0xFF,0xFF,0xFF,0xDF,0xFF,0x59, 0xEF,0xFF,0xEF,0xFB,0x7F,0x89,0xFF,0xFF, +0xE9,0xFF,0x6F,0xFF,0xF5,0xFF,0xFF,0xFF, 0xFF,0xFF,0x7F,0xF2,0xF7,0xFF,0xFF,0xEF, +0xF8,0x7F,0xFB,0xFF,0xFD,0xFF,0xFF,0xD9, 0xFF,0xEF,0xBB,0xFF,0xFF,0xFF,0xBF,0xEF, +0xDE,0xFF,0xFF,0x9F,0x7F,0xDF,0xFF,0xF7, 0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,0xAF, +0xFF,0xFF,0xF7,0x3F,0xEB,0x9F,0xFE,0x7F, 0x9E,0x7F,0x9F,0xFE,0x87,0xFF,0xED,0xDB, +0x56,0xFF,0xBF,0xAF,0x0B,0xD2,0xFF,0xEF, 0xDB,0x6E,0x7D,0xBD,0x6F,0xF8,0xFE,0x3F, +0xFA,0x5B,0xFF,0xFD,0xBF,0xEF,0xFF,0xBF, 0x6F,0xDB,0xE6,0xFF,0xFF,0x3F,0xFF,0xDF, +0xFE,0xFF,0xFF,0xFF,0xFF,0xDA,0x3F,0xFF, 0xFB,0xFE,0xFE,0xFF,0xFF,0xDF,0xF7,0xBD, +0xFF,0xFD,0xFF,0xFE,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFF,0xF1,0x5F,0xFD,0x9F,0xDF,0xFD, +0xFF,0xFD,0x7F,0xFF,0xFF,0xFF,0xFF,0x76, 0xFA,0xFF,0xFF,0x7F,0xE3,0xF8,0xFF,0xAE, +0xFF,0xFB,0x7E,0x9D,0x73,0xFF,0xFA,0x7F, 0xDF,0xFF,0xFF,0x7F,0xFF,0xFB,0xCD,0xFF, +0x7F,0xEF,0xFB,0xFF,0xFD,0xFF,0xF7,0x7F, 0x7F,0xEF,0xFF,0xED,0xFF,0xFF,0xFF,0xB5, +0xFF,0xBF,0xFF,0xBF,0xFD,0xEF,0xDB,0xF7, 0xFF,0x93,0xFF,0xEF,0xE2,0xF9,0xBE,0x7F, +0x8B,0xE7,0xF9,0xFE,0x6B,0xE7,0xF9,0xFE, 0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x47,0xFF, +0xFF,0xFD,0xFF,0x9F,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xF5,0xFF,0x9F,0xFF,0xF7,0xFE, +0xFF,0xBF,0xFE,0x6F,0xFF,0xFF,0xFB,0xFF, 0xFF,0xFF,0xAF,0xFF,0xFF,0xFF,0x7F,0xFB, +0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, 0xDF,0xFF,0xFF,0xF7,0xFF,0xFF,0xFF,0xDF, +0xFF,0xFF,0xFF,0x5F,0xFF,0xFF,0xFF,0xFF, 0x5F,0xFB,0xFE,0xFF,0xF8,0x37,0xFF,0xFF, +0xEF,0xFF,0x7F,0xFE,0xBF,0xFF,0xFF,0xFE, 0xBF,0xFF,0xFF,0x7F,0xFF,0xBF,0xFD,0xFF, +0x7F,0xFA,0x7F,0xFF,0xFF,0x6F,0xFF,0xFF, 0x7D,0xFF,0xCF,0xFF,0xFF,0xFF,0x4F,0xFF, +0xF2,0xFF,0xFF,0xFF,0xFF,0xFF,0xFA,0xBF, 0xFF,0xAE,0xEB,0xFA,0xFE,0xBB,0xAD,0xEB, +0xFA,0xF7,0xAF,0x6B,0xFA,0xF6,0xBF,0x25, 0xE9,0xF2,0x7F,0x45,0xFF,0xFF,0xFD,0xF7, +0xF7,0xBF,0xFF,0xDF,0xFF,0xFF,0xBF,0xFB, 0xFF,0xDF,0xF3,0xFF,0xF7,0x3F,0xCF,0xFF, +0xA1,0xFF,0xFF,0xBF,0xE7,0xFF,0xFF,0x7F, 0xFF,0x3D,0xFF,0xFF,0xFF,0xF7,0xFF,0x2F, +0xFF,0xFB,0xF5,0x7F,0xFE,0x57,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7, +0x3F,0xFF,0xFE,0xFF,0xFF,0xFF,0xFD,0xFE, 0xF7,0xEE,0xAF,0xFE,0xEE,0xE7,0xFA,0xFF, +0xFE,0x9D,0xF9,0x5E,0xFE,0xFF,0xEB,0xFF, 0xFF,0xDF,0xA7,0xFF,0xFF,0xFF,0xFC,0xDB, +0xFF,0xFF,0xFF,0x7E,0xFB,0xFF,0xFF,0xEF, 0xFB,0xFD,0xFF,0xDB,0xFF,0xFF,0xFF,0xEF, +0xFF,0xFF,0xFF,0xFD,0xBF,0xFE,0xBF,0xFF, 0x6F,0x7F,0xFF,0xF7,0xFF,0xFF,0xF9,0xFF, +0xF7,0xFF,0xBF,0xDE,0xF7,0xFF,0xFF,0xFF, 0xFA,0x7F,0xFD,0xBF,0x5F,0xFF,0xFF,0xBF, +0xFF,0xED,0xFF,0xF7,0xBF,0xFF,0xFF,0xEF, 0xFF,0xDF,0xFF,0xFF,0xFF,0xE6,0xFF,0xFB, +0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEB,0xFF, +0xFD,0xFF,0xF5,0xFF,0xF6,0x7F,0xDF,0xBD, 0xCF,0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF, +0xFF,0xF9,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3, 0xFF,0xEE,0xBF,0xFF,0x7D,0xEF,0xFE,0xFF, +0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFE, 0xFF,0xFF,0xFF,0xFF,0xE7,0xFF,0xB5,0xAE, +0xFF,0xFF,0xB6,0xFE,0xBF,0xFF,0xFF,0xBF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0x27,0xFF,0xEF,0xFE,0x7F,0xDF,0xFF, 0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFD,0xFF,0xF7,0xF9,0x9F,0xFF, 0x5F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F, +0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x0F,0xFF,0xE7,0xBF,0xFE, +0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFC,0xBF, 0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xC4, +0x6B,0xFF,0x29,0x1F,0xFB,0xAF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xEF,0x1B,0xFE,0xFF,0xFC, +0x6F,0xFF,0xFF,0xFD,0x6A,0xF7,0xD7,0xF5, 0xBF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFE,0xBF,0xFF,0xFF,0xFA,0xFF,0xFF,0xF7, 0xFB,0xDD,0xBF,0xFF,0xE7,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0x7F,0xFF, 0xFF,0xF5,0xFF,0xFF,0xF7,0xFD,0xB3,0xEF, +0xFD,0x7E,0x5D,0xFF,0xFD,0xFF,0xFF,0xFF, 0xFD,0x7F,0xD2,0xF5,0xFB,0x7E,0xCB,0xB7, +0xFF,0xFF,0xFF,0xC6,0xFF,0xFD,0xEE,0x63, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF6,0xFD,0x65, +0x5B,0xDF,0xFF,0xD5,0xFF,0xFF,0xFF,0xF6, 0xE7,0xBF,0xF7,0xA9,0xFF,0xFF,0xED,0xFF, +0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,0xFF,0xFF, 0xAF,0xFF,0xFF,0xFF,0xF8,0x1B,0xFF,0xE3, +0xD0,0xBF,0xFF,0xE1,0xFF,0xFF,0xFF,0xFF, 0xFF,0xD7,0xFF,0xFF,0xFF,0x5F,0xFF,0xFF, +0xFF,0xFF,0xAF,0xFF,0xDB,0x76,0xBF,0xFF, 0x7F,0xFF,0xBF,0xEF,0xFE,0xFF,0xBF,0xEF, +0xFB,0xFE,0xFF,0xFF,0xFF,0xBF,0xF2,0x7F, 0xFF,0x9F,0xFE,0xBD,0xFE,0x7F,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF7,0x3F,0xEC,0x7F,0xF6,0x95,0xBB, +0xEF,0xF8,0xFE,0xFC,0xBF,0x2F,0xDA,0xFC, 0xBF,0x2F,0xCB,0xF2,0xFC,0xBF,0xEF,0xFF, +0xA9,0xBF,0xCF,0xFB,0xFF,0xFF,0xFF,0xFE, 0xDD,0xB7,0x6D,0xF6,0xD9,0xB6,0x6D,0x9B, +0x76,0xD9,0xBF,0xFB,0xFD,0xA3,0xFF,0xBF, 0xEF,0xFF,0xEF,0xFF,0xFF,0xFF,0x7F,0xDF, +0xFD,0xEF,0x7B,0xDE,0xF7,0xFD,0xEF,0x7F, 0xFF,0xFF,0x05,0xFF,0xFA,0xFE,0x7F,0xEF, +0xE3,0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xFF, 0xFF,0x5F,0xFF,0xFF,0xFD,0x7F,0xFB,0xAF, +0xFF,0x63,0xC8,0xFF,0xBF,0xEF,0xFF,0xFF, 0xFA,0x7F,0xFF,0xFF,0xFF,0xFE,0x9F,0xF7, +0xFF,0xFA,0xBF,0xFE,0x9F,0xFB,0x7F,0xFF, 0xFF,0xEF,0xD7,0xFF,0xFF,0xF5,0xFF,0xFF, +0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xBF,0xFF, 0xF9,0xBF,0xFF,0xBE,0x27,0x9F,0xE7,0xF9, +0xFE,0x7F,0x8B,0xE7,0xFE,0x7F,0x9F,0xE2, 0xF9,0xFE,0x7F,0x9F,0xE7,0xF1,0x7F,0xFF, +0xFF,0xFF,0xFB,0xFE,0xFF,0xFF,0xFF,0xD7, 0xFF,0xFF,0xFF,0xFF,0xF5,0xFF,0xFF,0xFF, +0xD7,0xFF,0xFA,0xFF,0xFE,0xFF,0xFF,0xFF, 0xFD,0xFF,0xFF,0xFF,0xAF,0xF7,0xFF,0xFF, +0xFF,0xEB,0xFF,0xFF,0xFF,0xAF,0xFF,0xC4, 0xFF,0xF7,0xFF,0xFF,0xEF,0xFF,0xFF,0xFF, +0xFF,0x5F,0xFF,0xFF,0xFF,0xFF,0xD7,0xFF, 0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,0xFB,0x7A, +0xDF,0xF7,0xFD,0xFF,0xFF,0xFE,0xBF,0xFF, 0xFF,0x7F,0xFF,0xAF,0xFF,0xFF,0xFF,0xF7, +0xEF,0xE3,0xFF,0xDD,0xD2,0xFF,0xDF,0xFF, 0xFF,0xF2,0xFC,0xBF,0xCB,0xF6,0xFD,0xBF, +0x2F,0xCB,0xFF,0x7F,0xDF,0xDE,0xAF,0xFF, 0xDA,0xEE,0xBF,0xAF,0xE9,0xFA,0xF4,0xBD, +0xAF,0x5A,0xAE,0xBB,0xAB,0x6B,0xDA,0xDE, 0xBF,0xAD,0xD7,0x5E,0xFF,0xFF,0xBF,0xFC, +0xFF,0xDF,0xFD,0xFF,0xFF,0xFF,0xFF,0xDF, 0xF7,0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFA, +0x1F,0xFF,0xFE,0xFB,0xEF,0xBF,0xFD,0xFF, 0xFD,0xBD,0x77,0xFF,0xFF,0xFF,0xFF,0x9D, +0xEF,0xFF,0xFF,0xFF,0xEF,0x7D,0xFF,0xFB, 0xFE,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEE, 0xBF,0xE4,0xFB,0xFF,0xFE,0x3F,0xFE,0xFF, +0xFF,0xFF,0xFF,0xAF,0xEA,0xFE,0xBF,0xAF, 0xEB,0xFA,0xFE,0xFF,0xFF,0xFF,0x55,0xF6, +0xFF,0xFE,0xF7,0xFF,0x7F,0xFF,0xEB,0xF7, 0x5F,0xC5,0xFD,0x7F,0x5F,0xD7,0xF5,0xFF, +0x6F,0xFB,0xFF,0x8A,0xFF,0xFF,0xFF,0xFF, 0xEB,0xFF,0xFF,0xFF,0xFF,0xFB,0xBF,0xBF, +0xEF,0xFB,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF, 0x77,0xDF,0xFB,0xFF,0xFD,0x7F,0xEF,0xFF, +0xFF,0xFF,0xBF,0x7F,0xFF,0xDF,0xBF,0xFF, 0xFB,0xFF,0xFF,0xFF,0xFE,0xEF,0xDF,0xFF, +0xFE,0xFF,0x9F,0xEF,0x7D,0xFF,0xF7,0xFF, 0x7F,0xFF,0xFF,0xDF,0xF7,0xFD,0xFF,0xEF, +0xDF,0xFF,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0xFB, +0xFD,0xFF,0xBF,0xDF,0xD1,0xFF,0xF8,0x3B, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0x7E,0xDB,0xFD,0xFF,0x77,0xDB,0xB7,0x7D, 0xBF,0xFB,0xFF,0xF8,0x7F,0xED,0x7B,0x5E, +0xFF,0xFE,0xFF,0xFF,0x4F,0xD7,0xFD,0x7F, 0xDF,0xD7,0xF5,0xFF,0x7F,0xFF,0xFF,0xFF, +0xF2,0x3F,0xFE,0xFF,0xBF,0xFF,0xFF,0xFF, 0xFF,0xBF,0xEF,0xFE,0xFF,0x3B,0xEE,0xFF, +0xFC,0xEF,0xFF,0xFF,0xFF,0x85,0xFF,0xFD, 0xFE,0xFF,0xF5,0xFF,0xFF,0xFE,0xFF,0xDF, +0xFB,0xFF,0x5F,0xBF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xA8,0xFF,0xFF,0x9F,0x9E,0xFF, +0xFF,0xFF,0x7F,0xF3,0xFF,0xFF,0xCF,0xFF, 0xF7,0xFD,0xFF,0x7F,0xFF,0xFF,0xFC,0x16, +0xBF,0xCF,0xA3,0xE5,0xEF,0x7F,0xFF,0xF3, 0xE4,0xFF,0xCF,0x93,0xFC,0xFF,0x3F,0xCF, +0xFF,0xFF,0xFF,0xD6,0x0F,0x7D,0xBF,0x6E, 0xFB,0xF4,0xFC,0xAF,0x6D,0xDB,0x77,0xB7, +0x6D,0xDB,0xF6,0xFD,0xBF,0xFF,0xFF,0xFF, 0xBF,0x9B,0xFA,0xDE,0xB7,0xB7,0xED,0xF9, +0x7E,0xB7,0xAC,0xEB,0xD6,0xB3,0xAD,0xEB, 0x7A,0xDF,0xFF,0xFF,0xFF,0xD8,0xBF,0xFF, +0xB7,0xED,0x9F,0x6F,0xDD,0xF7,0x68,0xDB, 0x37,0xB3,0x6C,0xDB,0x36,0xCD,0xB3,0x7F, +0xFF,0x7F,0xF5,0x6F,0xFD,0xEF,0x79,0x3D, 0xF7,0x93,0xE4,0x7A,0x9E,0xAD,0xEA,0x7A, +0x9E,0xF7,0xBD,0xEF,0xFF,0xFF,0xFF,0x76, 0x7F,0xFB,0xC6,0xFF,0xBB,0xEF,0xDA,0xFE, +0xFD,0xBF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xFF,0xFB,0xFF,0xA5,0xFF,0xFD,0xAB, +0x6F,0x78,0xDE,0x17,0x8F,0x79,0xDF,0xFD, 0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0xFF,0xFB, +0xFF,0xFB,0xFF,0xEF,0xFB,0xEF,0xFB,0xFE, 0xFF,0xBB,0xDA,0xF3,0xEF,0x3B,0xCE,0xF3, +0xBC,0xEF,0x3F,0xCF,0xDF,0xFF,0xB7,0xFF, 0xFF,0xFF,0xCF,0x73,0xFF,0xBF,0xEF,0xFF, +0xF3,0xFF,0x3F,0xCF,0xF3,0xFC,0xFF,0x3D, 0xCF,0x9F,0xFE,0x07,0xFF,0xAF,0xEB,0xFE, +0xFD,0xBF,0xEF,0xEB,0xFA,0xFF,0xAF,0xEB, 0xFA,0xFE,0xBF,0xAF,0xFB,0xFE,0x3F,0xFB, +0x9B,0xFF,0x7F,0xDF,0xFF,0xF3,0xFE,0xFF, 0xDE,0xF7,0xBF,0x7B,0xDE,0xF7,0xBD,0xEF, +0x7B,0xFE,0xFF,0xFF,0xDF,0x3F,0xFE,0xFF, 0xB7,0xFF,0xEF,0xF7,0xFF,0xBF,0xED,0xFE, +0xDF,0xB7,0xED,0xFB,0x7E,0xDF,0xFF,0xFF, 0xFF,0xFD,0x5F,0xEF,0xEB,0xFA,0xFE,0xF5, +0xBF,0x6F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xF8,0xFF,0xA8,0xFF, +0xFF,0xBF,0xEF,0xFB,0x6A,0xFB,0xB7,0xEF, 0xFB,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF, +0xEF,0xFB,0xFF,0xE0,0xFF,0xFF,0xFD,0x7F, 0x5C,0xD7,0x7D,0xDF,0xF3,0x5C,0xF5,0xCD, +0x73,0x5E,0xD7,0xB5,0xFD,0x7F,0xEF,0xFF, 0xDB,0xFF,0xFF,0xE2,0xF8,0xBE,0x2F,0x8F, +0xE7,0xF8,0xBE,0x6B,0xE2,0xF8,0xBE,0x2F, 0x8B,0xE2,0xF9,0xFE,0x7F,0xE7,0xFF,0xD7, +0xF5,0xFD,0x7F,0xFF,0xF7,0xF5,0xFD,0x7F, 0xD7,0xF5,0xFD,0x7F,0x5F,0xD7,0xF5,0xFF, +0xFF,0xFF,0x8F,0xFF,0xAF,0xEB,0xFA,0xFF, 0xFF,0xBF,0xEB,0xFA,0xFF,0x2F,0xEB,0xFA, +0xFE,0xBF,0xAF,0xEB,0xFF,0xFF,0xFE,0x5F, 0xFF,0x5F,0xFF,0xFF,0xFD,0xFF,0xFF,0xD7, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xBF,0xFE,0xB7,0xFD, +0xFF,0x7E,0xDF,0xF7,0xAD,0xFF,0x7F,0xF7, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F, +0xF6,0x7F,0xFF,0xFF,0xFF,0xDB,0xF6,0xFC, 0xAF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xEC,0xBF,0xFF, 0xAF,0xEB,0xFA,0xF6,0xAB,0x8F,0xEB,0xFA, +0xF7,0xA5,0xEB,0xFA,0xBE,0xBF,0xAF,0xEB, 0xFA,0xFF,0x6D,0xFF,0xFF,0x7F,0xDF,0x33, +0xDD,0xFF,0x7F,0xFE,0xF7,0xFC,0x7F,0xFB, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xA9, +0xFF,0xFD,0xFF,0xFF,0xFE,0xFF,0xFF,0xDF, 0xFF,0xFF,0xEF,0xEF,0xFD,0xFF,0x7F,0xFF, +0xFF,0xFF,0xFF,0xFE,0xA7,0xFF,0xFF,0xFF, 0x77,0xDF,0xF7,0xFD,0x9F,0x7F,0xFE,0x77, +0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xAF,0xBF,0xAF,0xFF,0xF9,0xBE,0xBF, +0x8F,0xFB,0xFE,0xFE,0xEF,0xFB,0xFE,0xFF, 0xBF,0xEF,0xFB,0xFF,0xFF,0xFD,0xDF,0x6F, +0xEF,0xFF,0x7F,0xFF,0xBF,0xBF,0xDF,0xFF, 0xFC,0xFF,0xDF,0xF7,0xFD,0xEF,0x7F,0xDF, +0xFF,0xFF,0xFF,0x3F,0xF6,0xFF,0xCF,0xFF, 0xDB,0xFB,0xF7,0xFF,0xEB,0x7A,0xFF,0xFF, +0xFF,0xBF,0xEF,0xFB,0xFF,0xFF,0xFF,0xFE, 0x6D,0xFD,0xFF,0x5F,0xFB,0xFF,0xFF,0xF7, +0xFF,0x5F,0xF5,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xF8,0xFF,0xFB,0xFF, +0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xE7,0xF6, 0xBF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xFF, +0xFF,0xC9,0xFF,0xFF,0xFF,0xBD,0xFF,0xBF, 0xAF,0xEF,0xEF,0x3F,0xD1,0xFC,0x7F,0xFB, +0xC7,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3,0xFF, 0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0x77,0xFF, +0xDF,0xB7,0xFD,0xF7,0xFD,0xF7,0xFF,0xFF, 0xFF,0xFF,0xFF,0x57,0xFF,0xF7,0xA5,0xFD, +0x3F,0xDF,0xBF,0xBF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xDF,0xFA,0xFD,0xFF,0xFF,0xFF,0xFE, +0x87,0xFF,0xE9,0xFF,0xFE,0xEF,0xBF,0xEF, 0xFE,0xFE,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFA,0x9F,0xFF,0x3F, 0xFF,0xFD,0xFD,0x57,0xDF,0xFD,0xF3,0xFF, +0xDF,0xFD,0xFF,0x5F,0xDF,0xF5,0xFD,0xFF, 0xFF,0xF9,0x8F,0xFF,0xFF,0xFF,0xEE,0x7F, +0xFF,0xFF,0xBF,0x5E,0xFE,0xEC,0xFB,0x3F, 0x7F,0x9F,0xEF,0xF9,0xFF,0xFF,0xCD,0x6B, +0xFF,0xFF,0xFF,0xC5,0xF3,0xFC,0xFA,0x38, 0xFF,0xAF,0x3F,0xEE,0x7F,0x9F,0xFF,0xD9, +0xFF,0xFF,0xFD,0x7A,0xF7,0xFF,0xF3,0xFF, 0xAF,0x6F,0xDB,0xF2,0xB9,0xE9,0xFB,0xFF, +0xFF,0xFF,0xFE,0xFF,0xFF,0xEF,0xFF,0xFB, 0xC5,0xBF,0xFF,0xEF,0xFF,0x5E,0xB7,0xAD, +0xCD,0x79,0x7C,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0x93,0xFF,0xEF, +0xEA,0xFE,0xBF,0xEF,0x5B,0xD2,0xCD,0xF5, 0x6D,0x77,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF, +0xFF,0xFF,0x66,0xFF,0xD5,0x65,0x7D,0x5F, 0x75,0x9D,0x65,0x7F,0xD6,0xFB,0x4F,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF6,0xC7, 0xFF,0xBF,0xEF,0xFA,0xFE,0xFF,0xBF,0xEB, +0xFF,0xDF,0xFF,0x7E,0xFF,0xFF,0xEF,0xFD, 0x7E,0xD7,0xFF,0x78,0xDF,0xFF,0x5F,0xDF, +0xF5,0xBF,0x7F,0xDF,0xC5,0xFF,0x3F,0xF6, 0x7E,0xFF,0x0F,0xEF,0xF2,0x3E,0xBF,0xFF, +0xFB,0x3F,0xFF,0xFB,0x7F,0xFF,0xB3,0xFE, 0xFB,0xF6,0xFD,0xFF,0xDA,0xF7,0xFD,0xFF, +0x7F,0xDF,0xF7,0xBF,0xFF,0xFA,0x7F,0xFF, 0xFF,0xFF,0xFF,0x9F,0xFF,0xF3,0xDC,0xF9, +0xBF,0xCE,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7, 0xFF,0xFF,0xE2,0x7F,0xFE,0xFF,0xBF,0xEF, +0xEB,0xFA,0xFF,0x9F,0x67,0x1E,0xFF,0x8F, 0xE7,0xF8,0xFE,0x7F,0x8F,0xEF,0xFF,0xBD, +0xBF,0xFF,0xFB,0xFF,0xFF,0xDF,0xF7,0xFF, 0xFC,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFD,0xB3,0xFF,0xFF,0xEF, 0xFF,0xFF,0xBF,0xED,0xFF,0xFB,0xEE,0xFE, +0xFF,0xFF,0xEF,0xFF,0xFE,0xFF,0xFF,0xFF, 0xFF,0xB5,0xFF,0xB7,0xFD,0xFD,0x6E,0xFF, +0xFF,0xFE,0xFD,0x2F,0xD8,0xFE,0xBF,0x8F, 0xEB,0xF9,0xFE,0x3F,0xFF,0xFA,0xCF,0xFF, +0xE7,0xD9,0xFA,0xBF,0xDF,0x77,0xFC,0xFB, 0x3F,0xAB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFE, +0xFF,0xFF,0xEE,0x1F,0xFF,0xDF,0xF7,0xFF, 0xFF,0xFF,0x5F,0x97,0x35,0xBF,0x5E,0xFE, +0xBF,0xEF,0xFF,0xF7,0xFD,0xFF,0xFF,0xFA, 0xBF,0xFF,0xBE,0x6F,0x9F,0xE7,0xF8,0xBE, +0x2F,0x8B,0x66,0x94,0x7D,0x9D,0xE7,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF1,0x7F,0xFF,0xFF, +0xFF,0xF7,0xF5,0xFD,0x7F,0x5F,0xFB,0xFD, 0x9E,0xFF,0xFB,0xFE,0xFF,0xFF,0xEF,0xFF, +0xFF,0xA0,0xFF,0xFF,0xFF,0xBF,0xEF,0xEB, 0xFA,0xFE,0xBF,0xB7,0xF7,0xF7,0xFF,0xFF, +0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xDD,0xFF, 0xFD,0xFF,0xFF,0xFF,0xD7,0xFF,0xFF,0xFF, +0x7F,0xF5,0xFF,0xFF,0xEF,0xFF,0xFF,0xFF, 0xBF,0xFF,0xFF,0xAB,0xFE,0xFB,0xFE,0xFF, +0xF7,0xAF,0xFF,0xFF,0xDE,0xF7,0xEB,0x5F, 0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF,0xFF, +0xB3,0xFF,0xC9,0xFE,0xFF,0xFF,0xFF,0xFF, 0xD6,0xFF,0xFF,0xCB,0xFF,0xFF,0xDF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFC,0x8F,0xFF,0xBA, 0xBE,0xBF,0xAF,0xEB,0x78,0xFE,0xB7,0xAD, +0x3A,0xFE,0xB7,0xAF,0xEB,0x7A,0xFE,0xBF, 0xAF,0xFF,0x9F,0xFF,0xFF,0xDF,0xFC,0xFF, +0xFF,0xFE,0xC3,0xFE,0xFF,0xFF,0x33,0xFC, 0xFF,0xBF,0xDF,0xF3,0xFF,0xFF,0xBB,0x9F, +0xFF,0xFF,0xFF,0xEB,0xDF,0xFF,0xFF,0xAF, 0xF7,0x6F,0xF9,0xBF,0xEF,0xFD,0xFF,0xFF, +0xFF,0xFF,0xFF,0xE3,0x7F,0xFF,0xFF,0xFF, 0xFB,0xFF,0xFF,0xBF,0xFD,0xFB,0xF7,0xFF, +0xDF,0xF7,0xFF,0xFE,0xEF,0x5F,0xBD,0xFF, 0xFA,0xFF,0xF8,0xFF,0xBF,0xAF,0xFB,0xFE, +0xFE,0x3F,0xEF,0xE8,0xFF,0xDF,0xF3,0xFD, 0xFF,0xFF,0xFF,0xFF,0xFF,0xED,0xFF,0xFB, +0xFD,0xFF,0xAF,0xFF,0xFF,0xFE,0xFE,0xBF, 0xDB,0xFF,0xFF,0xFF,0xBF,0xFF,0xDF,0xFF, +0xFD,0xFF,0xCB,0xFF,0xFF,0xFF,0xFF,0xFF, 0xBF,0x6F,0xFF,0x7F,0xB7,0xB3,0xFF,0xFF, +0xDF,0xFF,0xFB,0xEF,0xFF,0xFF,0xFF,0x07, 0xFF,0xFB,0xFF,0xFF,0xFF,0xED,0xFF,0xF5, +0x7C,0xFF,0x7F,0xFE,0xFF,0xFF,0xEF,0xCF, 0xFF,0xFB,0xFF,0xFF,0x2F,0xFF,0xFF,0xFF, +0xFF,0xF3,0xFF,0xFB,0xFF,0xFE,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF, +0xFD,0x1B,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7C,0xFF,0xFF,0xFF,0xFF, +0xEF,0xFF,0xFF,0xFF,0xFF,0xFB,0xBF,0x7F, 0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xDB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, 0xFF,0xFF,0xF0,0x7F,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xDF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xBF,0xFE, +0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xEF,0xFE,0xFF,0xBF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xEF,0xFA,0xB5,0xFF,0xFF,0xFF, 0xF7,0xF7,0xFF,0xFF,0xFF,0xFF,0xDF,0xFB, +0xFC,0xFF,0xFF,0xFE,0xFF,0x7F,0xDF,0xBF, 0xFF,0xCB,0xBF,0xF9,0xFE,0x7F,0x9F,0xE7, +0xF9,0xFE,0x7F,0x97,0xE1,0xFE,0x79,0x9F, 0xE7,0xFD,0xFE,0x7F,0xDF,0xFE,0x37,0xFF, +0xFB,0xDE,0xDE,0xBD,0xEF,0xF3,0xFE,0xFB, 0xAF,0xEB,0xFE,0xFF,0xFF,0xCF,0xFF,0xFE, +0xFF,0xBF,0xFF,0x8F,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xE7,0xF9,0x5E,0x7F,0xEF,0xFB, +0xDA,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFD, 0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDF, +0xFF,0xFF,0x7F,0xFF,0xFF,0xF7,0xFB,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFC,0x3F,0xFF,0xBF, +0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0x7B,0x7F, 0xBF,0xEF,0xFB,0xFE,0xFF,0xB5,0xEF,0xFB, +0xBF,0xFA,0x7F,0xFC,0xFF,0x3F,0xCF,0xF3, 0xFC,0xFF,0x3F,0xCF,0xBC,0xFF,0x3F,0xEF, +0xF3,0xFC,0xFE,0x3F,0xCF,0xFF,0xEE,0xEF, 0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0x6A,0xD7, +0xB7,0xFB,0xF8,0xFF,0xB7,0xEF,0xBA,0xFE, 0xFF,0xBF,0x7F,0xE9,0xFF,0xF9,0x7E,0x5F, +0x97,0xE5,0xF9,0xFE,0x7F,0xBF,0xF9,0x7E, 0x5F,0x9F,0xE5,0xFB,0xFE,0x5F,0xB7,0xFF, +0xA3,0xFF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0x5E,0xF7,0x7D,0xFF,0x77,0xDF, +0xF7,0xFD,0xFF,0x7F,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xDF,0xFB,0x7F, +0xFF,0xFF,0xEF,0xFF,0xFE,0xFB,0xFF,0xFF, 0xBF,0xFE,0x8F,0xFF,0xDF,0xF7,0xFD,0xFD, +0x7F,0xDF,0xF7,0xFD,0x3E,0xDF,0xF5,0xBD, 0xFF,0x7F,0xDF,0xF7,0xFD,0xF7,0xFF,0x9F, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFD,0xFF,0xBE,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFD,0x3F,0xFF,0xDF,0xF7, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0xCF, +0x77,0xFC,0xFF,0x5F,0xDF,0xF7,0xFD,0xFF, 0xF4,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFD,0xFF,0xFF,0xFF,0xEE,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xED,0xFB,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xE9,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFB,0xFF,0xFF,0xFF,0xD3,0xFF,0xFF, +0xBF,0x3F,0xFB,0xFF,0xFF,0xFF,0xFB,0xF3, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xF7, 0xFF,0xFF,0xFF,0xFF,0x17,0xFF,0xFF,0xFF, +0xDF,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF, 0xDF,0xDF,0xFF,0xFD,0xFF,0xFF,0xDF,0xF7, +0xFF,0x4F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFD, +0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0x9F,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF, 0xFF,0xFF,0x7A,0x3F,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF2, +0x7F,0xFF,0xFB,0xFE,0xFF,0xBF,0xEF,0xF8, 0xFE,0xFF,0xBF,0xFB,0xFE,0xFF,0x8F,0xEC, +0xFB,0xFE,0xFF,0xBF,0xF8,0xF7,0xFE,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFD,0xBF,0xCF,0xEC, +0xFF,0x3F,0xEF,0xDB,0xF8,0xFF,0xBF,0xCF, 0xFF,0xF9,0xFF,0xFF,0xBF,0xFF,0xFB,0xFF, +0xFF,0xFF,0xEF,0xFB,0xDF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xBB,0xFF, +0xEF,0xFB,0xFE,0xEF,0xBF,0xEE,0xEB,0xFB, 0xFE,0xFF,0xEF,0xFE,0xEE,0xBF,0xFE,0xEB, +0xFF,0xEF,0xFF,0x17,0xFF,0x7E,0xEB,0xBB, 0xFE,0xBF,0xBE,0xFB,0xEF,0x5B,0xF7,0xBD, +0xFB,0xCF,0xBF,0xBF,0xBB,0xFB,0x7E,0xCC, 0xEF,0xFF + +}; diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/dabusb.c linux-2.2.17/drivers/usb/dabusb.c --- linux-2.2.17-orig/drivers/usb/dabusb.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/dabusb.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,846 @@ +/*****************************************************************************/ + +/* + * dabusb.c -- dab usb driver. + * + * Copyright (C) 1999 Deti Fliegl (deti@fliegl.de) + * + * 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. + * + * + * + * $Id: dabusb.c,v 1.45 2000/01/31 10:23:44 fliegl Exp $ + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dabusb.h" +#include "dabfirmware.h" + +/* --------------------------------------------------------------------- */ + +#define NRDABUSB 4 + +/*-------------------------------------------------------------------*/ + +static dabusb_t dabusb[NRDABUSB]; +static int buffers = 256; + +/*-------------------------------------------------------------------*/ + +static int dabusb_add_buf_tail (pdabusb_t s, struct list_head *dst, struct list_head *src) +{ + unsigned long flags; + struct list_head *tmp; + int ret = 0; + + spin_lock_irqsave (&s->lock, flags); + + if (list_empty (src)) { + // no elements in source buffer + ret = -1; + goto err; + } + tmp = src->next; + list_del (tmp); + list_add_tail (tmp, dst); + + err: spin_unlock_irqrestore (&s->lock, flags); + return ret; +} +/*-------------------------------------------------------------------*/ +#ifdef DEBUG +static void dump_urb (purb_t purb) +{ + dbg("urb :%p", purb); + dbg("next :%p", purb->next); + dbg("dev :%p", purb->dev); + dbg("pipe :%08X", purb->pipe); + dbg("status :%d", purb->status); + dbg("transfer_flags :%08X", purb->transfer_flags); + dbg("transfer_buffer :%p", purb->transfer_buffer); + dbg("transfer_buffer_length:%d", purb->transfer_buffer_length); + dbg("actual_length :%d", purb->actual_length); + dbg("setup_packet :%p", purb->setup_packet); + dbg("start_frame :%d", purb->start_frame); + dbg("number_of_packets :%d", purb->number_of_packets); + dbg("interval :%d", purb->interval); + dbg("error_count :%d", purb->error_count); + dbg("context :%p", purb->context); + dbg("complete :%p", purb->complete); +} +#endif +/*-------------------------------------------------------------------*/ +static int dabusb_cancel_queue (pdabusb_t s, struct list_head *q) +{ + unsigned long flags; + struct list_head *p; + pbuff_t b; + + dbg("dabusb_cancel_queue"); + + spin_lock_irqsave (&s->lock, flags); + + for (p = q->next; p != q; p = p->next) { + b = list_entry (p, buff_t, buff_list); + +#ifdef DEBUG + dump_urb(b->purb); +#endif + usb_unlink_urb (b->purb); + } + spin_unlock_irqrestore (&s->lock, flags); + return 0; +} +/*-------------------------------------------------------------------*/ +static int dabusb_free_queue (struct list_head *q) +{ + struct list_head *tmp; + struct list_head *p; + pbuff_t b; + + dbg("dabusb_free_queue"); + for (p = q->next; p != q;) { + b = list_entry (p, buff_t, buff_list); + +#ifdef DEBUG + dump_urb(b->purb); +#endif + if (b->purb->transfer_buffer) + kfree (b->purb->transfer_buffer); + usb_free_urb(b->purb); + tmp = p->next; + list_del (p); + kfree (b); + p = tmp; + } + + return 0; +} +/*-------------------------------------------------------------------*/ +static int dabusb_free_buffers (pdabusb_t s) +{ + unsigned long flags; + dbg("dabusb_free_buffers"); + + spin_lock_irqsave(&s->lock, flags); + + dabusb_free_queue (&s->free_buff_list); + dabusb_free_queue (&s->rec_buff_list); + + spin_unlock_irqrestore(&s->lock, flags); + + s->got_mem = 0; + return 0; +} +/*-------------------------------------------------------------------*/ +static void dabusb_iso_complete (purb_t purb) +{ + pbuff_t b = purb->context; + pdabusb_t s = b->s; + int i; + int len; + int dst = 0; + void *buf = purb->transfer_buffer; + + dbg("dabusb_iso_complete"); + + // process if URB was not killed + if (purb->status != -ENOENT) { + unsigned int pipe = usb_rcvisocpipe (purb->dev, _DABUSB_ISOPIPE); + int pipesize = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe)); + for (i = 0; i < purb->number_of_packets; i++) + if (!purb->iso_frame_desc[i].status) { + len = purb->iso_frame_desc[i].actual_length; + if (len <= pipesize) { + memcpy (buf + dst, buf + purb->iso_frame_desc[i].offset, len); + dst += len; + } + else + err("dabusb_iso_complete: invalid len %d", len); + } + else + warn("dabusb_iso_complete: corrupted packet status: %d", purb->iso_frame_desc[i].status); + if (dst != purb->actual_length) + err("dst!=purb->actual_length:%d!=%d", dst, purb->actual_length); + } + + if (atomic_dec_and_test (&s->pending_io) && !s->remove_pending && s->state != _stopped) { + s->overruns++; + err("overrun (%d)", s->overruns); + } + wake_up (&s->wait); +} +/*-------------------------------------------------------------------*/ +static int dabusb_alloc_buffers (pdabusb_t s) +{ + int buffers = 0; + pbuff_t b; + unsigned int pipe = usb_rcvisocpipe (s->usbdev, _DABUSB_ISOPIPE); + int pipesize = usb_maxpacket (s->usbdev, pipe, usb_pipeout (pipe)); + int packets = _ISOPIPESIZE / pipesize; + int transfer_buffer_length = packets * pipesize; + int i; + + dbg("dabusb_alloc_buffers pipesize:%d packets:%d transfer_buffer_len:%d", + pipesize, packets, transfer_buffer_length); + + while (buffers < (s->total_buffer_size << 10)) { + b = (pbuff_t) kmalloc (sizeof (buff_t), GFP_KERNEL); + if (!b) { + err("kmalloc(sizeof(buff_t))==NULL"); + goto err; + } + memset (b, sizeof (buff_t), 0); + b->s = s; + b->purb = usb_alloc_urb(packets); + if (!b->purb) { + err("usb_alloc_urb == NULL"); + kfree (b); + goto err; + } + + b->purb->transfer_buffer = kmalloc (transfer_buffer_length, GFP_KERNEL); + if (!b->purb->transfer_buffer) { + kfree (b->purb); + kfree (b); + err("kmalloc(%d)==NULL", transfer_buffer_length); + goto err; + } + + b->purb->transfer_buffer_length = transfer_buffer_length; + b->purb->number_of_packets = packets; + b->purb->complete = dabusb_iso_complete; + b->purb->context = b; + b->purb->dev = s->usbdev; + b->purb->pipe = pipe; + b->purb->transfer_flags = USB_ISO_ASAP; + + for (i = 0; i < packets; i++) { + b->purb->iso_frame_desc[i].offset = i * pipesize; + b->purb->iso_frame_desc[i].length = pipesize; + } + + buffers += transfer_buffer_length; + list_add_tail (&b->buff_list, &s->free_buff_list); + } + s->got_mem = buffers; + + return 0; + + err: + dabusb_free_buffers (s); + return -ENOMEM; +} +/*-------------------------------------------------------------------*/ +static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb) +{ + int ret; + unsigned int pipe; + int actual_length; + + dbg("dabusb_bulk"); + + if (!pb->pipe) + pipe = usb_rcvbulkpipe (s->usbdev, 2); + else + pipe = usb_sndbulkpipe (s->usbdev, 2); + + ret=usb_bulk_msg(s->usbdev, pipe, pb->data, pb->size, &actual_length, 1000); + if(ret<0) { + err("dabusb: usb_bulk_msg failed(%d)",ret); + } + + if( ret == -EPIPE ) { + warn("CLEAR_FEATURE request to remove STALL condition."); + if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe))) + err("request failed"); + } + + pb->size = actual_length; + return ret; +} +/* --------------------------------------------------------------------- */ +static int dabusb_writemem (pdabusb_t s, int pos, unsigned char *data, int len) +{ + int ret; + unsigned char *transfer_buffer = kmalloc (len, GFP_KERNEL); + + if (!transfer_buffer) { + err("dabusb_writemem: kmalloc(%d) failed.", len); + return -ENOMEM; + } + + memcpy (transfer_buffer, data, len); + + ret=usb_control_msg(s->usbdev, usb_sndctrlpipe( s->usbdev, 0 ), 0xa0, 0x40, pos, 0, transfer_buffer, len, 300); + + kfree (transfer_buffer); + return ret; +} +/* --------------------------------------------------------------------- */ +static int dabusb_8051_reset (pdabusb_t s, unsigned char reset_bit) +{ + dbg("dabusb_8051_reset: %d",reset_bit); + return dabusb_writemem (s, CPUCS_REG, &reset_bit, 1); +} +/* --------------------------------------------------------------------- */ +static int dabusb_loadmem (pdabusb_t s, const char *fname) +{ + int ret; + PINTEL_HEX_RECORD ptr = firmware; + + dbg("Enter dabusb_loadmem (internal)"); + + ret = dabusb_8051_reset (s, 1); + while (ptr->Type == 0) { + + dbg("dabusb_writemem: %04X %p %d)", ptr->Address, ptr->Data, ptr->Length); + + ret = dabusb_writemem (s, ptr->Address, ptr->Data, ptr->Length); + if (ret < 0) { + err("dabusb_writemem failed (%d %04X %p %d)", ret, ptr->Address, ptr->Data, ptr->Length); + break; + } + ptr++; + } + ret = dabusb_8051_reset (s, 0); + + dbg("dabusb_loadmem: exit"); + + return ret; +} +/* --------------------------------------------------------------------- */ +static int dabusb_fpga_clear (pdabusb_t s, pbulk_transfer_t b) +{ + b->size = 4; + b->data[0] = 0x2a; + b->data[1] = 0; + b->data[2] = 0; + b->data[3] = 0; + + dbg("dabusb_fpga_clear"); + + return dabusb_bulk (s, b); +} +/* --------------------------------------------------------------------- */ +static int dabusb_fpga_init (pdabusb_t s, pbulk_transfer_t b) +{ + b->size = 4; + b->data[0] = 0x2c; + b->data[1] = 0; + b->data[2] = 0; + b->data[3] = 0; + + dbg("dabusb_fpga_init"); + + return dabusb_bulk (s, b); +} +/* --------------------------------------------------------------------- */ +static int dabusb_fpga_download (pdabusb_t s, const char *fname) +{ + pbulk_transfer_t b = kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL); + unsigned int blen, n; + int ret; + unsigned char *buf = bitstream; + + dbg("Enter dabusb_fpga_download (internal)"); + + if (!b) { + err("kmalloc(sizeof(bulk_transfer_t))==NULL"); + return -ENOMEM; + } + + b->pipe = 1; + ret = dabusb_fpga_clear (s, b); + mdelay (10); + blen = buf[73] + (buf[72] << 8); + + dbg("Bitstream len: %i", blen); + + b->data[0] = 0x2b; + b->data[1] = 0; + b->data[2] = 0; + b->data[3] = 60; + + for (n = 0; n <= blen + 60; n += 60) { + // some cclks for startup + b->size = 64; + memcpy (b->data + 4, buf + 74 + n, 60); + ret = dabusb_bulk (s, b); + if (ret < 0) { + err("dabusb_bulk failed."); + break; + } + mdelay (1); + } + + ret = dabusb_fpga_init (s, b); + kfree (b); + + dbg("exit dabusb_fpga_download"); + + return ret; +} + +static loff_t dabusb_llseek (struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static int dabusb_stop (pdabusb_t s) +{ + dbg("dabusb_stop"); + + s->state = _stopped; + dabusb_cancel_queue (s, &s->rec_buff_list); + + dbg("pending_io: %d", s->pending_io.counter); + + s->pending_io.counter = 0; + return 0; +} + +static int dabusb_startrek (pdabusb_t s) +{ + if (!s->got_mem && s->state != _started) { + + dbg("dabusb_startrek"); + + if (dabusb_alloc_buffers (s) < 0) + return -ENOMEM; + dabusb_stop (s); + s->state = _started; + s->readptr = 0; + } + + if (!list_empty (&s->free_buff_list)) { + pbuff_t end; + int ret; + + while (!dabusb_add_buf_tail (s, &s->rec_buff_list, &s->free_buff_list)) { + + dbg("submitting: end:%p s->rec_buff_list:%p", s->rec_buff_list.prev, &s->rec_buff_list); + + end = list_entry (s->rec_buff_list.prev, buff_t, buff_list); + + ret = usb_submit_urb (end->purb); + if (ret) { + err("usb_submit_urb returned:%d", ret); + if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list)) + err("startrek: dabusb_add_buf_tail failed"); + break; + } + else + atomic_inc (&s->pending_io); + } + dbg("pending_io: %d",s->pending_io.counter); + } + + return 0; +} + +static ssize_t dabusb_read (struct file *file, char *buf, size_t count, loff_t * ppos) +{ + pdabusb_t s = (pdabusb_t) file->private_data; + unsigned long flags; + unsigned ret = 0; + int rem; + int cnt; + pbuff_t b; + purb_t purb = NULL; + + dbg("dabusb_read"); + + if (*ppos) + return -ESPIPE; + + if (s->remove_pending) + return -EIO; + + + if (!s->usbdev) + return -EIO; + + while (count > 0) { + dabusb_startrek (s); + + spin_lock_irqsave (&s->lock, flags); + + if (list_empty (&s->rec_buff_list)) { + + spin_unlock_irqrestore(&s->lock, flags); + + err("error: rec_buf_list is empty"); + goto err; + } + + b = list_entry (s->rec_buff_list.next, buff_t, buff_list); + purb = b->purb; + + spin_unlock_irqrestore(&s->lock, flags); + + if (purb->status == -EINPROGRESS) { + if (file->f_flags & O_NONBLOCK) // return nonblocking + { + if (!ret) + ret = -EAGAIN; + goto err; + } + + interruptible_sleep_on (&s->wait); + + if (signal_pending (current)) { + if (!ret) + ret = -ERESTARTSYS; + goto err; + } + + spin_lock_irqsave (&s->lock, flags); + + if (list_empty (&s->rec_buff_list)) { + spin_unlock_irqrestore(&s->lock, flags); + err("error: still no buffer available."); + goto err; + } + spin_unlock_irqrestore(&s->lock, flags); + s->readptr = 0; + } + if (s->remove_pending) { + ret = -EIO; + goto err; + } + + rem = purb->actual_length - s->readptr; // set remaining bytes to copy + + if (count >= rem) + cnt = rem; + else + cnt = count; + + dbg("copy_to_user:%p %p %d",buf, purb->transfer_buffer + s->readptr, cnt); + + if (copy_to_user (buf, purb->transfer_buffer + s->readptr, cnt)) { + err("read: copy_to_user failed"); + if (!ret) + ret = -EFAULT; + goto err; + } + + s->readptr += cnt; + count -= cnt; + buf += cnt; + ret += cnt; + + if (s->readptr == purb->actual_length) { + // finished, take next buffer + if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list)) + err("read: dabusb_add_buf_tail failed"); + s->readptr = 0; + } + } + err: //up(&s->mutex); + return ret; +} + +static int dabusb_open (struct inode *inode, struct file *file) +{ + int devnum = MINOR (inode->i_rdev); + pdabusb_t s; + + if (devnum < DABUSB_MINOR || devnum >= (DABUSB_MINOR + NRDABUSB)) + return -EIO; + + s = &dabusb[devnum - DABUSB_MINOR]; + + dbg("dabusb_open"); + down (&s->mutex); + + while (!s->usbdev || s->opened) { + up (&s->mutex); + + if (file->f_flags & O_NONBLOCK) { + return -EBUSY; + } + schedule_timeout (HZ / 2); + + if (signal_pending (current)) { + return -EAGAIN; + } + down (&s->mutex); + } + if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) { + err("set_interface failed"); + return -EINVAL; + } + s->opened = 1; + up (&s->mutex); + + file->f_pos = 0; + file->private_data = s; + + return 0; +} + +static int dabusb_release (struct inode *inode, struct file *file) +{ + pdabusb_t s = (pdabusb_t) file->private_data; + + dbg("dabusb_release"); + + down (&s->mutex); + dabusb_stop (s); + dabusb_free_buffers (s); + up (&s->mutex); + + if (!s->remove_pending) { + if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) + err("set_interface failed"); + } + else + wake_up (&s->remove_ok); + + s->opened = 0; + return 0; +} + +static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + pdabusb_t s = (pdabusb_t) file->private_data; + pbulk_transfer_t pbulk; + int ret = 0; + int version = DABUSB_VERSION; + + dbg("dabusb_ioctl"); + + if (s->remove_pending) + return -EIO; + + down (&s->mutex); + + if (!s->usbdev) { + up (&s->mutex); + return -EIO; + } + + switch (cmd) { + + case IOCTL_DAB_BULK: + pbulk = (pbulk_transfer_t) kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL); + + if (!pbulk) { + ret = -ENOMEM; + break; + } + + if (copy_from_user (pbulk, (void *) arg, sizeof (bulk_transfer_t))) { + ret = -EFAULT; + kfree (pbulk); + break; + } + + dabusb_bulk (s, pbulk); + ret = copy_to_user ((void *) arg, pbulk, sizeof (bulk_transfer_t)); + kfree (pbulk); + break; + + case IOCTL_DAB_OVERRUNS: + ret = put_user (s->overruns, (unsigned int *) arg); + break; + + case IOCTL_DAB_VERSION: + ret = put_user (version, (unsigned int *) arg); + break; + + default: + ret = -ENOIOCTLCMD; + break; + } + up (&s->mutex); + return ret; +} + +static struct file_operations dabusb_fops = +{ + llseek: dabusb_llseek, + read: dabusb_read, + ioctl: dabusb_ioctl, + open: dabusb_open, + release: dabusb_release, +}; + +static int dabusb_find_struct (void) +{ + int u; + + for (u = 0; u < NRDABUSB; u++) { + pdabusb_t s = &dabusb[u]; + if (!s->usbdev) + return u; + } + return -1; +} + +/* --------------------------------------------------------------------- */ +static void *dabusb_probe (struct usb_device *usbdev, unsigned int ifnum) +{ + int devnum; + pdabusb_t s; + + dbg("dabusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum); + + /* the 1234:5678 is just a self assigned test ID */ + if ((usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x2131) && + (usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x9999)) + return NULL; + + /* We don't handle multiple configurations */ + if (usbdev->descriptor.bNumConfigurations != 1) + return NULL; + + if (ifnum != _DABUSB_IF && usbdev->descriptor.idProduct == 0x9999) + return NULL; + + devnum = dabusb_find_struct (); + if (devnum == -1) + return NULL; + + s = &dabusb[devnum]; + + down (&s->mutex); + s->remove_pending = 0; + s->usbdev = usbdev; + + if (usb_set_configuration (usbdev, usbdev->config[0].bConfigurationValue) < 0) { + err("set_configuration failed"); + goto reject; + } + if (usbdev->descriptor.idProduct == 0x2131) { + dabusb_loadmem (s, NULL); + goto reject; + } + else { + dabusb_fpga_download (s, NULL); + + if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) { + err("set_interface failed"); + goto reject; + } + } + dbg("bound to interface: %d", ifnum); + up (&s->mutex); + MOD_INC_USE_COUNT; + return s; + + reject: + up (&s->mutex); + s->usbdev = NULL; + return NULL; +} + +static void dabusb_disconnect (struct usb_device *usbdev, void *ptr) +{ + pdabusb_t s = (pdabusb_t) ptr; + + dbg("dabusb_disconnect"); + + s->remove_pending = 1; + wake_up (&s->wait); + if (s->state == _started) + sleep_on (&s->remove_ok); + s->usbdev = NULL; + s->overruns = 0; + MOD_DEC_USE_COUNT; +} + +static struct usb_driver dabusb_driver = +{ + "dabusb", + dabusb_probe, + dabusb_disconnect, + {NULL, NULL}, + &dabusb_fops, + DABUSB_MINOR +}; + +/* --------------------------------------------------------------------- */ + +int __init dabusb_init (void) +{ + unsigned u; + + /* register misc device */ + if (usb_register(&dabusb_driver)) + return -1; + + /* initialize struct */ + for (u = 0; u < NRDABUSB; u++) { + pdabusb_t s = &dabusb[u]; + memset (s, 0, sizeof (dabusb_t)); + init_MUTEX (&s->mutex); + s->usbdev = NULL; + s->total_buffer_size = buffers; + init_waitqueue_head (&s->wait); + init_waitqueue_head (&s->remove_ok); + spin_lock_init (&s->lock); + INIT_LIST_HEAD (&s->free_buff_list); + INIT_LIST_HEAD (&s->rec_buff_list); + } + + dbg("dabusb_init: driver registered"); + return 0; +} + +void __exit dabusb_cleanup (void) +{ + dbg("dabusb_cleanup"); + + usb_deregister (&dabusb_driver); +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE +MODULE_AUTHOR ("Deti Fliegl, deti@fliegl.de"); +MODULE_DESCRIPTION ("DAB-USB Interface Driver for Linux (c)1999"); +MODULE_PARM (buffers, "i"); + +int __init init_module (void) +{ + return dabusb_init (); +} + +void __exit cleanup_module (void) +{ + dabusb_cleanup (); +} + +#endif + +/* --------------------------------------------------------------------- */ diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/dabusb.h linux-2.2.17/drivers/usb/dabusb.h --- linux-2.2.17-orig/drivers/usb/dabusb.h Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/dabusb.h Sun Sep 24 04:26:23 2000 @@ -0,0 +1,84 @@ +#define _BULK_DATA_LEN 64 +typedef struct +{ + unsigned char data[_BULK_DATA_LEN]; + unsigned int size; + unsigned int pipe; +}bulk_transfer_t,*pbulk_transfer_t; + +#define DABUSB_MINOR 240 /* some unassigned USB minor */ +#define DABUSB_VERSION 0x1000 +#define IOCTL_DAB_BULK _IOWR('d', 0x30, bulk_transfer_t) +#define IOCTL_DAB_OVERRUNS _IOR('d', 0x15, int) +#define IOCTL_DAB_VERSION _IOR('d', 0x3f, int) + +#ifdef __KERNEL__ + +typedef enum { _stopped=0, _started } driver_state_t; + +typedef struct +{ + struct semaphore mutex; + struct usb_device *usbdev; + wait_queue_head_t wait; + wait_queue_head_t remove_ok; + spinlock_t lock; + volatile atomic_t pending_io; + driver_state_t state; + int remove_pending; + int got_mem; + int total_buffer_size; + unsigned int overruns; + int readptr; + int opened; + struct list_head free_buff_list; + struct list_head rec_buff_list; +} dabusb_t,*pdabusb_t; + +typedef struct +{ + pdabusb_t s; + purb_t purb; + struct list_head buff_list; +} buff_t,*pbuff_t; + +typedef struct +{ + wait_queue_head_t wait; +} bulk_completion_context_t, *pbulk_completion_context_t; + + +#define _DABUSB_IF 2 +#define _DABUSB_ISOPIPE 0x09 +#define _ISOPIPESIZE 16384 + +#define _BULK_DATA_LEN 64 +// Vendor specific request code for Anchor Upload/Download +// This one is implemented in the core +#define ANCHOR_LOAD_INTERNAL 0xA0 + +// EZ-USB Control and Status Register. Bit 0 controls 8051 reset +#define CPUCS_REG 0x7F92 +#define _TOTAL_BUFFERS 384 + +#define MAX_INTEL_HEX_RECORD_LENGTH 16 + +#ifndef _BYTE_DEFINED +#define _BYTE_DEFINED +typedef unsigned char BYTE; +#endif // !_BYTE_DEFINED + +#ifndef _WORD_DEFINED +#define _WORD_DEFINED +typedef unsigned short WORD; +#endif // !_WORD_DEFINED + +typedef struct _INTEL_HEX_RECORD +{ + BYTE Length; + WORD Address; + BYTE Type; + BYTE Data[MAX_INTEL_HEX_RECORD_LENGTH]; +} INTEL_HEX_RECORD, *PINTEL_HEX_RECORD; + +#endif diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/dc2xx.c linux-2.2.17/drivers/usb/dc2xx.c --- linux-2.2.17-orig/drivers/usb/dc2xx.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/dc2xx.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,475 @@ +/* + * Copyright (C) 1999-2000 by David Brownell + * + * 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. + */ + + +/* + * USB driver for Kodak DC-2XX series digital still cameras + * + * The protocol here is the same as the one going over a serial line, but + * it uses USB for speed. Set up /dev/kodak, get gphoto (www.gphoto.org), + * and have fun! + * + * This should also work for a number of other digital (non-Kodak) cameras, + * by adding the vendor and product IDs to the table below. They'll need + * to be the sort using USB just as a fast bulk data channel. + */ + +/* + * HISTORY + * + * 26 August, 1999 -- first release (0.1), works with my DC-240. + * The DC-280 (2Mpixel) should also work, but isn't tested. + * If you use gphoto, make sure you have the USB updates. + * Lives in a 2.3.14 or so Linux kernel, in drivers/usb. + * 31 August, 1999 -- minor update to recognize DC-260 and handle + * its endpoints being in a different order. Note that as + * of gPhoto 0.36pre, the USB updates are integrated. + * 12 Oct, 1999 -- handle DC-280 interface class (0xff not 0x0); + * added timeouts to bulk_msg calls. Minor updates, docs. + * 03 Nov, 1999 -- update for 2.3.25 kernel API changes. + * 08 Jan, 2000 .. multiple camera support + * + * Thanks to: the folk who've provided USB product IDs, sent in + * patches, and shared their sucesses! + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef DEBUG +#include + + + +/* current USB framework handles max of 16 USB devices per driver */ +#define MAX_CAMERAS 8 + +/* USB char devs use USB_MAJOR and from USB_CAMERA_MINOR_BASE up */ +#define USB_CAMERA_MINOR_BASE 80 + + +// XXX remove packet size limit, now that bulk transfers seem fixed + +/* Application protocol limit is 0x8002; USB has disliked that limit! */ +#define MAX_PACKET_SIZE 0x2000 /* e.g. image downloading */ + +#define MAX_READ_RETRY 5 /* times to retry reads */ +#define MAX_WRITE_RETRY 5 /* times to retry writes */ +#define RETRY_TIMEOUT (HZ) /* sleep between retries */ + + +/* table of cameras that work through this driver */ +static const struct camera { + unsigned short idVendor; + unsigned short idProduct; + /* plus hooks for camera-specific info if needed */ +} cameras [] = { + /* These have the same application level protocol */ + { 0x040a, 0x0120 }, // Kodak DC-240 + { 0x040a, 0x0130 }, // Kodak DC-280 + + /* These have a different application level protocol which + * is part of the Flashpoint "DigitaOS". That supports some + * non-camera devices, and some non-Kodak cameras. + */ + { 0x040a, 0x0100 }, // Kodak DC-220 + { 0x040a, 0x0110 }, // Kodak DC-260 + { 0x040a, 0x0111 }, // Kodak DC-265 + { 0x040a, 0x0112 }, // Kodak DC-290 + { 0xf003, 0x6002 }, // HP PhotoSmart C500 + + /* Other USB devices may well work here too, so long as they + * just stick to half duplex bulk packet exchanges. That + * means, among other things, no iso or interrupt endpoints. + */ +}; + + +struct camera_state { + struct usb_device *dev; /* USB device handle */ + int inEP; /* read endpoint */ + int outEP; /* write endpoint */ + const struct camera *info; /* DC-240, etc */ + int subminor; /* which minor dev #? */ + int isActive; /* I/O taking place? */ + + /* this is non-null iff the device is open */ + char *buf; /* buffer for I/O */ + + /* always valid */ + wait_queue_head_t wait; /* for timed waits */ +}; + + +/* Support multiple cameras, possibly of different types. */ +static struct camera_state *minor_data [MAX_CAMERAS]; + + +static ssize_t camera_read (struct file *file, + char *buf, size_t len, loff_t *ppos) +{ + struct camera_state *camera; + int retries; + + if (len > MAX_PACKET_SIZE) + return -EINVAL; + + camera = (struct camera_state *) file->private_data; + if (!camera->dev) + return -ENODEV; + if (camera->isActive++) + return -EBUSY; + + /* Big reads are common, for image downloading. Smaller ones + * are also common (even "directory listing" commands don't + * send very much data). We preserve packet boundaries here, + * they matter in the application protocol. + */ + for (retries = 0; retries < MAX_READ_RETRY; retries++) { + int count; + int result; + + if (signal_pending (current)) { + camera->isActive = 0; + return -EINTR; + } + if (!camera->dev) { + camera->isActive = 0; + return -ENODEV; + } + + result = usb_bulk_msg (camera->dev, + usb_rcvbulkpipe (camera->dev, camera->inEP), + camera->buf, len, &count, HZ*10); + + dbg ("read (%d) - 0x%x %d", len, result, count); + + if (!result) { + if (copy_to_user (buf, camera->buf, count)) + return -EFAULT; + camera->isActive = 0; + return count; + } + if (result != USB_ST_TIMEOUT) + break; + interruptible_sleep_on_timeout (&camera->wait, RETRY_TIMEOUT); + + dbg ("read (%d) - retry", len); + } + camera->isActive = 0; + return -EIO; +} + +static ssize_t camera_write (struct file *file, + const char *buf, size_t len, loff_t *ppos) +{ + struct camera_state *camera; + ssize_t bytes_written = 0; + + if (len > MAX_PACKET_SIZE) + return -EINVAL; + + camera = (struct camera_state *) file->private_data; + if (!camera->dev) + return -ENODEV; + if (camera->isActive++) + return -EBUSY; + + /* most writes will be small: simple commands, sometimes with + * parameters. putting images (like borders) into the camera + * would be the main use of big writes. + */ + while (len > 0) { + char *obuf = camera->buf; + int maxretry = MAX_WRITE_RETRY; + unsigned long copy_size, thistime; + + /* it's not clear that retrying can do any good ... or that + * fragmenting application packets into N writes is correct. + */ + thistime = copy_size = len; + if (copy_from_user (obuf, buf, copy_size)) { + bytes_written = -EFAULT; + break; + } + while (thistime) { + int result; + int count; + + if (signal_pending (current)) { + if (!bytes_written) + bytes_written = -EINTR; + goto done; + } + if (!camera->dev) { + if (!bytes_written) + bytes_written = -ENODEV; + goto done; + } + + result = usb_bulk_msg (camera->dev, + usb_sndbulkpipe (camera->dev, camera->outEP), + obuf, thistime, &count, HZ*10); + + if (result) + dbg ("write USB err - %x", result); + + if (count) { + obuf += count; + thistime -= count; + maxretry = MAX_WRITE_RETRY; + continue; + } else if (!result) + break; + + if (result == USB_ST_TIMEOUT) { /* NAK - delay a bit */ + if (!maxretry--) { + if (!bytes_written) + bytes_written = -ETIME; + goto done; + } + interruptible_sleep_on_timeout (&camera->wait, + RETRY_TIMEOUT); + continue; + } + if (!bytes_written) + bytes_written = -EIO; + goto done; + } + bytes_written += copy_size; + len -= copy_size; + buf += copy_size; + } +done: + camera->isActive = 0; + dbg ("wrote %d", bytes_written); + return bytes_written; +} + +static int camera_open (struct inode *inode, struct file *file) +{ + struct camera_state *camera; + int subminor; + + subminor = MINOR (inode->i_rdev) - USB_CAMERA_MINOR_BASE; + if (subminor < 0 || subminor >= MAX_CAMERAS + || !(camera = minor_data [subminor])) { + return -ENODEV; + } + + if (!(camera->buf = (char *) kmalloc (MAX_PACKET_SIZE, GFP_KERNEL))) { + return -ENOMEM; + } + + dbg ("open"); + + camera->isActive = 0; + file->private_data = camera; + return 0; +} + +static int camera_release (struct inode *inode, struct file *file) +{ + struct camera_state *camera; + + camera = (struct camera_state *) file->private_data; + kfree (camera->buf); + + /* If camera was unplugged with open file ... */ + if (!camera->dev) { + minor_data [camera->subminor] = NULL; + kfree (camera); + } + + dbg ("close"); + + return 0; +} + + /* XXX should define some ioctls to expose camera type + * to applications ... what USB exposes should suffice. + * apps should be able to see the camera type. + */ +static /* const */ struct file_operations usb_camera_fops = { + /* Uses GCC initializer extension; simpler to maintain */ + read: camera_read, + write: camera_write, + open: camera_open, + release: camera_release, +}; + + + +static void * camera_probe(struct usb_device *dev, unsigned int ifnum) +{ + int i; + const struct camera *camera_info = NULL; + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + int direction, ep; + struct camera_state *camera; + + /* Is it a supported camera? */ + for (i = 0; i < sizeof (cameras) / sizeof (struct camera); i++) { + if (cameras [i].idVendor != dev->descriptor.idVendor) + continue; + if (cameras [i].idProduct != dev->descriptor.idProduct) + continue; + camera_info = &cameras [i]; + break; + } + if (camera_info == NULL) + return NULL; + + /* these have one config, one interface */ + if (dev->descriptor.bNumConfigurations != 1 + || dev->config[0].bNumInterfaces != 1) { + dbg ("Bogus camera config info"); + return NULL; + } + + /* models differ in how they report themselves */ + interface = &dev->actconfig->interface[ifnum].altsetting[0]; + if ((interface->bInterfaceClass != USB_CLASS_PER_INTERFACE + && interface->bInterfaceClass != USB_CLASS_VENDOR_SPEC) + || interface->bInterfaceSubClass != 0 + || interface->bInterfaceProtocol != 0 + || interface->bNumEndpoints != 2 + ) { + dbg ("Bogus camera interface info"); + return NULL; + } + + + /* select "subminor" number (part of a minor number) */ + for (i = 0; i < MAX_CAMERAS; i++) { + if (!minor_data [i]) + break; + } + if (i >= MAX_CAMERAS) { + info ("Ignoring additional USB Camera"); + return NULL; + } + + /* allocate & init camera state */ + camera = minor_data [i] = kmalloc (sizeof *camera, GFP_KERNEL); + if (!camera) { + err ("no memory!"); + return NULL; + } + camera->info = camera_info; + camera->subminor = i; + camera->isActive = 0; + camera->buf = NULL; + init_waitqueue_head (&camera->wait); + info ("USB Camera #%d connected", camera->subminor); + + + /* get input and output endpoints (either order) */ + endpoint = interface->endpoint; + camera->outEP = camera->inEP = -1; + + ep = endpoint [0].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + direction = endpoint [0].bEndpointAddress & USB_ENDPOINT_DIR_MASK; + if (direction == USB_DIR_IN) + camera->inEP = ep; + else + camera->outEP = ep; + + ep = endpoint [1].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + direction = endpoint [1].bEndpointAddress & USB_ENDPOINT_DIR_MASK; + if (direction == USB_DIR_IN) + camera->inEP = ep; + else + camera->outEP = ep; + + if (camera->outEP == -1 || camera->inEP == -1 + || endpoint [0].bmAttributes != USB_ENDPOINT_XFER_BULK + || endpoint [1].bmAttributes != USB_ENDPOINT_XFER_BULK + ) { + dbg ("Bogus endpoints"); + goto error; + } + + + if (usb_set_configuration (dev, dev->config[0].bConfigurationValue)) { + err ("Failed usb_set_configuration"); + goto error; + } + + camera->dev = dev; + return camera; + +error: + minor_data [camera->subminor] = NULL; + kfree (camera); + return NULL; +} + +static void camera_disconnect(struct usb_device *dev, void *ptr) +{ + struct camera_state *camera = (struct camera_state *) ptr; + int subminor = camera->subminor; + + /* If camera's not opened, we can clean up right away. + * Else apps see a disconnect on next I/O; the release cleans. + */ + if (!camera->buf) { + minor_data [subminor] = NULL; + kfree (camera); + } else + camera->dev = NULL; + + info ("USB Camera #%d disconnected", subminor); +} + +static /* const */ struct usb_driver camera_driver = { + "dc2xx", + camera_probe, + camera_disconnect, + { NULL, NULL }, + &usb_camera_fops, + USB_CAMERA_MINOR_BASE +}; + + +int __init usb_dc2xx_init(void) +{ + if (usb_register (&camera_driver) < 0) + return -1; + return 0; +} + +void __exit usb_dc2xx_cleanup(void) +{ + usb_deregister (&camera_driver); +} + + +MODULE_AUTHOR("David Brownell, david-b@pacbell.net"); +MODULE_DESCRIPTION("USB Camera Driver for Kodak DC-2xx series cameras"); + +module_init (usb_dc2xx_init); +module_exit (usb_dc2xx_cleanup); diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/devices.c linux-2.2.17/drivers/usb/devices.c --- linux-2.2.17-orig/drivers/usb/devices.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/devices.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,526 @@ +/* + * devices.c + * (C) Copyright 1999 Randy Dunlap. + * (C) Copyright 1999,2000 Thomas Sailer . (proc file per device) + * (C) Copyright 1999 Deti Fliegl (new USB architecture) + * + * $id$ + * + * 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 + * + ************************************************************* + * + * /devices contains USB topology, device, config, class, + * interface, & endpoint data. + * + * I considered using /proc/bus/usb/devices/device# for each device + * as it is attached or detached, but I didn't like this for some + * reason -- maybe it's just too deep of a directory structure. + * I also don't like looking in multiple places to gather and view + * the data. Having only one file for ./devices also prevents race + * conditions that could arise if a program was reading device info + * for devices that are being removed (unplugged). (That is, the + * program may find a directory for devnum_12 then try to open it, + * but it was just unplugged, so the directory is now deleted. + * But programs would just have to be prepared for situations like + * this in any plug-and-play environment.) + * + * 1999-12-16: Thomas Sailer + * Converted the whole proc stuff to real + * read methods. Now not the whole device list needs to fit + * into one page, only the device list for one bus. + * Added a poll method to /proc/bus/usb/devices, to wake + * up an eventual usbd + * 2000-01-04: Thomas Sailer + * Turned into its own filesystem + * + * $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_TOPO_LEVEL 6 + +/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */ +#define ALLOW_SERIAL_NUMBER + +static char *format_topo = +/* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd */ + "T: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s MxCh=%2d\n"; + +static char *format_string_manufacturer = +/* S: Manufacturer=xxxx */ + "S: Manufacturer=%.100s\n"; + +static char *format_string_product = +/* S: Product=xxxx */ + "S: Product=%.100s\n"; + +#ifdef ALLOW_SERIAL_NUMBER +static char *format_string_serialnumber = +/* S: SerialNumber=xxxx */ + "S: SerialNumber=%.100s\n"; +#endif + +static char *format_bandwidth = +/* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */ + "B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n"; + +static char *format_device1 = +/* D: Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */ + "D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n"; + +static char *format_device2 = +/* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx */ + "P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n"; + +static char *format_config = +/* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */ + "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n"; + +static char *format_iface = +/* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/ + "I: If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; + +static char *format_endpt = +/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms */ + "E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%3dms\n"; + + +/* + * Need access to the driver and USB bus lists. + * extern struct list_head usb_driver_list; + * extern struct list_head usb_bus_list; + * However, these will come from functions that return ptrs to each of them. + */ + +static DECLARE_WAIT_QUEUE_HEAD(deviceconndiscwq); +static unsigned int conndiscevcnt = 0; + +/* this struct stores the poll state for /devices pollers */ +struct usb_device_status { + unsigned int lastev; +}; + +struct class_info { + int class; + char *class_name; +}; + +static const struct class_info clas_info[] = +{ /* max. 5 chars. per name string */ + {USB_CLASS_PER_INTERFACE, ">ifc"}, + {USB_CLASS_AUDIO, "audio"}, + {USB_CLASS_COMM, "comm."}, + {USB_CLASS_HID, "HID"}, + {USB_CLASS_HUB, "hub"}, + {USB_CLASS_PRINTER, "print"}, + {USB_CLASS_MASS_STORAGE, "stor."}, + {USB_CLASS_DATA, "data"}, + {USB_CLASS_VENDOR_SPEC, "vend."}, + {-1, "unk."} /* leave as last */ +}; + +/*****************************************************************/ + +void usbdevfs_conn_disc_event(void) +{ + wake_up(&deviceconndiscwq); + conndiscevcnt++; +} + +static const char *class_decode(const int class) +{ + int ix; + + for (ix = 0; clas_info[ix].class != -1; ix++) + if (clas_info[ix].class == class) + break; + return (clas_info[ix].class_name); +} + +static char *usb_dump_endpoint_descriptor(char *start, char *end, const struct usb_endpoint_descriptor *desc) +{ + char *EndpointType [4] = {"Ctrl", "Isoc", "Bulk", "Int."}; + + if (start > end) + return start; + start += sprintf(start, format_endpt, desc->bEndpointAddress, + (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_CONTROL + ? 'B' : /* bidirectional */ + (desc->bEndpointAddress & USB_DIR_IN) ? 'I' : 'O', + desc->bmAttributes, EndpointType[desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK], + desc->wMaxPacketSize, desc->bInterval); + return start; +} + +static char *usb_dump_endpoint(char *start, char *end, const struct usb_endpoint_descriptor *endpoint) +{ + return usb_dump_endpoint_descriptor(start, end, endpoint); +} + +static char *usb_dump_interface_descriptor(char *start, char *end, const struct usb_interface *iface, int setno) +{ + struct usb_interface_descriptor *desc = &iface->altsetting[setno]; + + if (start > end) + return start; + start += sprintf(start, format_iface, + desc->bInterfaceNumber, + desc->bAlternateSetting, + desc->bNumEndpoints, + desc->bInterfaceClass, + class_decode(desc->bInterfaceClass), + desc->bInterfaceSubClass, + desc->bInterfaceProtocol, + iface->driver ? iface->driver->name : "(none)"); + return start; +} + +static char *usb_dump_interface(char *start, char *end, const struct usb_interface *iface, int setno) +{ + struct usb_interface_descriptor *desc = &iface->altsetting[setno]; + int i; + + start = usb_dump_interface_descriptor(start, end, iface, setno); + for (i = 0; i < desc->bNumEndpoints; i++) { + if (start > end) + return start; + start = usb_dump_endpoint(start, end, desc->endpoint + i); + } + return start; +} + +/* TBD: + * 0. TBDs + * 1. marking active config and ifaces (code lists all, but should mark + * which ones are active, if any) + * 2. add status to each endpoint line + */ + +static char *usb_dump_config_descriptor(char *start, char *end, const struct usb_config_descriptor *desc, int active) +{ + if (start > end) + return start; + start += sprintf(start, format_config, + active ? '*' : ' ', /* mark active/actual/current cfg. */ + desc->bNumInterfaces, + desc->bConfigurationValue, + desc->bmAttributes, + desc->MaxPower * 2); + return start; +} + +static char *usb_dump_config(char *start, char *end, const struct usb_config_descriptor *config, int active) +{ + int i, j; + struct usb_interface *interface; + + if (start > end) + return start; + if (!config) /* getting these some in 2.3.7; none in 2.3.6 */ + return start + sprintf(start, "(null Cfg. desc.)\n"); + start = usb_dump_config_descriptor(start, end, config, active); + for (i = 0; i < config->bNumInterfaces; i++) { + interface = config->interface + i; + if (!interface) + break; + for (j = 0; j < interface->num_altsetting; j++) { + if (start > end) + return start; + start = usb_dump_interface(start, end, interface, j); + } + } + return start; +} + +/* + * Dump the different USB descriptors. + */ +static char *usb_dump_device_descriptor(char *start, char *end, const struct usb_device_descriptor *desc) +{ + if (start > end) + return start; + start += sprintf (start, format_device1, + desc->bcdUSB >> 8, desc->bcdUSB & 0xff, + desc->bDeviceClass, + class_decode (desc->bDeviceClass), + desc->bDeviceSubClass, + desc->bDeviceProtocol, + desc->bMaxPacketSize0, + desc->bNumConfigurations); + if (start > end) + return start; + start += sprintf(start, format_device2, + desc->idVendor, desc->idProduct, + desc->bcdDevice >> 8, desc->bcdDevice & 0xff); + return start; +} + +/* + * Dump the different strings that this device holds. + */ +static char *usb_dump_device_strings (char *start, char *end, struct usb_device *dev) +{ + char *buf; + + if (start > end) + return start; + buf = kmalloc(128, GFP_KERNEL); + if (!buf) + return start; + if (dev->descriptor.iManufacturer) { + if (usb_string(dev, dev->descriptor.iManufacturer, buf, 128) > 0) + start += sprintf(start, format_string_manufacturer, buf); + } + if (start > end) + goto out; + if (dev->descriptor.iProduct) { + if (usb_string(dev, dev->descriptor.iProduct, buf, 128) > 0) + start += sprintf(start, format_string_product, buf); + } + if (start > end) + goto out; +#ifdef ALLOW_SERIAL_NUMBER + if (dev->descriptor.iSerialNumber) { + if (usb_string(dev, dev->descriptor.iSerialNumber, buf, 128) > 0) + start += sprintf(start, format_string_serialnumber, buf); + } +#endif + out: + kfree(buf); + return start; +} + +static char *usb_dump_desc(char *start, char *end, struct usb_device *dev) +{ + int i; + + if (start > end) + return start; + + start = usb_dump_device_descriptor(start, end, &dev->descriptor); + + if (start > end) + return start; + + start = usb_dump_device_strings (start, end, dev); + + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + if (start > end) + return start; + start = usb_dump_config(start, end, dev->config + i, + (dev->config + i) == dev->actconfig); /* active ? */ + } + return start; +} + + +#ifdef PROC_EXTRA /* TBD: may want to add this code later */ + +static char *usb_dump_hub_descriptor(char *start, char *end, const struct usb_hub_descriptor * desc) +{ + int leng = USB_DT_HUB_NONVAR_SIZE; + unsigned char *ptr = (unsigned char *)desc; + + if (start > end) + return start; + start += sprintf(start, "Interface:"); + while (leng && start <= end) { + start += sprintf(start, " %02x", *ptr); + ptr++; leng--; + } + *start++ = '\n'; + return start; +} + +static char *usb_dump_string(char *start, char *end, const struct usb_device *dev, char *id, int index) +{ + if (start > end) + return start; + start += sprintf(start, "Interface:"); + if (index <= dev->maxstring && dev->stringindex && dev->stringindex[index]) + start += sprintf(start, "%s: %.100s ", id, dev->stringindex[index]); + return start; +} + +#endif /* PROC_EXTRA */ + +/*****************************************************************/ + +static char *usb_device_dump(char *start, char *end, struct usb_device *usbdev, + struct usb_bus *bus, int level, int index, int count) +{ + int chix; + int cnt = 0; + int parent_devnum = 0; + + if (level > MAX_TOPO_LEVEL) + return start; + if (usbdev->parent && usbdev->parent->devnum != -1) + parent_devnum = usbdev->parent->devnum; + /* + * So the root hub's parent is 0 and any device that is + * plugged into the root hub has a parent of 0. + */ + start += sprintf(start, format_topo, bus->busnum, level, parent_devnum, index, count, + usbdev->devnum, usbdev->slow ? "1.5" : "12 ", usbdev->maxchild); + /* + * level = topology-tier level; + * parent_devnum = parent device number; + * index = parent's connector number; + * count = device count at this level + */ + /* If this is the root hub, display the bandwidth information */ + if (level == 0) + start += sprintf(start, format_bandwidth, bus->bandwidth_allocated, + FRAME_TIME_MAX_USECS_ALLOC, + (100 * bus->bandwidth_allocated + FRAME_TIME_MAX_USECS_ALLOC / 2) / FRAME_TIME_MAX_USECS_ALLOC, + bus->bandwidth_int_reqs, bus->bandwidth_isoc_reqs); + start = usb_dump_desc(start, end, usbdev); + if (start > end) + return start + sprintf(start, "(truncated)\n"); + /* Now look at all of this device's children. */ + for (chix = 0; chix < usbdev->maxchild; chix++) { + if (start > end) + return start; + if (usbdev->children[chix]) + start = usb_device_dump(start, end, usbdev->children[chix], bus, level + 1, chix, ++cnt); + } + return start; +} + +static ssize_t usb_device_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) +{ + struct list_head *buslist; + struct usb_bus *bus; + char *page, *end; + ssize_t ret = 0; + unsigned int pos, len; + + if (*ppos < 0) + return -EINVAL; + if (nbytes <= 0) + return 0; + if (!access_ok(VERIFY_WRITE, buf, nbytes)) + return -EFAULT; + if (!(page = (char*) __get_free_pages(GFP_KERNEL,1))) + return -ENOMEM; + pos = *ppos; + /* enumerate busses */ + for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) { + /* print devices for this bus */ + bus = list_entry(buslist, struct usb_bus, bus_list); + end = usb_device_dump(page, page + (2*PAGE_SIZE - 256), bus->root_hub, bus, 0, 0, 0); + len = end - page; + if (len > pos) { + len -= pos; + if (len > nbytes) + len = nbytes; + if (copy_to_user(buf, page + pos, len)) { + if (!ret) + ret = -EFAULT; + break; + } + nbytes -= len; + buf += len; + ret += len; + pos = 0; + *ppos += len; + } else + pos -= len; + } + free_pages((unsigned long)page, 1); + return ret; +} + +/* Kernel lock for "lastev" protection */ +static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait) +{ + struct usb_device_status *st = (struct usb_device_status *)file->private_data; + unsigned int mask = 0; + + lock_kernel(); + if (!st) { + st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL); + if (!st) + return POLLIN; + /* + * need to prevent the module from being unloaded, since + * proc_unregister does not call the release method and + * we would have a memory leak + */ + st->lastev = conndiscevcnt; + file->private_data = st; + mask = POLLIN; + } + if (file->f_mode & FMODE_READ) + poll_wait(file, &deviceconndiscwq, wait); + if (st->lastev != conndiscevcnt) + mask |= POLLIN; + st->lastev = conndiscevcnt; + unlock_kernel(); + return mask; +} + +static int usb_device_open(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return 0; +} + +static int usb_device_release(struct inode *inode, struct file *file) +{ + if (file->private_data) { + kfree(file->private_data); + file->private_data = NULL; + } + + return 0; +} + +static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig) +{ + switch (orig) { + case 0: + file->f_pos = offset; + return file->f_pos; + + case 1: + file->f_pos += offset; + return file->f_pos; + + case 2: + return -EINVAL; + + default: + return -EINVAL; + } +} + +struct file_operations usbdevfs_devices_fops = { + llseek: usb_device_lseek, + read: usb_device_read, + poll: usb_device_poll, + open: usb_device_open, + release: usb_device_release, +}; diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/devio.c linux-2.2.17/drivers/usb/devio.c --- linux-2.2.17-orig/drivers/usb/devio.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/devio.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,1201 @@ +/*****************************************************************************/ + +/* + * devio.c -- User space communication with USB devices. + * + * Copyright (C) 1999-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * 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. + * + * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $ + * + * This file implements the usbdevfs/x/y files, where + * x is the bus number and y the device number. + * + * It allows user space programs/"drivers" to communicate directly + * with USB devices without intervening kernel driver. + * + * Revision history + * 22.12.1999 0.1 Initial release (split from proc_usb.c) + * 04.01.2000 0.2 Turned into its own filesystem + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct async { + struct list_head asynclist; + struct dev_state *ps; + struct task_struct *task; + unsigned int signr; + void *userbuffer; + void *userurb; + urb_t urb; +}; + +/* + * my own sync control and bulk methods. Here to experiment + * and because the kernel ones set the process to TASK_UNINTERRUPTIBLE. + */ + +struct sync { + wait_queue_head_t wait; +}; + +static void sync_completed(purb_t urb) +{ + struct sync *s = (struct sync *)urb->context; + + wake_up(&s->wait); +} + +static int do_sync(purb_t urb, int timeout) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long tm; + signed long tmdiff; + struct sync s; + int ret; + + tm = jiffies+timeout; + init_waitqueue_head(&s.wait); + add_wait_queue(&s.wait, &wait); + urb->context = &s; + urb->complete = sync_completed; + set_current_state(TASK_INTERRUPTIBLE); + if ((ret = usb_submit_urb(urb))) + goto out; + while (urb->status == -EINPROGRESS) { + tmdiff = tm - jiffies; + if (tmdiff <= 0) { + ret = -ETIMEDOUT; + goto out; + } + if (signal_pending(current)) { + ret = -EINTR; + goto out; + } + schedule_timeout(tmdiff); + } + ret = urb->status; + out: + set_current_state(TASK_RUNNING); + usb_unlink_urb(urb); + remove_wait_queue(&s.wait, &wait); + return ret; +} + +static int my_usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, + __u16 value, __u16 index, void *data, __u16 size, int timeout) +{ + urb_t *urb; + int ret; + + if (!(urb = usb_alloc_urb(0))) + return -ENOMEM; + if (!(urb->setup_packet = kmalloc(8, GFP_KERNEL))) { + usb_free_urb(urb); + return -ENOMEM; + } + urb->setup_packet[0] = requesttype; + urb->setup_packet[1] = request; + urb->setup_packet[2] = value; + urb->setup_packet[3] = value >> 8; + urb->setup_packet[4] = index; + urb->setup_packet[5] = index >> 8; + urb->setup_packet[6] = size; + urb->setup_packet[7] = size >> 8; + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer = data; + urb->transfer_buffer_length = size; + ret = do_sync(urb, timeout); + //if (ret >= 0) + // ret = urb->status; + if (ret >= 0) + ret = urb->actual_length; + kfree(urb->setup_packet); + usb_free_urb(urb); + return ret; +} + +static int my_usb_bulk_msg(struct usb_device *dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout) +{ + urb_t *urb; + int ret; + + if (!(urb = usb_alloc_urb(0))) + return -ENOMEM; + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer = data; + urb->transfer_buffer_length = len; + ret = do_sync(urb, timeout); + //if (ret >= 0) + // ret = urb->status; + if (ret >= 0 && actual_length != NULL) + *actual_length = urb->actual_length; + usb_free_urb(urb); + return ret; +} + +static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig) +{ + switch (orig) { + case 0: + file->f_pos = offset; + return file->f_pos; + + case 1: + file->f_pos += offset; + return file->f_pos; + + case 2: + return -EINVAL; + + default: + return -EINVAL; + } +} + +static ssize_t usbdev_read(struct file *file, char * buf, size_t nbytes, loff_t *ppos) +{ + struct dev_state *ps = (struct dev_state *)file->private_data; + ssize_t ret = 0; + unsigned len; + loff_t pos; + int i; + + pos = *ppos; + down_read(&ps->devsem); + if (!ps->dev) { + ret = -ENODEV; + goto err; + } else if (pos < 0) { + ret = -EINVAL; + goto err; + } + + if (pos < sizeof(struct usb_device_descriptor)) { + len = sizeof(struct usb_device_descriptor) - pos; + if (len > nbytes) + len = nbytes; + if (copy_to_user(buf, ((char *)&ps->dev->descriptor) + pos, len)) { + ret = -EFAULT; + goto err; + } + + *ppos += len; + buf += len; + nbytes -= len; + ret += len; + } + + pos = sizeof(struct usb_device_descriptor); + for (i = 0; nbytes && i < ps->dev->descriptor.bNumConfigurations; i++) { + struct usb_config_descriptor *config = + (struct usb_config_descriptor *)ps->dev->rawdescriptors[i]; + unsigned int length = le16_to_cpu(config->wTotalLength); + + if (*ppos < pos + length) { + len = length - (*ppos - pos); + if (len > nbytes) + len = nbytes; + + if (copy_to_user(buf, + ps->dev->rawdescriptors[i] + (*ppos - pos), len)) { + ret = -EFAULT; + goto err; + } + + *ppos += len; + buf += len; + nbytes -= len; + ret += len; + } + + pos += length; + } + +err: + up_read(&ps->devsem); + return ret; +} + +extern inline unsigned int ld2(unsigned int x) +{ + unsigned int r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* + * async list handling + */ + +static struct async *alloc_async(unsigned int numisoframes) +{ + unsigned int assize = sizeof(struct async) + numisoframes * sizeof(iso_packet_descriptor_t); + struct async *as = kmalloc(assize, GFP_KERNEL); + if (!as) + return NULL; + memset(as, 0, assize); + as->urb.number_of_packets = numisoframes; + return as; +} + +static void free_async(struct async *as) +{ + if (as->urb.transfer_buffer) + kfree(as->urb.transfer_buffer); + kfree(as); +} + +extern __inline__ void async_newpending(struct async *as) +{ + struct dev_state *ps = as->ps; + unsigned long flags; + + spin_lock_irqsave(&ps->lock, flags); + list_add_tail(&as->asynclist, &ps->async_pending); + spin_unlock_irqrestore(&ps->lock, flags); +} + +extern __inline__ void async_removepending(struct async *as) +{ + struct dev_state *ps = as->ps; + unsigned long flags; + + spin_lock_irqsave(&ps->lock, flags); + list_del(&as->asynclist); + INIT_LIST_HEAD(&as->asynclist); + spin_unlock_irqrestore(&ps->lock, flags); +} + +extern __inline__ struct async *async_getcompleted(struct dev_state *ps) +{ + unsigned long flags; + struct async *as = NULL; + + spin_lock_irqsave(&ps->lock, flags); + if (!list_empty(&ps->async_completed)) { + as = list_entry(ps->async_completed.next, struct async, asynclist); + list_del(&as->asynclist); + INIT_LIST_HEAD(&as->asynclist); + } + spin_unlock_irqrestore(&ps->lock, flags); + return as; +} + +extern __inline__ struct async *async_getpending(struct dev_state *ps, void *userurb) +{ + unsigned long flags; + struct async *as; + struct list_head *p; + + spin_lock_irqsave(&ps->lock, flags); + for (p = ps->async_pending.next; p != &ps->async_pending; ) { + as = list_entry(p, struct async, asynclist); + p = p->next; + if (as->userurb != userurb) + continue; + list_del(&as->asynclist); + INIT_LIST_HEAD(&as->asynclist); + spin_unlock_irqrestore(&ps->lock, flags); + return as; + } + spin_unlock_irqrestore(&ps->lock, flags); + return NULL; +} + +static void async_completed(purb_t urb) +{ + struct async *as = (struct async *)urb->context; + struct dev_state *ps = as->ps; + struct siginfo sinfo; + +#if 1 + printk(KERN_DEBUG "usbdevfs: async_completed: status %d errcount %d actlen %d pipe 0x%x\n", + urb->status, urb->error_count, urb->actual_length, urb->pipe); +#endif + spin_lock(&ps->lock); + list_del(&as->asynclist); + list_add_tail(&as->asynclist, &ps->async_completed); + spin_unlock(&ps->lock); + wake_up(&ps->wait); + if (as->signr) { + sinfo.si_signo = as->signr; + sinfo.si_errno = as->urb.status; + sinfo.si_code = SI_ASYNCIO; + sinfo.si_addr = as->userurb; + send_sig_info(as->signr, &sinfo, as->task); + } +} + +static void destroy_all_async(struct dev_state *ps) +{ + struct async *as; + unsigned long flags; + + spin_lock_irqsave(&ps->lock, flags); + while (!list_empty(&ps->async_pending)) { + as = list_entry(ps->async_pending.next, struct async, asynclist); + list_del(&as->asynclist); + INIT_LIST_HEAD(&as->asynclist); + spin_unlock_irqrestore(&ps->lock, flags); + /* usb_unlink_urb calls the completion handler with status == USB_ST_URB_KILLED */ + usb_unlink_urb(&as->urb); + spin_lock_irqsave(&ps->lock, flags); + } + spin_unlock_irqrestore(&ps->lock, flags); + while ((as = async_getcompleted(ps))) + free_async(as); +} + +/* + * interface claiming + */ + +static void *driver_probe(struct usb_device *dev, unsigned int intf) +{ + return NULL; +} + +static void driver_disconnect(struct usb_device *dev, void *context) +{ + struct dev_state *ps = (struct dev_state *)context; + + ps->ifclaimed = 0; +} + +struct usb_driver usbdevfs_driver = { + name: "usbdevfs", + probe: driver_probe, + disconnect: driver_disconnect, +}; + +static int claimintf(struct dev_state *ps, unsigned int intf) +{ + struct usb_device *dev = ps->dev; + struct usb_interface *iface; + int err; + + if (intf >= 8*sizeof(ps->ifclaimed) || !dev || intf >= dev->actconfig->bNumInterfaces) + return -EINVAL; + /* already claimed */ + if (test_bit(intf, &ps->ifclaimed)) + return 0; + iface = &dev->actconfig->interface[intf]; + err = -EBUSY; + lock_kernel(); + if (!usb_interface_claimed(iface)) { + usb_driver_claim_interface(&usbdevfs_driver, iface, ps); + set_bit(intf, &ps->ifclaimed); + err = 0; + } + unlock_kernel(); + return err; +} + +static int releaseintf(struct dev_state *ps, unsigned int intf) +{ + struct usb_device *dev; + struct usb_interface *iface; + int err; + + if (intf >= 8*sizeof(ps->ifclaimed)) + return -EINVAL; + err = -EINVAL; + lock_kernel(); + dev = ps->dev; + if (dev && test_and_clear_bit(intf, &ps->ifclaimed)) { + iface = &dev->actconfig->interface[intf]; + usb_driver_release_interface(&usbdevfs_driver, iface); + err = 0; + } + unlock_kernel(); + return err; +} + +static int checkintf(struct dev_state *ps, unsigned int intf) +{ + if (intf >= 8*sizeof(ps->ifclaimed)) + return -EINVAL; + if (test_bit(intf, &ps->ifclaimed)) + return 0; + /* if not yet claimed, claim it for the driver */ + printk(KERN_WARNING "usbdevfs: process %d (%s) did not claim interface %u before use\n", + current->pid, current->comm, intf); + return claimintf(ps, intf); +} + +static int findintfep(struct usb_device *dev, unsigned int ep) +{ + unsigned int i, j, e; + struct usb_interface *iface; + struct usb_interface_descriptor *alts; + struct usb_endpoint_descriptor *endpt; + + if (ep & ~(USB_DIR_IN|0xf)) + return -EINVAL; + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { + iface = &dev->actconfig->interface[i]; + for (j = 0; j < iface->num_altsetting; j++) { + alts = &iface->altsetting[j]; + for (e = 0; e < alts->bNumEndpoints; e++) { + endpt = &alts->endpoint[e]; + if (endpt->bEndpointAddress == ep) + return i; + } + } + } + return -ENOENT; +} + +static int findintfif(struct usb_device *dev, unsigned int ifn) +{ + unsigned int i, j; + struct usb_interface *iface; + struct usb_interface_descriptor *alts; + + if (ifn & ~0xff) + return -EINVAL; + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { + iface = &dev->actconfig->interface[i]; + for (j = 0; j < iface->num_altsetting; j++) { + alts = &iface->altsetting[j]; + if (alts->bInterfaceNumber == ifn) + return i; + } + } + return -ENOENT; +} + +extern struct list_head usb_driver_list; + +static int finddriver(struct usb_driver **driver, char *name) +{ + struct list_head *tmp; + + tmp = usb_driver_list.next; + while (tmp != &usb_driver_list) { + struct usb_driver *d = list_entry(tmp, struct usb_driver, + driver_list); + + if (!strcmp(d->name, name)) { + *driver = d; + return 0; + } + + tmp = tmp->next; + } + + return -EINVAL; +} + +/* + * file operations + */ +static int usbdev_open(struct inode *inode, struct file *file) +{ + struct usb_device *dev; + struct dev_state *ps; + int ret; + + /* + * no locking necessary here, as both sys_open (actually filp_open) + * and the hub thread have the kernel lock + * (still acquire the kernel lock for safety) + */ + lock_kernel(); + ret = -ENOENT; + if (ITYPE(inode->i_ino) != IDEVICE) + goto out; + dev = inode->u.usbdev_i.p.dev; + if (!dev) + goto out; + ret = -ENOMEM; + if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL))) + goto out; + ret = 0; + ps->dev = dev; + ps->file = file; + spin_lock_init(&ps->lock); + INIT_LIST_HEAD(&ps->async_pending); + INIT_LIST_HEAD(&ps->async_completed); + init_waitqueue_head(&ps->wait); + init_rwsem(&ps->devsem); + ps->discsignr = 0; + ps->disctask = current; + ps->disccontext = NULL; + ps->ifclaimed = 0; + wmb(); + list_add_tail(&ps->list, &dev->filelist); + file->private_data = ps; + out: + unlock_kernel(); + return ret; +} + +static int usbdev_release(struct inode *inode, struct file *file) +{ + struct dev_state *ps = (struct dev_state *)file->private_data; + unsigned int i; + + lock_kernel(); + list_del(&ps->list); + INIT_LIST_HEAD(&ps->list); + if (ps->dev) { + for (i = 0; ps->ifclaimed && i < 8*sizeof(ps->ifclaimed); i++) + if (test_bit(i, &ps->ifclaimed)) + releaseintf(ps, i); + } + unlock_kernel(); + destroy_all_async(ps); + kfree(ps); + return 0; +} + +static int proc_control(struct dev_state *ps, void *arg) +{ + struct usb_device *dev = ps->dev; + struct usbdevfs_ctrltransfer ctrl; + unsigned int tmo; + unsigned char *tbuf; + int i, ret; + + copy_from_user_ret(&ctrl, (void *)arg, sizeof(ctrl), -EFAULT); + switch (ctrl.requesttype & 0x1f) { + case USB_RECIP_ENDPOINT: + if ((ret = findintfep(ps->dev, ctrl.index & 0xff)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + break; + + case USB_RECIP_INTERFACE: + if ((ret = findintfif(ps->dev, ctrl.index & 0xff)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + break; + } + if (ctrl.length > PAGE_SIZE) + return -EINVAL; + if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL))) + return -ENOMEM; + tmo = (ctrl.timeout * HZ + 999) / 1000; + if (ctrl.requesttype & 0x80) { + if (ctrl.length && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.length)) { + free_page((unsigned long)tbuf); + return -EINVAL; + } + i = my_usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.request, ctrl.requesttype, + ctrl.value, ctrl.index, tbuf, ctrl.length, tmo); + if ((i > 0) && ctrl.length) { + copy_to_user_ret(ctrl.data, tbuf, ctrl.length, -EFAULT); + } + } else { + if (ctrl.length) { + copy_from_user_ret(tbuf, ctrl.data, ctrl.length, -EFAULT); + } + i = my_usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.request, ctrl.requesttype, + ctrl.value, ctrl.index, tbuf, ctrl.length, tmo); + } + free_page((unsigned long)tbuf); + if (i<0) { + printk(KERN_DEBUG "usbdevfs: USBDEVFS_CONTROL failed dev %d rqt %u rq %u len %u ret %d\n", + dev->devnum, ctrl.requesttype, ctrl.request, ctrl.length, i); + } + return i; +} + +static int proc_bulk(struct dev_state *ps, void *arg) +{ + struct usb_device *dev = ps->dev; + struct usbdevfs_bulktransfer bulk; + unsigned int tmo, len1, pipe; + int len2; + unsigned char *tbuf; + int i, ret; + + copy_from_user_ret(&bulk, (void *)arg, sizeof(bulk), -EFAULT); + if ((ret = findintfep(ps->dev, bulk.ep)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + if (bulk.ep & USB_DIR_IN) + pipe = usb_rcvbulkpipe(dev, bulk.ep & 0x7f); + else + pipe = usb_sndbulkpipe(dev, bulk.ep & 0x7f); + if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN))) + return -EINVAL; + len1 = bulk.len; + if (len1 > PAGE_SIZE) + len1 = PAGE_SIZE; + if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL))) + return -ENOMEM; + tmo = (bulk.timeout * HZ + 999) / 1000; + if (bulk.ep & 0x80) { + if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) { + free_page((unsigned long)tbuf); + return -EINVAL; + } + i = my_usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); + if (!i && len2) { + copy_to_user_ret(bulk.data, tbuf, len2, -EFAULT); + } + } else { + if (len1) { + copy_from_user_ret(tbuf, bulk.data, len1, -EFAULT); + } + i = my_usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); + } + free_page((unsigned long)tbuf); + if (i < 0) { + printk(KERN_WARNING "usbdevfs: USBDEVFS_BULK failed dev %d ep 0x%x len %u ret %d\n", + dev->devnum, bulk.ep, bulk.len, i); + return i; + } + return len2; +} + +static int proc_resetep(struct dev_state *ps, void *arg) +{ + unsigned int ep; + int ret; + + get_user_ret(ep, (unsigned int *)arg, -EFAULT); + if ((ret = findintfep(ps->dev, ep)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + usb_settoggle(ps->dev, ep & 0xf, !(ep & USB_DIR_IN), 0); + return 0; +} + +static int proc_getdriver(struct dev_state *ps, void *arg) +{ + struct usbdevfs_getdriver gd; + struct usb_interface *interface; + int ret; + + copy_from_user_ret(&gd, arg, sizeof(gd), -EFAULT); + if ((ret = findintfif(ps->dev, gd.interface)) < 0) + return ret; + interface = usb_ifnum_to_if(ps->dev, gd.interface); + if (!interface) + return -EINVAL; + if (!interface->driver) + return -ENODATA; + strcpy(gd.driver, interface->driver->name); + copy_to_user_ret(arg, &gd, sizeof(gd), -EFAULT); + return 0; +} + +static int proc_connectinfo(struct dev_state *ps, void *arg) +{ + struct usbdevfs_connectinfo ci; + + ci.devnum = ps->dev->devnum; + ci.slow = ps->dev->slow; + copy_to_user_ret(arg, &ci, sizeof(ci), -EFAULT); + return 0; +} + +static int proc_resetdevice(struct dev_state *ps) +{ + int i, ret; + + ret = usb_reset_device(ps->dev); + if (ret < 0) + return ret; + + for (i = 0; i < ps->dev->actconfig->bNumInterfaces; i++) { + struct usb_interface *intf = &ps->dev->actconfig->interface[i]; + + /* Don't simulate interfaces we've claimed */ + if (test_bit(i, &ps->ifclaimed)) + continue; + + if (intf->driver) { + down(&intf->driver->serialize); + intf->driver->disconnect(ps->dev, intf->private_data); + intf->driver->probe(ps->dev, i); + up(&intf->driver->serialize); + } + } + + return 0; +} + +static int proc_setintf(struct dev_state *ps, void *arg) +{ + struct usbdevfs_setinterface setintf; + struct usb_interface *interface; + int ret; + + copy_from_user_ret(&setintf, arg, sizeof(setintf), -EFAULT); + if ((ret = findintfif(ps->dev, setintf.interface)) < 0) + return ret; + interface = usb_ifnum_to_if(ps->dev, setintf.interface); + if (!interface) + return -EINVAL; + if (interface->driver) { + if ((ret = checkintf(ps, ret))) + return ret; + } + if (usb_set_interface(ps->dev, setintf.interface, setintf.altsetting)) + return -EINVAL; + return 0; +} + +static int proc_setconfig(struct dev_state *ps, void *arg) +{ + unsigned int u; + + get_user_ret(u, (unsigned int *)arg, -EFAULT); + if (usb_set_configuration(ps->dev, u) < 0) + return -EINVAL; + return 0; +} + +static int proc_submiturb(struct dev_state *ps, void *arg) +{ + struct usbdevfs_urb uurb; + struct usbdevfs_iso_packet_desc *isopkt = NULL; + struct async *as; + unsigned int u, totlen, isofrmlen; + int ret; + + copy_from_user_ret(&uurb, arg, sizeof(uurb), -EFAULT); + if (uurb.flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_DISABLE_SPD)) + return -EINVAL; + if (!uurb.buffer) + return -EINVAL; + if (uurb.signr != 0 && (uurb.signr < SIGRTMIN || uurb.signr > SIGRTMAX)) + return -EINVAL; + if ((ret = findintfep(ps->dev, uurb.endpoint)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + switch(uurb.type) { + case USBDEVFS_URB_TYPE_BULK: + uurb.number_of_packets = 0; + if (uurb.buffer_length > 16384) + return -EINVAL; + if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length)) + return -EFAULT; + break; + + case USBDEVFS_URB_TYPE_ISO: + /* arbitrary limit */ + if (uurb.number_of_packets < 1 || uurb.number_of_packets > 128) + return -EINVAL; + isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb.number_of_packets; + if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL))) + return -ENOMEM; + if (copy_from_user(isopkt, &((struct usbdevfs_urb *)arg)->iso_frame_desc, isofrmlen)) { + kfree(isopkt); + return -EFAULT; + } + for (totlen = u = 0; u < uurb.number_of_packets; u++) { + if (isopkt[u].length > 1023) { + kfree(isopkt); + return -EINVAL; + } + totlen += isopkt[u].length; + } + if (totlen > 32768) { + kfree(isopkt); + return -ENOMEM; + } + uurb.buffer_length = totlen; + break; + + default: + return -EINVAL; + } + if (!(as = alloc_async(uurb.number_of_packets))) { + if (isopkt) + kfree(isopkt); + return -ENOMEM; + } + if (!(as->urb.transfer_buffer = kmalloc(uurb.buffer_length, GFP_KERNEL))) { + if (isopkt) + kfree(isopkt); + free_async(as); + return -ENOMEM; + } + as->urb.next = NULL; + as->urb.dev = ps->dev; + as->urb.pipe = (uurb.type << 30) | __create_pipe(ps->dev, uurb.endpoint & 0xf) | (uurb.endpoint & USB_DIR_IN); + as->urb.transfer_flags = uurb.flags; + as->urb.transfer_buffer_length = uurb.buffer_length; + as->urb.start_frame = uurb.start_frame; + as->urb.number_of_packets = uurb.number_of_packets; + as->urb.context = as; + as->urb.complete = async_completed; + for (totlen = u = 0; u < uurb.number_of_packets; u++) { + as->urb.iso_frame_desc[u].offset = totlen; + as->urb.iso_frame_desc[u].length = isopkt[u].length; + totlen += isopkt[u].length; + } + if (isopkt) + kfree(isopkt); + as->ps = ps; + as->userurb = arg; + if (uurb.endpoint & USB_DIR_IN) + as->userbuffer = uurb.buffer; + else + as->userbuffer = NULL; + as->signr = uurb.signr; + as->task = current; + if (!(uurb.endpoint & USB_DIR_IN)) { + if (copy_from_user(as->urb.transfer_buffer, uurb.buffer, as->urb.transfer_buffer_length)) { + free_async(as); + return -EFAULT; + } + } + async_newpending(as); + if ((ret = usb_submit_urb(&as->urb))) { + printk(KERN_DEBUG "usbdevfs: usb_submit_urb returned %d\n", ret); + async_removepending(as); + free_async(as); + return ret; + } + return 0; +} + +static int proc_unlinkurb(struct dev_state *ps, void *arg) +{ + struct async *as; + + as = async_getpending(ps, arg); + if (!as) + return -EINVAL; + usb_unlink_urb(&as->urb); + return 0; +} + +static int processcompl(struct async *as) +{ + unsigned int i; + + if (as->userbuffer) + if (copy_to_user(as->userbuffer, as->urb.transfer_buffer, as->urb.transfer_buffer_length)) + return -EFAULT; + put_user_ret(as->urb.status, &((struct usbdevfs_urb *)as->userurb)->status, -EFAULT); + put_user_ret(as->urb.actual_length, &((struct usbdevfs_urb *)as->userurb)->actual_length, -EFAULT); + put_user_ret(as->urb.error_count, &((struct usbdevfs_urb *)as->userurb)->error_count, -EFAULT); + if (!(usb_pipeisoc(as->urb.pipe))) + return 0; + for (i = 0; i < as->urb.number_of_packets; i++) { + put_user_ret(as->urb.iso_frame_desc[i].actual_length, + &((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].actual_length, + -EFAULT); + put_user_ret(as->urb.iso_frame_desc[i].status, + &((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].status, + -EFAULT); + } + return 0; +} + +static int proc_reapurb(struct dev_state *ps, void *arg) +{ + DECLARE_WAITQUEUE(wait, current); + struct async *as = NULL; + void *addr; + int ret; + + add_wait_queue(&ps->wait, &wait); + while (ps->dev) { + __set_current_state(TASK_INTERRUPTIBLE); + if ((as = async_getcompleted(ps))) + break; + if (signal_pending(current)) + break; + up_read(&ps->devsem); + schedule(); + down_read(&ps->devsem); + } + remove_wait_queue(&ps->wait, &wait); + set_current_state(TASK_RUNNING); + if (as) { + ret = processcompl(as); + addr = as->userurb; + free_async(as); + if (ret) + return ret; + put_user_ret(addr, (void **)arg, -EFAULT); + return 0; + } + if (signal_pending(current)) + return -EINTR; + return -EIO; +} + +static int proc_reapurbnonblock(struct dev_state *ps, void *arg) +{ + struct async *as; + void *addr; + int ret; + + if (!(as = async_getcompleted(ps))) + return -EAGAIN; + ret = processcompl(as); + addr = as->userurb; + free_async(as); + if (ret) + return ret; + put_user_ret(addr, (void **)arg, -EFAULT); + return 0; +} + +static int proc_disconnectsignal(struct dev_state *ps, void *arg) +{ + struct usbdevfs_disconnectsignal ds; + + copy_from_user_ret(&ds, arg, sizeof(ds), -EFAULT); + if (ds.signr != 0 && (ds.signr < SIGRTMIN || ds.signr > SIGRTMAX)) + return -EINVAL; + ps->discsignr = ds.signr; + ps->disccontext = ds.context; + return 0; +} + +static int proc_claiminterface(struct dev_state *ps, void *arg) +{ + unsigned int intf; + int ret; + + get_user_ret(intf, (unsigned int *)arg, -EFAULT); + if ((ret = findintfif(ps->dev, intf)) < 0) + return ret; + return claimintf(ps, ret); +} + +static int proc_releaseinterface(struct dev_state *ps, void *arg) +{ + unsigned int intf; + int ret; + + get_user_ret(intf, (unsigned int *)arg, -EFAULT); + if ((ret = findintfif(ps->dev, intf)) < 0) + return ret; + return releaseintf(ps, intf); +} + +static int proc_ioctl (struct dev_state *ps, void *arg) +{ + struct usbdevfs_ioctl ctrl; + int size; + void *buf = 0; + int retval = 0; + + /* get input parameters and alloc buffer */ + copy_from_user_ret (&ctrl, (void *) arg, sizeof (ctrl), -EFAULT); + if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) { + if ((buf = kmalloc (size, GFP_KERNEL)) == 0) + return -ENOMEM; + if ((_IOC_DIR (ctrl.ioctl_code) & _IOC_WRITE) != 0 + && copy_from_user (buf, ctrl.data, size) != 0) { + kfree (buf); + return -EFAULT; + } else + memset (arg, size, 0); + } + + /* ioctl to device */ + if (ctrl.ifno < 0) { + switch (ctrl.ioctl_code) { + /* access/release token for issuing control messages + * ask a particular driver to bind/unbind, ... etc + */ + } + retval = -ENOSYS; + + /* ioctl to the driver which has claimed a given interface */ + } else { + struct usb_interface *ifp = 0; + if (!ps->dev) + retval = -ENODEV; + else if (ctrl.ifno >= ps->dev->actconfig->bNumInterfaces) + retval = -EINVAL; + else { + if (!(ifp = usb_ifnum_to_if (ps->dev, ctrl.ifno))) + retval = -EINVAL; + else if (ifp->driver == 0 || ifp->driver->ioctl == 0) + retval = -ENOSYS; + } + if (retval == 0) + /* ifno might usefully be passed ... */ + retval = ifp->driver->ioctl (ps->dev, ctrl.ioctl_code, buf); + /* size = min (size, retval)? */ + } + + /* cleanup and return */ + if (retval >= 0 + && (_IOC_DIR (ctrl.ioctl_code) & _IOC_READ) != 0 + && size > 0 + && copy_to_user (ctrl.data, buf, size) != 0) + retval = -EFAULT; + if (buf != 0) + kfree (buf); + return retval; +} + +static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct dev_state *ps = (struct dev_state *)file->private_data; + int ret = -ENOIOCTLCMD; + + if (!(file->f_mode & FMODE_WRITE)) + return -EPERM; + down_read(&ps->devsem); + if (!ps->dev) { + up_read(&ps->devsem); + return -ENODEV; + } + switch (cmd) { + case USBDEVFS_CONTROL: + ret = proc_control(ps, (void *)arg); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_BULK: + ret = proc_bulk(ps, (void *)arg); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_RESETEP: + ret = proc_resetep(ps, (void *)arg); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_RESET: + ret = proc_resetdevice(ps); + break; + + case USBDEVFS_GETDRIVER: + ret = proc_getdriver(ps, (void *)arg); + break; + + case USBDEVFS_CONNECTINFO: + ret = proc_connectinfo(ps, (void *)arg); + break; + + case USBDEVFS_SETINTERFACE: + ret = proc_setintf(ps, (void *)arg); + break; + + case USBDEVFS_SETCONFIGURATION: + ret = proc_setconfig(ps, (void *)arg); + break; + + case USBDEVFS_SUBMITURB: + ret = proc_submiturb(ps, (void *)arg); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_DISCARDURB: + ret = proc_unlinkurb(ps, (void *)arg); + break; + + case USBDEVFS_REAPURB: + ret = proc_reapurb(ps, (void *)arg); + break; + + case USBDEVFS_REAPURBNDELAY: + ret = proc_reapurbnonblock(ps, (void *)arg); + break; + + case USBDEVFS_DISCSIGNAL: + ret = proc_disconnectsignal(ps, (void *)arg); + break; + + case USBDEVFS_CLAIMINTERFACE: + ret = proc_claiminterface(ps, (void *)arg); + break; + + case USBDEVFS_RELEASEINTERFACE: + ret = proc_releaseinterface(ps, (void *)arg); + break; + + case USBDEVFS_IOCTL: + ret = proc_ioctl(ps, (void *) arg); + break; + } + up_read(&ps->devsem); + if (ret >= 0) + inode->i_atime = CURRENT_TIME; + return ret; +} + +/* No kernel lock - fine */ +static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait) +{ + struct dev_state *ps = (struct dev_state *)file->private_data; + unsigned int mask = 0; + + poll_wait(file, &ps->wait, wait); + if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed)) + mask |= POLLOUT | POLLWRNORM; + if (!ps->dev) + mask |= POLLERR | POLLHUP; + return mask; +} + +struct file_operations usbdevfs_device_file_operations = { + llseek: usbdev_lseek, + read: usbdev_read, + poll: usbdev_poll, + ioctl: usbdev_ioctl, + open: usbdev_open, + release: usbdev_release, +}; diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/drivers.c linux-2.2.17/drivers/usb/drivers.c --- linux-2.2.17-orig/drivers/usb/drivers.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/drivers.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,119 @@ +/* + * drivers.c + * (C) Copyright 1999 Randy Dunlap. + * (C) Copyright 1999, 2000 Thomas Sailer . (proc file per device) + * (C) Copyright 1999 Deti Fliegl (new USB architecture) + * + * $id$ + * + * 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 + * + ************************************************************* + * + * 1999-12-16: Thomas Sailer + * Converted the whole proc stuff to real + * read methods. Now not the whole device list needs to fit + * into one page, only the device list for one bus. + * Added a poll method to /proc/bus/usb/devices, to wake + * up an eventual usbd + * 2000-01-04: Thomas Sailer + * Turned into its own filesystem + * + * $Id: drivers.c,v 1.3 2000/01/11 13:58:24 tom Exp $ + */ + +#include +#include +#include +#include +#include + +/*****************************************************************/ + +/* + * Dump usb_driver_list. + * + * We now walk the list of registered USB drivers. + */ +static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) +{ + struct list_head *tmp = usb_driver_list.next; + char *page, *start, *end; + ssize_t ret = 0; + unsigned int pos, len; + + if (*ppos < 0) + return -EINVAL; + if (nbytes <= 0) + return 0; + if (!access_ok(VERIFY_WRITE, buf, nbytes)) + return -EFAULT; + if (!(page = (char*) __get_free_page(GFP_KERNEL))) + return -ENOMEM; + start = page; + end = page + (PAGE_SIZE - 100); + pos = *ppos; + for (; tmp != &usb_driver_list; tmp = tmp->next) { + struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list); + int minor = driver->fops ? driver->minor : -1; + if (minor == -1) + start += sprintf (start, " %s\n", driver->name); + else + start += sprintf (start, "%3d-%3d: %s\n", minor, minor + 15, driver->name); + if (start > end) { + start += sprintf(start, "(truncated)\n"); + break; + } + } + if (start == page) + start += sprintf(start, "(none)\n"); + len = start - page; + if (len > pos) { + len -= pos; + if (len > nbytes) + len = nbytes; + ret = len; + if (copy_to_user(buf, page + pos, len)) + ret = -EFAULT; + else + *ppos += len; + } + free_page((unsigned long)page); + return ret; +} + +static loff_t usb_driver_lseek(struct file * file, loff_t offset, int orig) +{ + switch (orig) { + case 0: + file->f_pos = offset; + return file->f_pos; + + case 1: + file->f_pos += offset; + return file->f_pos; + + case 2: + return -EINVAL; + + default: + return -EINVAL; + } +} + +struct file_operations usbdevfs_drivers_fops = { + llseek: usb_driver_lseek, + read: usb_driver_read, +}; diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/dsbr100.c linux-2.2.17/drivers/usb/dsbr100.c --- linux-2.2.17-orig/drivers/usb/dsbr100.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/dsbr100.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,354 @@ +/* A driver for the D-Link DSB-R100 USB radio. The R100 plugs + into both the USB and an analog audio input, so this thing + only deals with initialisation and frequency setting, the + audio data has to be handled by a sound driver. + + Major issue: I can't find out where the device reports the signal + strength, and indeed the windows software appearantly just looks + at the stereo indicator as well. So, scanning will only find + stereo stations. Sad, but I can't help it. + + Also, the windows program sends oodles of messages over to the + device, and I couldn't figure out their meaning. My suspicion + is that they don't have any:-) + + You might find some interesting stuff about this module at + http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr + + Copyright (c) 2000 Markus Demleitner + + 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 + + History: + + Version 0.23: + Markus: Sign extension bug fixed by declaring transfer_buffer unsigned + + Version 0.22: + Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns, + thanks to Mike Cox for pointing the problem out. + + Version 0.21: + Markus: Minor cleanup, warnings if something goes wrong, lame attempt + to adhere to Documentation/CodingStyle + + Version 0.2: + Brad Hards : Fixes to make it work as non-module + Markus: Copyright clarification + + Version 0.01: Markus: initial release + +*/ + + +#include +#include +#include +#include +#include +#include +#include + +#define DSB100_VENDOR 0x04b4 +#define DSB100_PRODUCT 0x1002 + +#define TB_LEN 16 + +static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum); +static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr); +static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd, + void *arg); +static int usb_dsbr100_open(struct video_device *dev, int flags); +static void usb_dsbr100_close(struct video_device *dev); + + +typedef struct +{ struct urb readurb,writeurb; + struct usb_device *dev; + unsigned char transfer_buffer[TB_LEN]; + int curfreq; + int stereo; + int ifnum; +} usb_dsbr100; + + +static struct video_device usb_dsbr100_radio= +{ + "D-Link DSB R-100 USB radio", + VID_TYPE_TUNER, + VID_HARDWARE_AZTECH, + usb_dsbr100_open, + usb_dsbr100_close, + NULL, /* Can't read (no capture ability) */ + NULL, /* Can't write */ + NULL, /* No poll */ + usb_dsbr100_ioctl, + NULL, + NULL +}; + +static int users = 0; + +static struct usb_driver usb_dsbr100_driver = { + name: "dsbr100", + probe: usb_dsbr100_probe, + disconnect: usb_dsbr100_disconnect, + driver_list: {NULL,NULL}, + fops: NULL, + minor: 0 +}; + + +static int dsbr100_start(usb_dsbr100 *radio) +{ + if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300)<0) + return -1; + return (radio->transfer_buffer)[0]; +} + + +static int dsbr100_stop(usb_dsbr100 *radio) +{ + if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300)<0) + return -1; + return (radio->transfer_buffer)[0]; +} + + +static int dsbr100_setfreq(usb_dsbr100 *radio, int freq) +{ + freq = (freq*80)/16+856; + if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x01, 0xC0, (freq&0xff00)>>8, freq&0xff, + radio->transfer_buffer, 8, 300)<0 + || usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) { + radio->stereo = -1; + return -1; + } + radio->stereo = ! ((radio->transfer_buffer)[0]&0x01); + return (radio->transfer_buffer)[0]; +} + +static void dsbr100_getstat(usb_dsbr100 *radio) +{ + if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0) + radio->stereo = -1; + else + radio->stereo = ! (radio->transfer_buffer[0]&0x01); +} + + +static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum) +{ + usb_dsbr100 *radio; + + if (dev->descriptor.idVendor!=DSB100_VENDOR || + dev->descriptor.idProduct!=DSB100_PRODUCT) + return NULL; + if (!(radio = kmalloc(sizeof(usb_dsbr100),GFP_KERNEL))) + return NULL; + usb_dsbr100_radio.priv = radio; + radio->dev = dev; + radio->ifnum = ifnum; + radio->curfreq = 1454; + return (void*)radio; +} + +static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr) +{ + usb_dsbr100 *radio=ptr; + + if (users) + return; + kfree(radio); + usb_dsbr100_radio.priv = NULL; +} + +static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd, + void *arg) +{ + usb_dsbr100 *radio=dev->priv; + + if (!radio) + return -EINVAL; + + switch(cmd) + { + case VIDIOCGCAP: { + struct video_capability v; + v.type=VID_TYPE_TUNER; + v.channels=1; + v.audios=1; + /* No we don't do pictures */ + v.maxwidth=0; + v.maxheight=0; + v.minwidth=0; + v.minheight=0; + strcpy(v.name, "D-Link R-100 USB Radio"); + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCGTUNER: { + struct video_tuner v; + dsbr100_getstat(radio); + if(copy_from_user(&v, arg,sizeof(v))!=0) + return -EFAULT; + if(v.tuner) /* Only 1 tuner */ + return -EINVAL; + v.rangelow = 87*16; + v.rangehigh = 108*16; + v.flags = VIDEO_TUNER_LOW; + v.mode = VIDEO_MODE_AUTO; + v.signal = radio->stereo*0x7000; + /* Don't know how to get signal strength */ + v.flags |= VIDEO_TUNER_STEREO_ON*radio->stereo; + strcpy(v.name, "DSB R-100"); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSTUNER: { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.tuner!=0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + if (radio->curfreq==-1) + return -EINVAL; + if(copy_to_user(arg, &(radio->curfreq), + sizeof(radio->curfreq))) + return -EFAULT; + return 0; + + case VIDIOCSFREQ: + if(copy_from_user(&(radio->curfreq), arg, + sizeof(radio->curfreq))) + return -EFAULT; + if (dsbr100_setfreq(radio, radio->curfreq)==-1) + warn("set frequency failed"); + return 0; + + case VIDIOCGAUDIO: { + struct video_audio v; + memset(&v,0, sizeof(v)); + v.flags|=VIDEO_AUDIO_MUTABLE; + v.mode=VIDEO_SOUND_STEREO; + v.volume=1; + v.step=1; + strcpy(v.name, "Radio"); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: { + struct video_audio v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + + if(v.flags&VIDEO_AUDIO_MUTE) { + if (dsbr100_stop(radio)==-1) + warn("radio did not respond properly"); + } + else + if (dsbr100_start(radio)==-1) + warn("radio did not respond properly"); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + + +static int usb_dsbr100_open(struct video_device *dev, int flags) +{ + usb_dsbr100 *radio=dev->priv; + + if (! radio) { + warn("radio not initialised"); + return -EAGAIN; + } + if(users) + { + warn("radio in use"); + return -EBUSY; + } + users++; + MOD_INC_USE_COUNT; + if (dsbr100_start(radio)<0) + warn("radio did not start up properly"); + dsbr100_setfreq(radio,radio->curfreq); + return 0; +} + +static void usb_dsbr100_close(struct video_device *dev) +{ + usb_dsbr100 *radio=dev->priv; + + if (!radio) + return; + users--; + dsbr100_stop(radio); + MOD_DEC_USE_COUNT; +} + +int __init dsbr100_init(void) +{ + usb_dsbr100_radio.priv = NULL; + usb_register(&usb_dsbr100_driver); + if (video_register_device(&usb_dsbr100_radio,VFL_TYPE_RADIO)==-1) { + warn("couldn't register video device"); + return -EINVAL; + } + return 0; +} + +int __init init_module(void) +{ + return dsbr100_init(); +} + +void cleanup_module(void) +{ + usb_dsbr100 *radio=usb_dsbr100_radio.priv; + + if (radio) + dsbr100_stop(radio); + video_unregister_device(&usb_dsbr100_radio); + usb_deregister(&usb_dsbr100_driver); +} + +/* +vi: ts=8 +Sigh. Of course, I am one of the ts=2 heretics, but Linus' wish is +my command. +*/ diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/evdev.c linux-2.2.17/drivers/usb/evdev.c --- linux-2.2.17-orig/drivers/usb/evdev.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/evdev.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,349 @@ +/* + * $Id: evdev.c,v 1.8 2000/05/29 09:01:52 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Event char devices, giving access to raw input device events. + * + * Sponsored by SuSE + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#define EVDEV_MINOR_BASE 64 +#define EVDEV_MINORS 32 +#define EVDEV_BUFFER_SIZE 64 + +#include +#include +#include +#include +#include +#include + +struct evdev { + int used; + int open; + int minor; + struct input_handle handle; + wait_queue_head_t wait; + devfs_handle_t devfs; + struct evdev_list *list; +}; + +struct evdev_list { + struct input_event buffer[EVDEV_BUFFER_SIZE]; + int head; + int tail; + struct fasync_struct *fasync; + struct evdev *evdev; + struct evdev_list *next; +}; + +static struct evdev *evdev_table[EVDEV_MINORS] = { NULL, /* ... */ }; + +static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) +{ + struct evdev *evdev = handle->private; + struct evdev_list *list = evdev->list; + + while (list) { + + get_fast_time(&list->buffer[list->head].time); + list->buffer[list->head].type = type; + list->buffer[list->head].code = code; + list->buffer[list->head].value = value; + list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1); + + kill_fasync(list->fasync, SIGIO); + + list = list->next; + } + + wake_up_interruptible(&evdev->wait); +} + +static int evdev_fasync(int fd, struct file *file, int on) +{ + int retval; + struct evdev_list *list = file->private_data; + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; +} + +static int evdev_release(struct inode * inode, struct file * file) +{ + struct evdev_list *list = file->private_data; + struct evdev_list **listptr = &list->evdev->list; + + evdev_fasync(-1, file, 0); + + while (*listptr && (*listptr != list)) + listptr = &((*listptr)->next); + *listptr = (*listptr)->next; + + if (!--list->evdev->open) + input_close_device(&list->evdev->handle); + + if (!--list->evdev->used) { + input_unregister_minor(list->evdev->devfs); + evdev_table[list->evdev->minor] = NULL; + kfree(list->evdev); + } + + kfree(list); + + return 0; +} + +static int evdev_open(struct inode * inode, struct file * file) +{ + struct evdev_list *list; + int i = MINOR(inode->i_rdev) - EVDEV_MINOR_BASE; + + if (i > EVDEV_MINORS || !evdev_table[i]) + return -ENODEV; + + if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL))) { + return -ENOMEM; + } + memset(list, 0, sizeof(struct evdev_list)); + + list->evdev = evdev_table[i]; + list->next = evdev_table[i]->list; + evdev_table[i]->list = list; + + file->private_data = list; + + list->evdev->used++; + + if (!list->evdev->open++) + input_open_device(&list->evdev->handle); + + return 0; +} + +static ssize_t evdev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static ssize_t evdev_read(struct file * file, char * buffer, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct evdev_list *list = file->private_data; + int retval = 0; + + if (list->head == list->tail) { + + add_wait_queue(&list->evdev->wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + while (list->head == list->tail) { + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + schedule(); + } + + current->state = TASK_RUNNING; + remove_wait_queue(&list->evdev->wait, &wait); + } + + if (retval) + return retval; + + while (list->head != list->tail && retval + sizeof(struct input_event) <= count) { + if (copy_to_user(buffer + retval, list->buffer + list->tail, + sizeof(struct input_event))) return -EFAULT; + list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1); + retval += sizeof(struct input_event); + } + + return retval; +} + +/* No kernel lock - fine */ +static unsigned int evdev_poll(struct file *file, poll_table *wait) +{ + struct evdev_list *list = file->private_data; + poll_wait(file, &list->evdev->wait, wait); + if (list->head != list->tail) + return POLLIN | POLLRDNORM; + return 0; +} + +static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct evdev_list *list = file->private_data; + struct evdev *evdev = list->evdev; + struct input_dev *dev = evdev->handle.dev; + int retval; + + switch (cmd) { + + case EVIOCGVERSION: + return put_user(EV_VERSION, (int *) arg); + + case EVIOCGID: + if ((retval = put_user(dev->idbus, ((short *) arg) + 0))) return retval; + if ((retval = put_user(dev->idvendor, ((short *) arg) + 1))) return retval; + if ((retval = put_user(dev->idproduct, ((short *) arg) + 2))) return retval; + if ((retval = put_user(dev->idversion, ((short *) arg) + 3))) return retval; + return 0; + + default: + + if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ) + return -EINVAL; + + if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { + + long *bits; + int len; + + switch (_IOC_NR(cmd) & EV_MAX) { + case 0: bits = dev->evbit; len = EV_MAX; break; + case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; + case EV_REL: bits = dev->relbit; len = REL_MAX; break; + case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; + case EV_LED: bits = dev->ledbit; len = LED_MAX; break; + case EV_SND: bits = dev->sndbit; len = SND_MAX; break; + default: return -EINVAL; + } + len = NBITS(len) * sizeof(long); + if (len > _IOC_SIZE(cmd)) { + printk(KERN_WARNING "evdev.c: Truncating bitfield length from %d to %d\n", + len, _IOC_SIZE(cmd)); + len = _IOC_SIZE(cmd); + } + return copy_to_user((char *) arg, bits, len) ? -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) { + int len; + if (!dev->name) return 0; + len = strlen(dev->name) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user((char *) arg, dev->name, len) ? -EFAULT : len; + } + + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { + + int t = _IOC_NR(cmd) & ABS_MAX; + + if ((retval = put_user(dev->abs[t], ((int *) arg) + 0))) return retval; + if ((retval = put_user(dev->absmin[t], ((int *) arg) + 1))) return retval; + if ((retval = put_user(dev->absmax[t], ((int *) arg) + 2))) return retval; + if ((retval = put_user(dev->absfuzz[t], ((int *) arg) + 3))) return retval; + if ((retval = put_user(dev->absflat[t], ((int *) arg) + 4))) return retval; + + return 0; + } + } + return -EINVAL; +} + +static struct file_operations evdev_fops = { + read: evdev_read, + write: evdev_write, + poll: evdev_poll, + open: evdev_open, + release: evdev_release, + ioctl: evdev_ioctl, + fasync: evdev_fasync, +}; + +static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev) +{ + struct evdev *evdev; + int minor; + + for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); + if (evdev_table[minor]) { + printk(KERN_ERR "evdev: no more free evdev devices\n"); + return NULL; + } + + if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL))) + return NULL; + memset(evdev, 0, sizeof(struct evdev)); + + init_waitqueue_head(&evdev->wait); + + evdev->minor = minor; + evdev_table[minor] = evdev; + + evdev->handle.dev = dev; + evdev->handle.handler = handler; + evdev->handle.private = evdev; + + evdev->used = 1; + + evdev->devfs = input_register_minor("event%d", minor, EVDEV_MINOR_BASE); + + printk(KERN_INFO "event%d: Event device for input%d\n", minor, dev->number); + + return &evdev->handle; +} + +static void evdev_disconnect(struct input_handle *handle) +{ + struct evdev *evdev = handle->private; + + if (evdev->open) + input_close_device(handle); + + if (!--evdev->used) { + input_unregister_minor(evdev->devfs); + evdev_table[evdev->minor] = NULL; + kfree(evdev); + } +} + +static struct input_handler evdev_handler = { + event: evdev_event, + connect: evdev_connect, + disconnect: evdev_disconnect, + fops: &evdev_fops, + minor: EVDEV_MINOR_BASE, +}; + +static int __init evdev_init(void) +{ + input_register_handler(&evdev_handler); + return 0; +} + +static void __exit evdev_exit(void) +{ + input_unregister_handler(&evdev_handler); +} + +module_init(evdev_init); +module_exit(evdev_exit); diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/hid-debug.h linux-2.2.17/drivers/usb/hid-debug.h --- linux-2.2.17-orig/drivers/usb/hid-debug.h Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/hid-debug.h Sun Sep 24 04:26:23 2000 @@ -0,0 +1,235 @@ +/* + * $Id: hid-debug.h,v 1.2 2000/05/29 10:54:53 vojtech Exp $ + * + * (c) 1999 Andreas Gal + * (c) 2000 Vojtech Pavlik + * + * Some debug stuff for the HID parser. + * + * Sponsored by SuSE + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +struct hid_usage_entry { + unsigned page; + unsigned usage; + char *description; +}; + +static struct hid_usage_entry hid_usage_table[] = { + { 1, 0, "GenericDesktop" }, + {0, 0x01, "Pointer"}, + {0, 0x02, "Mouse"}, + {0, 0x04, "Joystick"}, + {0, 0x05, "GamePad"}, + {0, 0x06, "Keyboard"}, + {0, 0x07, "Keypad"}, + {0, 0x08, "MultiAxis"}, + {0, 0x30, "X"}, + {0, 0x31, "Y"}, + {0, 0x32, "Z"}, + {0, 0x33, "Rx"}, + {0, 0x34, "Ry"}, + {0, 0x35, "Rz"}, + {0, 0x36, "Slider"}, + {0, 0x37, "Dial"}, + {0, 0x38, "Wheel"}, + {0, 0x39, "HatSwitch"}, + {0, 0x3a, "CountedBuffer"}, + {0, 0x3b, "ByteCount"}, + {0, 0x3c, "MotionWakeup"}, + {0, 0x3d, "Start"}, + {0, 0x3e, "Select"}, + {0, 0x40, "Vx"}, + {0, 0x41, "Vy"}, + {0, 0x42, "Vz"}, + {0, 0x43, "Vbrx"}, + {0, 0x44, "Vbry"}, + {0, 0x45, "Vbrz"}, + {0, 0x46, "Vno"}, + {0, 0x80, "SystemControl"}, + {0, 0x81, "SystemPowerDown"}, + {0, 0x82, "SystemSleep"}, + {0, 0x83, "SystemWakeUp"}, + {0, 0x84, "SystemContextMenu"}, + {0, 0x85, "SystemMainMenu"}, + {0, 0x86, "SystemAppMenu"}, + {0, 0x87, "SystemMenuHelp"}, + {0, 0x88, "SystemMenuExit"}, + {0, 0x89, "SystemMenuSelect"}, + {0, 0x8a, "SystemMenuRight"}, + {0, 0x8b, "SystemMenuLeft"}, + {0, 0x8c, "SystemMenuUp"}, + {0, 0x8d, "SystemMenuDown"}, + {0, 0x90, "D-padUp"}, + {0, 0x91, "D-padDown"}, + {0, 0x92, "D-padRight"}, + {0, 0x93, "D-padLeft"}, + { 7, 0, "Keyboard" }, + { 8, 0, "LED" }, + { 9, 0, "Button" }, + { 12, 0, "Hotkey" }, + { 13, 0, "Digitizers" }, + {0, 0x01, "Digitizer"}, + {0, 0x02, "Pen"}, + {0, 0x03, "LightPen"}, + {0, 0x04, "TouchScreen"}, + {0, 0x05, "TouchPad"}, + {0, 0x20, "Stylus"}, + {0, 0x21, "Puck"}, + {0, 0x22, "Finger"}, + {0, 0x30, "TipPressure"}, + {0, 0x31, "BarrelPressure"}, + {0, 0x32, "InRange"}, + {0, 0x33, "Touch"}, + {0, 0x34, "UnTouch"}, + {0, 0x35, "Tap"}, + {0, 0x39, "TabletFunctionKey"}, + {0, 0x3a, "ProgramChangeKey"}, + {0, 0x3c, "Invert"}, + {0, 0x42, "TipSwitch"}, + {0, 0x43, "SecondaryTipSwitch"}, + {0, 0x44, "BarrelSwitch"}, + {0, 0x45, "Eraser"}, + {0, 0x46, "TabletPick"}, + { 15, 0, "PhysicalInterfaceDevice" }, + { 0, 0, NULL } +}; + +static void resolv_usage_page(unsigned page) { + struct hid_usage_entry *p; + + for (p = hid_usage_table; p->description; p++) + if (p->page == page) { + printk("%s", p->description); + return; + } + printk("%04x", page); +} + +static void resolv_usage(unsigned usage) { + struct hid_usage_entry *p; + + resolv_usage_page(usage >> 16); + printk("."); + for (p = hid_usage_table; p->description; p++) + if (p->page == (usage >> 16)) { + for(++p; p->description && p->page == 0; p++) + if (p->usage == (usage & 0xffff)) { + printk("%s", p->description); + return; + } + break; + } + printk("%04x", usage & 0xffff); +} + +__inline__ static void tab(int n) { + while (n--) printk(" "); +} + +static void hid_dump_field(struct hid_field *field, int n) { + int j; + + if (field->physical) { + tab(n); + printk("Physical("); + resolv_usage(field->physical); printk(")\n"); + } + if (field->logical) { + tab(n); + printk("Logical("); + resolv_usage(field->logical); printk(")\n"); + } + tab(n); printk("Usage(%d)\n", field->maxusage); + for (j = 0; j < field->maxusage; j++) { + tab(n+2);resolv_usage(field->usage[j].hid); printk("\n"); + } + if (field->logical_minimum != field->logical_maximum) { + tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum); + tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum); + } + if (field->physical_minimum != field->physical_maximum) { + tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum); + tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum); + } + if (field->unit_exponent) { + tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent); + } + if (field->unit) { + tab(n); printk("Unit(%u)\n", field->unit); + } + tab(n); printk("Report Size(%u)\n", field->report_size); + tab(n); printk("Report Count(%u)\n", field->report_count); + tab(n); printk("Report Offset(%u)\n", field->report_offset); + + tab(n); printk("Flags( "); + j = field->flags; + printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : ""); + printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array "); + printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); + printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); + printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); + printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPrefferedState " : ""); + printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); + printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); + printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); + printk(")\n"); +} + +void hid_dump_device(struct hid_device *device) { + struct hid_report_enum *report_enum; + struct hid_report *report; + struct list_head *list; + unsigned i,k; + static char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; + + printk("Application("); + resolv_usage(device->application); + printk(")\n"); + + for (i = 0; i < HID_REPORT_TYPES; i++) { + report_enum = device->report_enum + i; + list = report_enum->report_list.next; + while (list != &report_enum->report_list) { + report = (struct hid_report *) list; + tab(2); + printk("%s", table[i]); + if (report->id) + printk("(%d)", report->id); + printk("[%s]", table[report->type]); + printk("\n"); + for (k = 0; k < report->maxfield; k++) { + tab(4); + printk("Field(%d)\n", k); + hid_dump_field(report->field[k], 6); + } + list = list->next; + } + } +} + +void hid_dump_input(struct hid_usage *usage, __s32 value) { + printk("hidd: input "); + resolv_usage(usage->hid); + printk(" = %d\n", value); +} diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/hid.c linux-2.2.17/drivers/usb/hid.c --- linux-2.2.17-orig/drivers/usb/hid.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/hid.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,1526 @@ +/* + * $Id: hid.c,v 1.6 2000/05/29 09:01:52 vojtech Exp $ + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000 Vojtech Pavlik + * + * USB HID support for the Linux input drivers + * + * Sponsored by SuSE + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef DEBUG +#undef DEBUG_DATA +#include + +#include + +#include "hid.h" + +#ifdef DEBUG +#include "hid-debug.h" +#else +#define hid_dump_input(a,b) do { } while (0) +#define hid_dump_device(c) do { } while (0) +#endif + +#define unk KEY_UNKNOWN + +static unsigned char hid_keyboard[256] = { + 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, + 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, + 27, 43, 84, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, + 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, + 72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95, + 120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113, + 115,114,unk,unk,unk,unk,unk,124,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, + 150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk +}; + +static struct { + __s32 x; + __s32 y; +} hid_hat_to_axis[] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}, { 0, 0}}; + +static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick", + "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"}; + +/* + * Register a new report for a device. + */ + +static struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id) +{ + struct hid_report_enum *report_enum = device->report_enum + type; + struct hid_report *report; + + if (report_enum->report_id_hash[id]) + return report_enum->report_id_hash[id]; + + if (!(report = kmalloc(sizeof(struct hid_report), GFP_KERNEL))) + return NULL; + memset(report, 0, sizeof(struct hid_report)); + + if (id != 0) report_enum->numbered = 1; + + report->id = id; + report->type = type; + report->size = 0; + report->device = device; + report_enum->report_id_hash[id] = report; + + list_add_tail(&report->list, &report_enum->report_list); + + return report; +} + +/* + * Register a new field for this report. + */ + +static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values) +{ + if (report->maxfield < HID_MAX_FIELDS) { + struct hid_field *field; + + if (!(field = kmalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage) + + values * sizeof(unsigned), GFP_KERNEL))) + return NULL; + memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage) + + values * sizeof(unsigned)); + + report->field[report->maxfield++] = field; + field->usage = (struct hid_usage *)(field + 1); + field->value = (unsigned *)(field->usage + usages); + field->report = report; + + return field; + } + + dbg("too many fields in report"); + return NULL; +} + +/* + * Open a collection. The type/usage is pushed on the stack. + */ + +static int open_collection(struct hid_parser *parser, unsigned type) +{ + unsigned usage; + + usage = parser->local.usage[0]; + + if (type == HID_COLLECTION_APPLICATION) + parser->device->application = usage; + + if (parser->collection_stack_ptr < HID_COLLECTION_STACK_SIZE) { /* PUSH on stack */ + struct hid_collection *collection = parser->collection_stack + parser->collection_stack_ptr++; + collection->type = type; + collection->usage = usage; + return 0; + } + + dbg("collection stack overflow"); + return -1; +} + +/* + * Close a collection. + */ + +static int close_collection(struct hid_parser *parser) +{ + if (parser->collection_stack_ptr > 0) { /* POP from stack */ + parser->collection_stack_ptr--; + return 0; + } + dbg("collection stack underflow"); + return -1; +} + +/* + * Climb up the stack, search for the specified collection type + * and return the usage. + */ + +static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) +{ + int n; + for (n = parser->collection_stack_ptr - 1; n >= 0; n--) + if (parser->collection_stack[n].type == type) + return parser->collection_stack[n].usage; + return 0; /* we know nothing about this usage type */ +} + +/* + * Add a usage to the temporary parser table. + */ + +static int hid_add_usage(struct hid_parser *parser, unsigned usage) +{ + if (parser->local.usage_index >= MAX_USAGES) { + dbg("usage index exceeded"); + return -1; + } + parser->local.usage[parser->local.usage_index++] = usage; + return 0; +} + +/* + * Register a new field for this report. + */ + +static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsigned flags) +{ + struct hid_report *report; + struct hid_field *field; + int usages; + unsigned offset; + int i; + + if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) { + dbg("hid_register_report failed"); + return -1; + } + + if (HID_MAIN_ITEM_VARIABLE & ~flags) { /* ARRAY */ + if (parser->global.logical_maximum <= parser->global.logical_minimum) { + dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum); + return -1; + } + usages = parser->local.usage_index; + /* Hint: we can assume usages < MAX_USAGE here */ + } else { /* VARIABLE */ + usages = parser->global.report_count; + } + offset = report->size; + report->size += parser->global.report_size * + parser->global.report_count; + if (usages == 0) + return 0; /* ignore padding fields */ + if ((field = hid_register_field(report, usages, + parser->global.report_count)) == NULL) + return 0; + field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL); + field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL); + for (i = 0; i < usages; i++) field->usage[i].hid = parser->local.usage[i]; + field->maxusage = usages; + field->flags = flags; + field->report_offset = offset; + field->report_type = report_type; + field->report_size = parser->global.report_size; + field->report_count = parser->global.report_count; + field->logical_minimum = parser->global.logical_minimum; + field->logical_maximum = parser->global.logical_maximum; + field->physical_minimum = parser->global.physical_minimum; + field->physical_maximum = parser->global.physical_maximum; + field->unit_exponent = parser->global.unit_exponent; + field->unit = parser->global.unit; + return 0; +} + +/* + * Read data value from item. + */ + +static __inline__ __u32 item_udata(struct hid_item *item) +{ + switch (item->size) { + case 1: return item->data.u8; + case 2: return item->data.u16; + case 4: return item->data.u32; + } + return 0; +} + +static __inline__ __s32 item_sdata(struct hid_item *item) +{ + switch (item->size) { + case 1: return item->data.s8; + case 2: return item->data.s16; + case 4: return item->data.s32; + } + return 0; +} + +/* + * Process a global item. + */ + +static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) +{ + switch (item->tag) { + + case HID_GLOBAL_ITEM_TAG_PUSH: + + if (parser->global_stack_ptr < HID_GLOBAL_STACK_SIZE) { + memcpy(parser->global_stack + parser->global_stack_ptr++, + &parser->global, sizeof(struct hid_global)); + return 0; + } + dbg("global enviroment stack overflow"); + return -1; + + case HID_GLOBAL_ITEM_TAG_POP: + + if (parser->global_stack_ptr > 0) { + memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr, + sizeof(struct hid_global)); + return 0; + } + dbg("global enviroment stack underflow"); + return -1; + + case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: + parser->global.usage_page = item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: + parser->global.logical_minimum = item_sdata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: + parser->global.logical_maximum = item_sdata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: + parser->global.physical_minimum = item_sdata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: + parser->global.physical_maximum = item_sdata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: + parser->global.unit_exponent = item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_UNIT: + parser->global.unit = item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: + if ((parser->global.report_size = item_udata(item)) > 32) { + dbg("invalid report_size %d", parser->global.report_size); + return -1; + } + return 0; + + case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: + if ((parser->global.report_count = item_udata(item)) > MAX_USAGES) { + dbg("invalid report_count %d", parser->global.report_count); + return -1; + } + return 0; + + case HID_GLOBAL_ITEM_TAG_REPORT_ID: + if ((parser->global.report_id = item_udata(item)) == 0) { + dbg("report_id 0 is invalid"); + return -1; + } + return 0; + + default: + dbg("unknown global tag 0x%x", item->tag); + return -1; + } +} + +/* + * Process a local item. + */ + +static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) +{ + __u32 data; + + if (item->size == 0) { + dbg("item data expected for local item"); + return -1; + } + + data = item_udata(item); + + switch (item->tag) { + + case HID_LOCAL_ITEM_TAG_DELIMITER: + + if (data) { + /* + * We treat items before the first delimiter + * as global to all usage sets (branch 0). + * In the moment we process only these global + * items and the first delimiter set. + */ + if (parser->local.delimiter_depth != 0) { + dbg("nested delimiters"); + return -1; + } + parser->local.delimiter_depth++; + parser->local.delimiter_branch++; + } else { + if (parser->local.delimiter_depth < 1) { + dbg("bogus close delimiter"); + return -1; + } + parser->local.delimiter_depth--; + } + return 1; + + case HID_LOCAL_ITEM_TAG_USAGE: + + if (parser->local.delimiter_branch < 2) { + if (item->size <= 2) + data = (parser->global.usage_page << 16) + data; + return hid_add_usage(parser, data); + } + dbg("alternative usage ignored"); + return 0; + + case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: + + if (parser->local.delimiter_branch < 2) { + if (item->size <= 2) + data = (parser->global.usage_page << 16) + data; + parser->local.usage_minimum = data; + return 0; + } + dbg("alternative usage ignored"); + return 0; + + case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: + + if (parser->local.delimiter_branch < 2) { + unsigned n; + if (item->size <= 2) + data = (parser->global.usage_page << 16) + data; + for (n = parser->local.usage_minimum; n <= data; n++) + if (hid_add_usage(parser, n)) { + dbg("hid_add_usage failed\n"); + return -1; + } + return 0; + } + dbg("alternative usage ignored"); + return 0; + + default: + + dbg("unknown local item tag 0x%x", item->tag); + return 0; + } +} + +/* + * Process a main item. + */ + +static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) +{ + __u32 data; + int ret; + + data = item_udata(item); + + switch (item->tag) { + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: + ret = open_collection(parser, data & 3); + break; + case HID_MAIN_ITEM_TAG_END_COLLECTION: + ret = close_collection(parser); + break; + case HID_MAIN_ITEM_TAG_INPUT: + ret = hid_add_field(parser, HID_INPUT_REPORT, data); + break; + case HID_MAIN_ITEM_TAG_OUTPUT: + ret = hid_add_field(parser, HID_OUTPUT_REPORT, data); + break; + case HID_MAIN_ITEM_TAG_FEATURE: + ret = hid_add_field(parser, HID_FEATURE_REPORT, data); + break; + default: + dbg("unknown main item tag 0x%x", item->tag); + ret = 0; + } + + memset(&parser->local, 0, sizeof(parser->local)); /* Reset the local parser environment */ + + return ret; +} + +/* + * Process a reserved item. + */ + +static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item) +{ + dbg("reserved item type, tag 0x%x", item->tag); + return 0; +} + +/* + * Free a report and all registered fields. The field->usage and + * field->value table's are allocated behind the field, so we need + * only to free(field) itself. + */ + +static void hid_free_report(struct hid_report *report) +{ + unsigned n; + + for (n = 0; n < report->maxfield; n++) + kfree(report->field[n]); + kfree(report); +} + +/* + * Free a device structure, all reports, and all fields. + */ + +static void hid_free_device(struct hid_device *device) +{ + unsigned i,j; + + for (i = 0; i < HID_REPORT_TYPES; i++) { + struct hid_report_enum *report_enum = device->report_enum + i; + + for (j = 0; j < 256; j++) { + struct hid_report *report = report_enum->report_id_hash[j]; + if (report) hid_free_report(report); + } + } + + if (device->rdesc) kfree(device->rdesc); +} + +/* + * Fetch a report description item from the data stream. We support long + * items, though they are not used yet. + */ + +static __u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) +{ + if ((end - start) > 0) { + + __u8 b = *start++; + item->type = (b >> 2) & 3; + item->tag = (b >> 4) & 15; + + if (item->tag == HID_ITEM_TAG_LONG) { + + item->format = HID_ITEM_FORMAT_LONG; + + if ((end - start) >= 2) { + + item->size = *start++; + item->tag = *start++; + + if ((end - start) >= item->size) { + item->data.longdata = start; + start += item->size; + return start; + } + } + } else { + + item->format = HID_ITEM_FORMAT_SHORT; + item->size = b & 3; + switch (item->size) { + + case 0: + return start; + + case 1: + if ((end - start) >= 1) { + item->data.u8 = *start++; + return start; + } + break; + + case 2: + if ((end - start) >= 2) { + item->data.u16 = le16_to_cpu( get_unaligned(((__u16*)start)++)); + return start; + } + + case 3: + item->size++; + if ((end - start) >= 4) { + item->data.u32 = le32_to_cpu( get_unaligned(((__u32*)start)++)); + return start; + } + } + } + } + return NULL; +} + +/* + * Parse a report description into a hid_device structure. Reports are + * enumerated, fields are attached to these reports. + */ + +static struct hid_device *hid_parse_report(__u8 *start, unsigned size) +{ + struct hid_device *device; + struct hid_parser *parser; + struct hid_item item; + __u8 *end; + unsigned i; + static int (*dispatch_type[])(struct hid_parser *parser, + struct hid_item *item) = { + hid_parser_main, + hid_parser_global, + hid_parser_local, + hid_parser_reserved + }; + + if (!(device = kmalloc(sizeof(struct hid_device), GFP_KERNEL))) + return NULL; + memset(device, 0, sizeof(struct hid_device)); + + for (i = 0; i < HID_REPORT_TYPES; i++) + INIT_LIST_HEAD(&device->report_enum[i].report_list); + + if (!(device->rdesc = (__u8 *)kmalloc(size, GFP_KERNEL))) { + kfree(device); + return NULL; + } + memcpy(device->rdesc, start, size); + + if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) { + kfree(device->rdesc); + kfree(device); + return NULL; + } + memset(parser, 0, sizeof(struct hid_parser)); + parser->device = device; + + end = start + size; + while ((start = fetch_item(start, end, &item)) != 0) { + if (item.format != HID_ITEM_FORMAT_SHORT) { + dbg("unexpected long global item"); + hid_free_device(device); + kfree(parser); + return NULL; + } + if (dispatch_type[item.type](parser, &item)) { + dbg("item %u %u %u %u parsing failed\n", + item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); + hid_free_device(device); + kfree(parser); + return NULL; + } + + if (start == end) { + if (parser->collection_stack_ptr) { + dbg("unbalanced collection at end of report description"); + hid_free_device(device); + kfree(parser); + return NULL; + } + if (parser->local.delimiter_depth) { + dbg("unbalanced delimiter at end of report description"); + hid_free_device(device); + kfree(parser); + return NULL; + } + kfree(parser); + return device; + } + } + + dbg("item fetching failed at offset %d\n", (int)(end - start)); + hid_free_device(device); + kfree(parser); + return NULL; +} + +/* + * Convert a signed n-bit integer to signed 32-bit integer. Common + * cases are done through the compiler, the screwed things has to be + * done by hand. + */ + +static __inline__ __s32 snto32(__u32 value, unsigned n) +{ + switch (n) { + case 8: return ((__s8)value); + case 16: return ((__s16)value); + case 32: return ((__s32)value); + } + return value & (1 << (n - 1)) ? value | (-1 << n) : value; +} + +/* + * Convert a signed 32-bit integer to a signed n-bit integer. + */ + +static __inline__ __u32 s32ton(__s32 value, unsigned n) +{ + __s32 a = value >> (n - 1); + if (a && a != -1) return value > 0 ? 1 << (n - 1) : (1 << n) - 1; + return value & ((1 << n) - 1); +} + +/* + * Extract/implement a data field from/to a report. We use 64-bit unsigned, + * 32-bit aligned, so that we can possibly have alignment problems on some + * odd architectures. + */ + +static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n) +{ + report += (offset >> 5) << 2; offset &= 31; + return (le64_to_cpu(get_unaligned((__u64*)report)) >> offset) & ((1 << n) - 1); +} + +static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value) +{ + report += (offset >> 5) << 2; offset &= 31; + *(__u64*)report &= cpu_to_le64(~((((__u64) 1 << n) - 1) << offset)); + *(__u64*)report |= cpu_to_le64((__u64)value << offset); +} + +static void hid_configure_usage(struct hid_device *device, struct hid_field *field, struct hid_usage *usage) +{ + struct input_dev *input = &device->input; + int max; + unsigned long *bit; + + switch (usage->hid & HID_USAGE_PAGE) { + + case HID_UP_KEYBOARD: + + set_bit(EV_REP, input->evbit); + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + + if ((usage->hid & HID_USAGE) < 256) { + if (!(usage->code = hid_keyboard[usage->hid & HID_USAGE])) + return; + clear_bit(usage->code, bit); + } else + usage->code = KEY_UNKNOWN; + + break; + + case HID_UP_BUTTON: + + usage->code = ((usage->hid - 1) & 0xf) + 0x100; + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + + switch (device->application) { + case HID_GD_GAMEPAD: usage->code += 0x10; + case HID_GD_JOYSTICK: usage->code += 0x10; + case HID_GD_MOUSE: usage->code += 0x10; break; + default: + if (field->physical == HID_GD_POINTER) + usage->code += 0x10; + break; + } + break; + + case HID_UP_GENDESK: + + if ((usage->hid & 0xf0) == 0x80) { /* SystemControl */ + switch (usage->hid & 0xf) { + case 0x1: usage->code = KEY_POWER; break; + case 0x2: usage->code = KEY_SLEEP; break; + case 0x3: usage->code = KEY_WAKEUP; break; + default: usage->code = KEY_UNKNOWN; break; + } + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + break; + } + + usage->code = usage->hid & 0xf; + + if (field->report_size == 1) { + usage->code = BTN_MISC; + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + break; + } + + if (field->flags & HID_MAIN_ITEM_RELATIVE) { + usage->type = EV_REL; bit = input->relbit; max = REL_MAX; + break; + } + + usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; + + if (usage->hid == HID_GD_HATSWITCH) { + usage->code = ABS_HAT0X; + usage->hat = 1 + (field->logical_maximum == 4); + } + break; + + case HID_UP_LED: + + usage->code = (usage->hid - 1) & 0xf; + usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; + break; + + case HID_UP_DIGITIZER: + + switch (usage->hid & 0xff) { + + case 0x30: /* TipPressure */ + + if (!test_bit(BTN_TOUCH, input->keybit)) { + device->quirks |= HID_QUIRK_NOTOUCH; + set_bit(EV_KEY, input->evbit); + set_bit(BTN_TOUCH, input->keybit); + } + usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; + usage->code = ABS_PRESSURE; + clear_bit(usage->code, bit); + break; + + case 0x32: /* InRange */ + + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + switch (field->physical & 0xff) { + case 0x21: usage->code = BTN_TOOL_MOUSE; break; + case 0x22: usage->code = BTN_TOOL_FINGER; break; + default: usage->code = BTN_TOOL_PEN; break; + } + break; + + case 0x3c: /* Invert */ + + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + usage->code = BTN_TOOL_RUBBER; + clear_bit(usage->code, bit); + break; + + case 0x33: /* Touch */ + case 0x42: /* TipSwitch */ + case 0x43: /* TipSwitch2 */ + + device->quirks &= ~HID_QUIRK_NOTOUCH; + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + usage->code = BTN_TOUCH; + clear_bit(usage->code, bit); + break; + + case 0x44: /* BarrelSwitch */ + + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + usage->code = BTN_STYLUS; + clear_bit(usage->code, bit); + break; + + default: goto unknown; + } + break; + + case HID_UP_CONSUMER: /* USB HUT v1.1, pages 56-62 */ + + switch (usage->hid & HID_USAGE) { + + case 0x034: usage->code = KEY_SLEEP; break; + case 0x036: usage->code = BTN_MISC; break; + case 0x08a: usage->code = KEY_WWW; break; + case 0x095: usage->code = KEY_HELP; break; + + case 0x0b4: usage->code = KEY_REWIND; break; + case 0x0b5: usage->code = KEY_NEXTSONG; break; + case 0x0b6: usage->code = KEY_PREVIOUSSONG; break; + case 0x0b7: usage->code = KEY_STOPCD; break; + case 0x0b8: usage->code = KEY_EJECTCD; break; + case 0x0cd: usage->code = KEY_PLAYPAUSE; break; + + case 0x0e2: usage->code = KEY_MUTE; break; + case 0x0e9: usage->code = KEY_VOLUMEUP; break; + case 0x0ea: usage->code = KEY_VOLUMEDOWN; break; + + case 0x183: usage->code = KEY_CONFIG; break; + case 0x18a: usage->code = KEY_MAIL; break; + case 0x192: usage->code = KEY_CALC; break; + case 0x194: usage->code = KEY_FILE; break; + + case 0x21a: usage->code = KEY_UNDO; break; + case 0x21b: usage->code = KEY_COPY; break; + case 0x21c: usage->code = KEY_CUT; break; + case 0x21d: usage->code = KEY_PASTE; break; + + case 0x221: usage->code = KEY_FIND; break; + case 0x223: usage->code = KEY_HOMEPAGE; break; + case 0x224: usage->code = KEY_BACK; break; + case 0x225: usage->code = KEY_FORWARD; break; + case 0x226: usage->code = KEY_STOP; break; + case 0x227: usage->code = KEY_REFRESH; break; + case 0x22a: usage->code = KEY_BOOKMARKS; break; + + default: usage->code = KEY_UNKNOWN; break; + + } + + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + break; + + default: + unknown: + + if (field->report_size == 1) { + usage->code = BTN_MISC; + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + break; + } + + if (field->flags & HID_MAIN_ITEM_RELATIVE) { + usage->code = REL_MISC; + usage->type = EV_REL; bit = input->relbit; max = REL_MAX; + break; + } + + usage->code = ABS_MISC; + usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; + break; + } + + set_bit(usage->type, input->evbit); + + while (usage->code <= max && test_and_set_bit(usage->code, bit)) { + usage->code = find_next_zero_bit(bit, max + 1, usage->code); + } + + if (usage->type == EV_ABS) { + int a = field->logical_minimum; + int b = field->logical_maximum; + + input->absmin[usage->code] = a; + input->absmax[usage->code] = b; + input->absfuzz[usage->code] = (b - a) >> 8; + input->absflat[usage->code] = (b - a) >> 4; + } + + if (usage->hat) { + int i; + for (i = usage->code; i < usage->code + 2; i++) { + input->absmax[i] = 1; + input->absmin[i] = -1; + input->absfuzz[i] = 0; + input->absflat[i] = 0; + } + set_bit(usage->code + 1, input->absbit); + } +} + +static void hid_process_event(struct input_dev *input, int *quirks, struct hid_field *field, struct hid_usage *usage, __s32 value) +{ + hid_dump_input(usage, value); + + if (usage->hat) { + if (usage->hat == 2) value = value * 2; + if (value > 8) value = 8; + input_event(input, usage->type, usage->code , hid_hat_to_axis[value].x); + input_event(input, usage->type, usage->code + 1, hid_hat_to_axis[value].y); + return; + } + + if (usage->hid == (HID_UP_DIGITIZER | 0x003c)) { /* Invert */ + *quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT); + return; + } + + if (usage->hid == (HID_UP_DIGITIZER | 0x0032)) { /* InRange */ + if (value) { + input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1); + return; + } + input_event(input, usage->type, usage->code, 0); + input_event(input, usage->type, BTN_TOOL_RUBBER, 0); + return; + } + + if (usage->hid == (HID_UP_DIGITIZER | 0x0030) && (*quirks & HID_QUIRK_NOTOUCH)) { /* Pressure */ + int a = field->logical_minimum; + int b = field->logical_maximum; + input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3)); + } + + input_event(input, usage->type, usage->code, value); + + if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY)) + input_event(input, usage->type, usage->code, 0); +} + +/* + * Search an array for a value. + */ + +static __inline__ int search(__s32 *array, __s32 value, unsigned n) +{ + while (n--) if (*array++ == value) return 0; + return -1; +} + +/* + * Analyse a received field, and fetch the data from it. The field + * content is stored for next report processing (we do differential + * reporting to the layer). + */ + +static void hid_input_field(struct hid_device *dev, struct hid_field *field, __u8 *data) +{ + unsigned n; + unsigned count = field->report_count; + unsigned offset = field->report_offset; + unsigned size = field->report_size; + __s32 min = field->logical_minimum; + __s32 max = field->logical_maximum; + __s32 value[count]; /* WARNING: gcc specific */ + + for (n = 0; n < count; n++) + value[n] = min < 0 ? snto32(extract(data, offset + n * size, size), size) : + extract(data, offset + n * size, size); + + for (n = 0; n < count; n++) { + + if (HID_MAIN_ITEM_VARIABLE & field->flags) { + + if (field->flags & HID_MAIN_ITEM_RELATIVE) { + if (!value[n]) continue; + } else { + if (value[n] == field->value[n]) continue; + } + hid_process_event(&dev->input, &dev->quirks, field, &field->usage[n], value[n]); + + } else { + + if (field->value[n] >= min && field->value[n] <= max /* non-NULL value */ + && field->usage[field->value[n] - min].hid /* nonzero usage */ + && search(value, field->value[n], count)) + hid_process_event(&dev->input, &dev->quirks, field, + &field->usage[field->value[n] - min], 0); + + if (value[n] >= min && value[n] <= max /* non-NULL value */ + && field->usage[value[n] - min].hid /* nonzero usage */ + && search(field->value, value[n], count)) + hid_process_event(&dev->input, &dev->quirks, + field, &field->usage[value[n] - min], 1); + } + } + + memcpy(field->value, value, count * sizeof(__s32)); +} + +/* + * Interrupt input handler - analyse a received report. + */ + +static void hid_irq(struct urb *urb) +{ + struct hid_device *device = urb->context; + struct hid_report_enum *report_enum = device->report_enum + HID_INPUT_REPORT; + struct hid_report *report; + __u8 *data = urb->transfer_buffer; + int len = urb->actual_length; + int n; + + if (urb->status) { + dbg("nonzero status in irq %d", urb->status); + return; + } + + if (!len) { + dbg("empty report"); + return; + } + +#ifdef DEBUG_DATA + printk(KERN_DEBUG __FILE__ ": report (size %u) (%snumbered) = ", len, report_enum->numbered ? "" : "un"); + for (n = 0; n < len; n++) + printk(" %02x", data[n]); + printk("\n"); +#endif + + n = 0; /* Normally report number is 0 */ + + if (report_enum->numbered) { /* Device uses numbered reports, data[0] is report number */ + n = *data++; + len--; + } + + if (!(report = report_enum->report_id_hash[n])) { + dbg("undefined report_id %d received", n); +#ifdef DEBUG + printk(KERN_DEBUG __FILE__ ": report (size %u) = ", len); + for (n = 0; n < len; n++) + printk(" %02x", data[n]); + printk("\n"); +#endif + + return; + } + + if (len < ((report->size - 1) >> 3) + 1) { + dbg("report %d is too short, (%d < %d)", report->id, len, ((report->size - 1) >> 3) + 1); + return; + } + + for (n = 0; n < report->maxfield; n++) + hid_input_field(device, report->field[n], data); + + return; +} + +/* + * hid_read_report() s intended to read the hid devices values even + * before the input device is registered, so that the userland interface + * modules start with real values. This is especially important for joydev.c + * automagic calibration. Doesn't work yet, though. Don't know why, the control + * request just times out on most devices I have and returns nonsense on others. + */ + +static void hid_read_report(struct hid_device *hid, struct hid_report *report) +{ +#if 0 + int rlen = ((report->size - 1) >> 3) + 1; + char rdata[rlen]; + struct urb urb; + int read, j; + + memset(&urb, 0, sizeof(struct urb)); + memset(rdata, 0, rlen); + + urb.transfer_buffer = rdata; + urb.actual_length = rlen; + urb.context = hid; + + dbg("getting report type %d id %d len %d", report->type + 1, report->id, rlen); + + if ((read = usb_get_report(hid->dev, hid->ifnum, report->type + 1, report->id, rdata, rlen)) != rlen) { + dbg("reading report failed rlen %d read %d", rlen, read); +#ifdef DEBUG + printk(KERN_DEBUG __FILE__ ": report = "); + for (j = 0; j < rlen; j++) printk(" %02x", rdata[j]); + printk("\n"); +#endif + return; + } + + hid_irq(&urb); +#endif +} + +/* + * Output the field into the report. + */ + +static void hid_output_field(struct hid_field *field, __u8 *data) +{ + unsigned count = field->report_count; + unsigned offset = field->report_offset; + unsigned size = field->report_size; + unsigned n; + + for (n = 0; n < count; n++) { + if (field->logical_minimum < 0) /* signed values */ + implement(data, offset + n * size, size, s32ton(field->value[n], size)); + else /* unsigned values */ + implement(data, offset + n * size, size, field->value[n]); + } +} + +/* + * Create a report. + */ + +void hid_output_report(struct hid_report *report, __u8 *data) +{ + unsigned n; + for (n = 0; n < report->maxfield; n++) + hid_output_field(report->field[n], data); +}; + +/* + * Set a field value. The report this field belongs to has to be + * created and transfered to the device, to set this value in the + * device. + */ + +int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) +{ + unsigned size = field->report_size; + + hid_dump_input(field->usage + offset, value); + + if (offset >= field->report_count) { + dbg("offset exceeds report_count"); + return -1; + } + if (field->logical_minimum < 0) { + if (value != snto32(s32ton(value, size), size)) { + dbg("value %d is out of range", value); + return -1; + } + } + if ( (value > field->logical_maximum) + || (value < field->logical_minimum)) { + dbg("value %d is invalid", value); + return -1; + } + field->value[offset] = value; + return 0; +} + +static int hid_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field) +{ + struct hid_report_enum *report_enum = hid->report_enum + HID_OUTPUT_REPORT; + struct list_head *list = report_enum->report_list.next; + int i, j; + + while (list != &report_enum->report_list) { + struct hid_report *report = (struct hid_report *) list; + list = list->next; + for (i = 0; i < report->maxfield; i++) { + *field = report->field[i]; + for (j = 0; j < (*field)->maxusage; j++) + if ((*field)->usage[j].type == type && (*field)->usage[j].code == code) + return j; + } + } + return -1; +} + +static void hid_ctrl(struct urb *urb) +{ + if (urb->status) + warn("ctrl urb status %d received", urb->status); +} + +static int hid_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct hid_device *hid = dev->private; + struct hid_field *field = NULL; + int offset; + + if ((offset = hid_find_field(hid, type, code, &field)) == -1) { + warn("event field not found"); + return -1; + } + + hid_set_field(field, offset, value); + + if (hid->urbout.status == -EINPROGRESS) { + warn("had to kill output urb"); + usb_unlink_urb(&hid->urbout); + } + + hid_output_report(field->report, hid->bufout); + + hid->dr.value = 0x200 | field->report->id; + hid->dr.length = ((field->report->size - 1) >> 3) + 1; + hid->urbout.transfer_buffer_length = hid->dr.length; + + if (usb_submit_urb(&hid->urbout)) { + err("usb_submit_urb(out) failed"); + return -1; + } + + return 0; +} + +static int hid_open(struct input_dev *dev) +{ + struct hid_device *hid = dev->private; + + if (hid->open++) + return 0; + + if (usb_submit_urb(&hid->urb)) + return -EIO; + + return 0; +} + +static void hid_close(struct input_dev *dev) +{ + struct hid_device *hid = dev->private; + + if (!--hid->open) + usb_unlink_urb(&hid->urb); +} + +/* + * Configure the input layer interface + * Read all reports and initalize the absoulte field values. + */ + +static void hid_init_input(struct hid_device *hid) +{ + struct hid_report_enum *report_enum; + struct list_head *list; + int i, j, k; + + hid->input.private = hid; + hid->input.event = hid_event; + hid->input.open = hid_open; + hid->input.close = hid_close; + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { + + report_enum = hid->report_enum + k; + list = report_enum->report_list.next; + + while (list != &report_enum->report_list) { + + struct hid_report *report = (struct hid_report *) list; + + list = list->next; + + for (i = 0; i < report->maxfield; i++) + for (j = 0; j < report->field[i]->maxusage; j++) + hid_configure_usage(hid, report->field[i], report->field[i]->usage + j); + + if (k == HID_INPUT_REPORT) { + usb_set_idle(hid->dev, hid->ifnum, 0, report->id); + hid_read_report(hid, report); + } + } + } +} + +#define USB_VENDOR_ID_WACOM 0x056a +#define USB_DEVICE_ID_WACOM_GRAPHIRE 0x0010 +#define USB_DEVICE_ID_WACOM_INTUOS 0x0020 + +struct hid_blacklist { + __u16 idVendor; + __u16 idProduct; +} hid_blacklist[] = { + { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE }, + { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS }, + { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 1}, + { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 2}, + { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 3}, + { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 4}, + { 0, 0 } +}; + +static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum, char *name) +{ + struct usb_interface_descriptor *interface = dev->actconfig->interface[ifnum].altsetting + 0; + struct hid_descriptor *hdesc; + struct hid_device *hid; + unsigned rsize = 0; + int n; + + for (n = 0; hid_blacklist[n].idVendor; n++) + if ((hid_blacklist[n].idVendor == dev->descriptor.idVendor) && + (hid_blacklist[n].idProduct == dev->descriptor.idProduct)) return NULL; + + if (interface->bInterfaceClass != USB_INTERFACE_CLASS_HID) + return NULL; + + if (usb_get_extra_descriptor(interface, USB_DT_HID, &hdesc) + && usb_get_extra_descriptor(&interface->endpoint[0], USB_DT_HID, &hdesc)) { + dbg("class descriptor not present\n"); + return NULL; + } + + for (n = 0; n < hdesc->bNumDescriptors; n++) + if (hdesc->desc[n].bDescriptorType == USB_DT_REPORT) + rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength); + + if (!rsize || rsize > 1024) { + dbg("weird size of report descriptor (%u)", rsize); + return NULL; + } + + { + __u8 rdesc[rsize]; + + if ((n = usb_get_class_descriptor(dev, interface->bInterfaceNumber, USB_DT_REPORT, 0, rdesc, rsize)) < 0) { + dbg("reading report descriptor failed"); + return NULL; + } + +#ifdef DEBUG_DATA + printk(KERN_DEBUG __FILE__ ": report (size %u, read %d) = ", rsize, n); + for (n = 0; n < rsize; n++) + printk(" %02x", (unsigned) rdesc[n]); + printk("\n"); +#endif + + if (!(hid = hid_parse_report(rdesc, rsize))) { + dbg("parsing report descriptor failed"); + return NULL; + } + } + + for (n = 0; n < interface->bNumEndpoints; n++) { + + struct usb_endpoint_descriptor *endpoint = &interface->endpoint[n]; + int pipe, maxp; + + if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */ + continue; + + if (!(endpoint->bEndpointAddress & 0x80)) /* Not an input endpoint */ + continue; + + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + FILL_INT_URB(&hid->urb, dev, pipe, hid->buffer, maxp > 32 ? 32 : maxp, hid_irq, hid, endpoint->bInterval); + + break; + } + + if (n == interface->bNumEndpoints) { + dbg("couldn't find an input interrupt endpoint"); + hid_free_device(hid); + return NULL; + } + + hid->version = hdesc->bcdHID; + hid->country = hdesc->bCountryCode; + hid->dev = dev; + hid->ifnum = interface->bInterfaceNumber; + + hid->dr.requesttype = USB_TYPE_CLASS | USB_RECIP_INTERFACE; + hid->dr.request = USB_REQ_SET_REPORT; + hid->dr.value = 0x200; + hid->dr.index = hid->ifnum; + hid->dr.length = 1; + + hid->input.name = hid->name; + hid->input.idbus = BUS_USB; + hid->input.idvendor = dev->descriptor.idVendor; + hid->input.idproduct = dev->descriptor.idProduct; + hid->input.idversion = dev->descriptor.bcdDevice; + + if (strlen(name)) + strcpy(hid->name, name); + else + sprintf(hid->name, "USB HID %s %04x:%04x", + ((hid->application >= 0x00010000) && (hid->application <= 0x00010008)) ? + hid_types[hid->application & 0xffff] : "Device", + hid->input.idvendor, hid->input.idproduct); + + FILL_CONTROL_URB(&hid->urbout, dev, usb_sndctrlpipe(dev, 0), + (void*) &hid->dr, hid->bufout, 1, hid_ctrl, hid); + + if (interface->bInterfaceSubClass == 1) + usb_set_protocol(dev, hid->ifnum, 1); + + return hid; +} + +static void* hid_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct hid_device *hid; + char name[128]; + char *buf; + + dbg("HID probe called for ifnum %d", ifnum); + + name[0] = 0; + + if (!(buf = kmalloc(63, GFP_KERNEL))) + return NULL; + + if (dev->descriptor.iManufacturer && + usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0) + strcat(name, buf); + if (dev->descriptor.iProduct && + usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0) + sprintf(name, "%s %s", name, buf); + + kfree(buf); + + if (!(hid = usb_hid_configure(dev, ifnum, name))) + return NULL; + + hid_dump_device(hid); + + hid_init_input(hid); + input_register_device(&hid->input); + + printk(KERN_INFO "input%d: USB HID v%x.%02x %s", + hid->input.number, + hid->version >> 8, hid->version & 0xff, + ((hid->application >= 0x00010000) && (hid->application <= 0x00010008)) ? + hid_types[hid->application & 0xffff] : "Device"); + + if (strlen(name)) + printk(" [%s]", name); + else + printk(" [%04x:%04x]", hid->input.idvendor, hid->input.idproduct); + + printk(" on usb%d:%d.%d\n", dev->bus->busnum, dev->devnum, ifnum); + + return hid; +} + +static void hid_disconnect(struct usb_device *dev, void *ptr) +{ + struct hid_device *hid = ptr; + + dbg("cleanup called"); + usb_unlink_urb(&hid->urb); + input_unregister_device(&hid->input); + hid_free_device(hid); +} + +static struct usb_driver hid_driver = { + name: "hid", + probe: hid_probe, + disconnect: hid_disconnect +}; + +static int __init hid_init(void) +{ + usb_register(&hid_driver); + return 0; +} + +static void __exit hid_exit(void) +{ + usb_deregister(&hid_driver); +} + +module_init(hid_init); +module_exit(hid_exit); diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/hid.h linux-2.2.17/drivers/usb/hid.h --- linux-2.2.17-orig/drivers/usb/hid.h Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/hid.h Sun Sep 24 05:08:25 2000 @@ -0,0 +1,330 @@ +#ifndef __HID_H +#define __HID_H + +/* + * $Id: hid.h,v 1.4 2000/05/29 09:01:52 vojtech Exp $ + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include + +/* + * USB HID (Human Interface Device) interface class code + */ + +#define USB_INTERFACE_CLASS_HID 3 + +/* + * We parse each description item into this structure. Short items data + * values are expanded to 32-bit signed int, long items contain a pointer + * into the data area. + */ + +struct hid_item { + unsigned format; + __u8 size; + __u8 type; + __u8 tag; + union { + __u8 u8; + __s8 s8; + __u16 u16; + __s16 s16; + __u32 u32; + __s32 s32; + __u8 *longdata; + } data; +}; + +/* + * HID report item format + */ + +#define HID_ITEM_FORMAT_SHORT 0 +#define HID_ITEM_FORMAT_LONG 1 + +/* + * Special tag indicating long items + */ + +#define HID_ITEM_TAG_LONG 15 + +/* + * HID report descriptor item type (prefix bit 2,3) + */ + +#define HID_ITEM_TYPE_MAIN 0 +#define HID_ITEM_TYPE_GLOBAL 1 +#define HID_ITEM_TYPE_LOCAL 2 +#define HID_ITEM_TYPE_RESERVED 3 + +/* + * HID report descriptor main item tags + */ + +#define HID_MAIN_ITEM_TAG_INPUT 8 +#define HID_MAIN_ITEM_TAG_OUTPUT 9 +#define HID_MAIN_ITEM_TAG_FEATURE 11 +#define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION 10 +#define HID_MAIN_ITEM_TAG_END_COLLECTION 12 + +/* + * HID report descriptor main item contents + */ + +#define HID_MAIN_ITEM_CONSTANT 0x001 +#define HID_MAIN_ITEM_VARIABLE 0x002 +#define HID_MAIN_ITEM_RELATIVE 0x004 +#define HID_MAIN_ITEM_WRAP 0x008 +#define HID_MAIN_ITEM_NONLINEAR 0x010 +#define HID_MAIN_ITEM_NO_PREFERRED 0x020 +#define HID_MAIN_ITEM_NULL_STATE 0x040 +#define HID_MAIN_ITEM_VOLATILE 0x080 +#define HID_MAIN_ITEM_BUFFERED_BYTE 0x100 + +/* + * HID report descriptor collection item types + */ + +#define HID_COLLECTION_PHYSICAL 0 +#define HID_COLLECTION_APPLICATION 1 +#define HID_COLLECTION_LOGICAL 2 + +/* + * HID report descriptor global item tags + */ + +#define HID_GLOBAL_ITEM_TAG_USAGE_PAGE 0 +#define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM 1 +#define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM 2 +#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM 3 +#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM 4 +#define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT 5 +#define HID_GLOBAL_ITEM_TAG_UNIT 6 +#define HID_GLOBAL_ITEM_TAG_REPORT_SIZE 7 +#define HID_GLOBAL_ITEM_TAG_REPORT_ID 8 +#define HID_GLOBAL_ITEM_TAG_REPORT_COUNT 9 +#define HID_GLOBAL_ITEM_TAG_PUSH 10 +#define HID_GLOBAL_ITEM_TAG_POP 11 + +/* + * HID report descriptor local item tags + */ + +#define HID_LOCAL_ITEM_TAG_USAGE 0 +#define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM 1 +#define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM 2 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX 3 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM 4 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM 5 +#define HID_LOCAL_ITEM_TAG_STRING_INDEX 7 +#define HID_LOCAL_ITEM_TAG_STRING_MINIMUM 8 +#define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM 9 +#define HID_LOCAL_ITEM_TAG_DELIMITER 10 + +/* + * HID usage tables + */ + +#define HID_USAGE_PAGE 0xffff0000 + +#define HID_UP_GENDESK 0x00010000 +#define HID_UP_KEYBOARD 0x00070000 +#define HID_UP_LED 0x00080000 +#define HID_UP_BUTTON 0x00090000 +#define HID_UP_CONSUMER 0x000c0000 +#define HID_UP_DIGITIZER 0x000d0000 +#define HID_UP_PID 0x000f0000 + +#define HID_USAGE 0x0000ffff + +#define HID_GD_POINTER 0x00010001 +#define HID_GD_MOUSE 0x00010002 +#define HID_GD_JOYSTICK 0x00010004 +#define HID_GD_GAMEPAD 0x00010005 +#define HID_GD_HATSWITCH 0x00010039 + +/* + * HID report types --- Ouch! HID spec says 1 2 3! + */ + +#define HID_INPUT_REPORT 0 +#define HID_OUTPUT_REPORT 1 +#define HID_FEATURE_REPORT 2 + +/* + * HID device quirks. + */ + +#define HID_QUIRK_INVERT 0x01 +#define HID_QUIRK_NOTOUCH 0x02 + +/* + * This is the global enviroment of the parser. This information is + * persistent for main-items. The global enviroment can be saved and + * restored with PUSH/POP statements. + */ + +struct hid_global { + unsigned usage_page; + __s32 logical_minimum; + __s32 logical_maximum; + __s32 physical_minimum; + __s32 physical_maximum; + unsigned unit_exponent; + unsigned unit; + unsigned report_id; + unsigned report_size; + unsigned report_count; +}; + +/* + * This is the local enviroment. It is resistent up the the next main-item. + */ + +#define MAX_USAGES 1024 + +struct hid_local { + unsigned usage[MAX_USAGES]; /* usage array */ + unsigned usage_index; + unsigned usage_minimum; + unsigned delimiter_depth; + unsigned delimiter_branch; +}; + +/* + * This is the collection stack. We climb up the stack to determine + * application and function of each field. + */ + +struct hid_collection { + unsigned type; + unsigned usage; +}; + +struct hid_usage { + unsigned hid; /* hid usage code */ + __u16 code; /* input driver code */ + __u8 type; /* input driver type */ + __u8 hat; /* hat switch fun */ +}; + +struct hid_field { + unsigned physical; /* physical usage for this field */ + unsigned logical; /* logical usage for this field */ + struct hid_usage *usage; /* usage table for this function */ + unsigned maxusage; /* maximum usage index */ + unsigned flags; /* main-item flags (i.e. volatile,array,constant) */ + unsigned report_offset; /* bit offset in the report */ + unsigned report_size; /* size of this field in the report */ + unsigned report_count; /* number of this field in the report */ + unsigned report_type; /* (input,output,feature) */ + __s32 *value; /* last known value(s) */ + __s32 logical_minimum; + __s32 logical_maximum; + __s32 physical_minimum; + __s32 physical_maximum; + unsigned unit_exponent; + unsigned unit; + struct hid_report *report; /* associated report */ +}; + +#define HID_MAX_FIELDS 64 + +struct hid_report { + struct list_head list; + unsigned id; /* id of this report */ + unsigned type; /* report type */ + struct hid_field *field[HID_MAX_FIELDS]; /* fields of the report */ + unsigned maxfield; /* maximum valid field index */ + unsigned size; /* size of the report (bits) */ + struct hid_device *device; /* associated device */ +}; + +struct hid_report_enum { + unsigned numbered; + struct list_head report_list; + struct hid_report *report_id_hash[256]; +}; + +#define HID_REPORT_TYPES 3 + +struct hid_device { /* device report descriptor */ + __u8 *rdesc; + unsigned rsize; + unsigned application; /* HID application, i.e. Digitizer */ + unsigned version; /* HID version */ + unsigned country; /* HID country */ + struct hid_report_enum report_enum[HID_REPORT_TYPES]; + + struct usb_device *dev; /* USB device */ + int ifnum; /* USB interface number */ + + char buffer[32]; /* Receive buffer */ + char bufout[32]; /* Transmit buffer */ + devrequest dr; /* Startup packet */ + struct urb urb; /* USB URB structure */ + struct urb urbout; /* Output URB */ + struct input_dev input; /* input device structure */ + int open; /* is the device open by input? */ + int quirks; /* Various nasty tricks the device can pull on us */ + char name[128]; /* Device name */ +}; + +#define HID_GLOBAL_STACK_SIZE 4 +#define HID_COLLECTION_STACK_SIZE 4 + +struct hid_parser { + struct hid_global global; + struct hid_global global_stack[HID_GLOBAL_STACK_SIZE]; + unsigned global_stack_ptr; + struct hid_local local; + struct hid_collection collection_stack[HID_COLLECTION_STACK_SIZE]; + unsigned collection_stack_ptr; + struct hid_device *device; +}; + +struct hid_class_descriptor { + __u8 bDescriptorType; + __u16 wDescriptorLength; +} __attribute__ ((packed)); + +struct hid_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u16 bcdHID; + __u8 bCountryCode; + __u8 bNumDescriptors; + + struct hid_class_descriptor desc[1]; +} __attribute__ ((packed)); + + +#endif + diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/hub.c linux-2.2.17/drivers/usb/hub.c --- linux-2.2.17-orig/drivers/usb/hub.c Fri Apr 30 08:20:01 1999 +++ linux-2.2.17/drivers/usb/hub.c Sun Sep 24 04:26:23 2000 @@ -1,220 +1,317 @@ /* * USB hub driver. * - * This is horrible, it knows about the UHCI driver - * internals, but it's just meant as a rough example, - * let's do the virtualization later when this works. - * * (C) Copyright 1999 Linus Torvalds * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Gregory P. Smith */ +#include #include #include #include #include #include +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include +#include -#include +#include +#include +#include -#include "usb.h" -#include "uhci.h" #include "hub.h" -extern struct usb_operations uhci_device_operations; - /* Wakes up khubd */ -static struct wait_queue *usb_hub_wait = NULL; static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED; +static DECLARE_MUTEX(usb_address0_sem); -/* List of hubs needing servicing */ -static struct list_head hub_event_list; +static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */ +static LIST_HEAD(hub_list); /* List containing all of the hubs (for cleanup) */ -/* PID of khubd */ -static int khubd_pid = 0; +static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); +static int khubd_pid = 0; /* PID of khubd */ +static int khubd_running = 0; + +static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, + USB_DT_HUB << 8, 0, data, size, HZ); +} + +static int usb_clear_hub_feature(struct usb_device *dev, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, HZ); +} + +static int usb_clear_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ); +} + +static int usb_set_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ); +} + +static int usb_get_hub_status(struct usb_device *dev, void *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0, + data, sizeof(struct usb_hub_status), HZ); +} + +static int usb_get_port_status(struct usb_device *dev, int port, void *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port, + data, sizeof(struct usb_hub_status), HZ); +} /* * A irq handler returns non-zero to indicate to * the low-level driver that it wants to be re-activated, * or zero to say "I'm done". */ -static int hub_irq(int status, void *__buffer, void *dev_id) +static void hub_irq(struct urb *urb) { - struct usb_hub *hub = dev_id; + struct usb_hub *hub = (struct usb_hub *)urb->context; unsigned long flags; - if (waitqueue_active(&usb_hub_wait)) { + if (urb->status) { + if (urb->status != -ENOENT) + dbg("nonzero status in irq %d", urb->status); + + return; + } + + /* Something happened, let khubd figure it out */ + if (waitqueue_active(&khubd_wait)) { /* Add the hub to the event queue */ spin_lock_irqsave(&hub_event_lock, flags); if (hub->event_list.next == &hub->event_list) { list_add(&hub->event_list, &hub_event_list); /* Wake up khubd */ - wake_up(&usb_hub_wait); + wake_up(&khubd_wait); } spin_unlock_irqrestore(&hub_event_lock, flags); } +} - return 1; +static void usb_hub_power_on(struct usb_hub *hub) +{ + int i; + + /* Enable power to the ports */ + dbg("enabling power on all ports"); + for (i = 0; i < hub->nports; i++) + usb_set_port_feature(hub->dev, i + 1, USB_PORT_FEAT_POWER); } -static void usb_hub_configure(struct usb_hub *hub) +static int usb_hub_configure(struct usb_hub *hub) { struct usb_device *dev = hub->dev; - unsigned char hubdescriptor[8], buf[4]; - int charac, i; + unsigned char buffer[HUB_DESCRIPTOR_MAX_SIZE], *bitmap; + struct usb_hub_descriptor *descriptor; + struct usb_descriptor_header *header; + struct usb_hub_status *hubsts; + int i, ret; + + /* Request the entire hub descriptor. */ + header = (struct usb_descriptor_header *)buffer; + ret = usb_get_hub_descriptor(dev, buffer, sizeof(buffer)); + /* is large enough for a hub with 127 ports; + * the hub can/will return fewer bytes here. */ + if (ret < 0) { + err("Unable to get hub descriptor (err = %d)", ret); + return -1; + } - usb_set_configuration(dev, dev->config[0].bConfigurationValue); + bitmap = kmalloc(header->bLength, GFP_KERNEL); + if (!bitmap) { + err("Unable to kmalloc %d bytes for bitmap", header->bLength); + return -1; + } - if (usb_get_hub_descriptor(dev, hubdescriptor, 8)) - return; + memcpy (bitmap, buffer, header->bLength); + descriptor = (struct usb_hub_descriptor *)bitmap; - hub->nports = dev->maxchild = hubdescriptor[2]; - printk("hub: %d-port%s detected\n", hub->nports, - (hub->nports == 1) ? "" : "s"); + hub->nports = dev->maxchild = descriptor->bNbrPorts; + info("%d port%s detected", hub->nports, (hub->nports == 1) ? "" : "s"); - charac = (hubdescriptor[4] << 8) + hubdescriptor[3]; - switch (charac & HUB_CHAR_LPSM) { + switch (descriptor->wHubCharacteristics & HUB_CHAR_LPSM) { case 0x00: - printk("hub: ganged power switching\n"); + dbg("ganged power switching"); break; case 0x01: - printk("hub: individual port power switching\n"); + dbg("individual port power switching"); break; case 0x02: case 0x03: - printk("hub: unknown reserved power switching mode\n"); + dbg("unknown reserved power switching mode"); break; } - if (charac & HUB_CHAR_COMPOUND) - printk("hub: part of a compound device\n"); + if (descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) + dbg("part of a compound device"); else - printk("hub: standalone hub\n"); + dbg("standalone hub"); - switch (charac & HUB_CHAR_OCPM) { + switch (descriptor->wHubCharacteristics & HUB_CHAR_OCPM) { case 0x00: - printk("hub: global over current protection\n"); + dbg("global over-current protection"); break; case 0x08: - printk("hub: individual port over current protection\n"); + dbg("individual port over-current protection"); break; case 0x10: case 0x18: - printk("hub: no over current protection\n"); + dbg("no over-current protection"); break; } - printk("hub: power on to power good time: %dms\n", - hubdescriptor[5] * 2); - - printk("hub: hub controller current requirement: %dmA\n", - hubdescriptor[6]); + dbg("power on to power good time: %dms", descriptor->bPwrOn2PwrGood * 2); + dbg("hub controller current requirement: %dmA", descriptor->bHubContrCurrent); for (i = 0; i < dev->maxchild; i++) - printk("hub: port %d is%s removable\n", i + 1, - hubdescriptor[7 + ((i + 1)/8)] & (1 << ((i + 1) % 8)) + dbg("port %d is%s removable", i + 1, + bitmap[7 + ((i + 1)/8)] & (1 << ((i + 1) % 8)) ? " not" : ""); - if (usb_get_hub_status(dev, buf)) - return; - - printk("hub: local power source is %s\n", - (buf[0] & 1) ? "lost (inactive)" : "good"); + kfree(bitmap); - printk("hub: %sover current condition exists\n", - (buf[0] & 2) ? "" : "no "); + ret = usb_get_hub_status(dev, buffer); + if (ret < 0) { + err("Unable to get hub status (err = %d)", ret); + return -1; + } -#if 0 - for (i = 0; i < hub->nports; i++) { - int portstat, portchange; - unsigned char portstatus[4]; + hubsts = (struct usb_hub_status *)buffer; + dbg("local power source is %s", + (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good"); - if (usb_get_port_status(dev, i + 1, portstatus)) - return; - portstat = (portstatus[1] << 8) + portstatus[0]; - portchange = (portstatus[3] << 8) + portstatus[2]; + dbg("%sover-current condition exists", + (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? "" : "no "); - printk("hub: port %d status\n", i + 1); - printk("hub: %sdevice present\n", (portstat & 1) ? "" : "no "); - printk("hub: %s\n", (portstat & 2) ? "enabled" : "disabled"); - printk("hub: %ssuspended\n", (portstat & 4) ? "" : "not "); - printk("hub: %sover current\n", (portstat & 8) ? "" : "not "); - printk("hub: has %spower\n", (portstat & 0x100) ? "" : "no "); - printk("hub: %s speed\n", (portstat & 0x200) ? "low" : "full"); - } -#endif + usb_hub_power_on(hub); - /* Enable power to the ports */ - printk("enabling power on all ports\n"); - for (i = 0; i < hub->nports; i++) - usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); + return 0; } -static int hub_probe(struct usb_device *dev) +static void *hub_probe(struct usb_device *dev, unsigned int i) { struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; struct usb_hub *hub; + unsigned long flags; + unsigned int pipe; + int maxp, ret; - /* We don't handle multi-config hubs */ - if (dev->descriptor.bNumConfigurations != 1) - return -1; - - /* We don't handle multi-interface hubs */ - if (dev->config[0].bNumInterfaces != 1) - return -1; - - interface = &dev->config[0].interface[0]; + interface = &dev->actconfig->interface[i].altsetting[0]; /* Is it a hub? */ - if (interface->bInterfaceClass != 9) - return -1; + if (interface->bInterfaceClass != USB_CLASS_HUB) + return NULL; + + /* Some hubs have a subclass of 1, which AFAICT according to the */ + /* specs is not defined, but it works */ if ((interface->bInterfaceSubClass != 0) && (interface->bInterfaceSubClass != 1)) - return -1; + return NULL; /* Multiple endpoints? What kind of mutant ninja-hub is this? */ if (interface->bNumEndpoints != 1) - return -1; + return NULL; endpoint = &interface->endpoint[0]; /* Output endpoint? Curiousier and curiousier.. */ - if (!(endpoint->bEndpointAddress & 0x80)) - return -1; + if (!(endpoint->bEndpointAddress & USB_DIR_IN)) { + err("Device is hub class, but has output endpoint?"); + return NULL; + } /* If it's not an interrupt endpoint, we'd better punt! */ - if ((endpoint->bmAttributes & 3) != 3) - return -1; + if ((endpoint->bmAttributes & 3) != 3) { + err("Device is hub class, but has endpoint other than interrupt?"); + return NULL; + } /* We found a hub */ - printk("USB hub found\n"); + info("USB hub found"); - if ((hub = kmalloc(sizeof(*hub), GFP_KERNEL)) == NULL) { - printk("couldn't kmalloc hub struct\n"); - return -1; + hub = kmalloc(sizeof(*hub), GFP_KERNEL); + if (!hub) { + err("couldn't kmalloc hub struct"); + return NULL; } memset(hub, 0, sizeof(*hub)); - dev->private = hub; - INIT_LIST_HEAD(&hub->event_list); hub->dev = dev; - usb_hub_configure(hub); + /* Record the new hub's existence */ + spin_lock_irqsave(&hub_event_lock, flags); + INIT_LIST_HEAD(&hub->hub_list); + list_add(&hub->hub_list, &hub_list); + spin_unlock_irqrestore(&hub_event_lock, flags); - usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), hub_irq, endpoint->bInterval, hub); + if (usb_hub_configure(hub) >= 0) { + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + if (maxp > sizeof(hub->buffer)) + maxp = sizeof(hub->buffer); + + hub->urb = usb_alloc_urb(0); + if (!hub->urb) { + err("couldn't allocate interrupt urb"); + goto fail; + } + + FILL_INT_URB(hub->urb, dev, pipe, hub->buffer, maxp, hub_irq, + hub, endpoint->bInterval); + ret = usb_submit_urb(hub->urb); + if (ret) { + err("usb_submit_urb failed (%d)", ret); + goto fail; + } /* Wake up khubd */ - wake_up(&usb_hub_wait); + wake_up(&khubd_wait); + } - return 0; + return hub; + +fail: + /* free hub, but first clean up its list. */ + spin_lock_irqsave(&hub_event_lock, flags); + + /* Delete it and then reset it */ + list_del(&hub->event_list); + INIT_LIST_HEAD(&hub->event_list); + list_del(&hub->hub_list); + INIT_LIST_HEAD(&hub->hub_list); + + spin_unlock_irqrestore(&hub_event_lock, flags); + + kfree(hub); + + return NULL; } -static void hub_disconnect(struct usb_device *dev) +static void hub_disconnect(struct usb_device *dev, void *ptr) { - struct usb_hub *hub = dev->private; + struct usb_hub *hub = (struct usb_hub *)ptr; unsigned long flags; spin_lock_irqsave(&hub_event_lock, flags); @@ -222,201 +319,478 @@ /* Delete it and then reset it */ list_del(&hub->event_list); INIT_LIST_HEAD(&hub->event_list); + list_del(&hub->hub_list); + INIT_LIST_HEAD(&hub->hub_list); spin_unlock_irqrestore(&hub_event_lock, flags); + if (hub->urb) { + usb_unlink_urb(hub->urb); + usb_free_urb(hub->urb); + hub->urb = NULL; + } + /* Free the memory */ kfree(hub); } +static int hub_ioctl (struct usb_device *hub, unsigned int code, void *user_data) +{ + /* assert ifno == 0 (part of hub spec) */ + switch (code) { + case USBDEVFS_HUB_PORTINFO: { + struct usbdevfs_hub_portinfo *info = user_data; + unsigned long flags; + int i; + + spin_lock_irqsave (&hub_event_lock, flags); + if (hub->devnum <= 0) + info->nports = 0; + else { + info->nports = hub->maxchild; + for (i = 0; i < info->nports; i++) { + if (hub->children [i] == NULL) + info->port [i] = 0; + else + info->port [i] = hub->children [i]->devnum; + } + } + spin_unlock_irqrestore (&hub_event_lock, flags); + + return info->nports + 1; + } + + default: + return -ENOSYS; + } +} + static void usb_hub_port_connect_change(struct usb_device *hub, int port) { struct usb_device *usb; - unsigned char buf[4]; + struct usb_port_status portsts; unsigned short portstatus, portchange; + int ret, tries; + + wait_ms(100); + + ret = usb_get_port_status(hub, port + 1, &portsts); + if (ret < 0) { + err("get_port_status(%d) failed (err = %d)", port + 1, ret); + return; + } + portstatus = le16_to_cpu(portsts.wPortStatus); + portchange = le16_to_cpu(portsts.wPortChange); + dbg("portstatus %x, change %x, %s", portstatus, portchange, + portstatus&(1<children[port])) { usb_disconnect(&hub->children[port]); + /* Return now if nothing is connected */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) + return; + } + wait_ms(400); - usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); + down(&usb_address0_sem); - wait_ms(50); /* FIXME: This is from the *BSD stack, thanks! :) */ +#define MAX_TRIES 5 + /* Reset the port */ + for (tries = 0; tries < MAX_TRIES ; tries++) { + usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); + wait_ms(200); - if (usb_get_port_status(hub, port + 1, buf)) { - printk("get_port_status failed\n"); - return; + ret = usb_get_port_status(hub, port + 1, &portsts); + if (ret < 0) { + err("get_port_status(%d) failed (err = %d)", port + 1, ret); + goto out; } - portstatus = *((unsigned short *)buf + 0); - portchange = *((unsigned short *)buf + 1); + portstatus = le16_to_cpu(portsts.wPortStatus); + portchange = le16_to_cpu(portsts.wPortChange); + dbg("portstatus %x, change %x, %s", portstatus ,portchange, + portstatus&(1<bus->op->allocate(hub); - if (!usb) { - printk("couldn't allocate usb_device\n"); - return; + if (tries >= MAX_TRIES) { + err("Cannot enable port %i after %i retries, disabling port.", port+1, MAX_TRIES); + err("Maybe the USB cable is bad?"); + goto out; } - usb_connect(usb); + usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET); + + /* Allocate a new device struct for it */ + usb = usb_alloc_dev(hub, hub->bus); + if (!usb) { + err("couldn't allocate usb_device"); + goto out; + } usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0; hub->children[port] = usb; - usb_new_device(usb); + /* Find a new device ID for it */ + usb_connect(usb); + + /* Run it through the hoops (find a driver, etc) */ + ret = usb_new_device(usb); + if (ret) { + /* Try resetting the device. Windows does this and it */ + /* gets some devices working correctly */ + usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); + + ret = usb_new_device(usb); + if (ret) { + usb_disconnect(&hub->children[port]); + + /* Woops, disable the port */ + dbg("hub: disabling port %d", port + 1); + usb_clear_port_feature(hub, port + 1, + USB_PORT_FEAT_ENABLE); + } + } + +out: + up(&usb_address0_sem); } static void usb_hub_events(void) { unsigned long flags; - unsigned char buf[4]; - unsigned short portstatus, portchange; int i; - struct list_head *next, *tmp, *head = &hub_event_list; + struct list_head *tmp; struct usb_device *dev; struct usb_hub *hub; + struct usb_hub_status hubsts; + unsigned short hubstatus, hubchange; + /* + * We restart the list everytime to avoid a deadlock with + * deleting hubs downstream from this one. This should be + * safe since we delete the hub from the event list. + * Not the most efficient, but avoids deadlocks. + */ + while (1) { spin_lock_irqsave(&hub_event_lock, flags); - tmp = head->next; - while (tmp != head) { + if (list_empty(&hub_event_list)) + goto he_unlock; + + /* Grab the next entry from the beginning of the list */ + tmp = hub_event_list.next; + hub = list_entry(tmp, struct usb_hub, event_list); dev = hub->dev; - next = tmp->next; - list_del(tmp); INIT_LIST_HEAD(tmp); + spin_unlock_irqrestore(&hub_event_lock, flags); + for (i = 0; i < hub->nports; i++) { - if (usb_get_port_status(dev, i + 1, buf)) { - printk("get_port_status failed\n"); + struct usb_port_status portsts; + unsigned short portstatus, portchange; + + if (usb_get_port_status(dev, i + 1, &portsts) < 0) { + err("get_port_status failed"); continue; } - portstatus = *((unsigned short *)buf + 0); - portchange = *((unsigned short *)buf + 1); + portstatus = le16_to_cpu(portsts.wPortStatus); + portchange = le16_to_cpu(portsts.wPortChange); if (portchange & USB_PORT_STAT_C_CONNECTION) { - printk("hub: port %d connection change\n", i + 1); - - usb_clear_port_feature(dev, i + 1, - USB_PORT_FEAT_C_CONNECTION); + dbg("port %d connection change", i + 1); usb_hub_port_connect_change(dev, i); } if (portchange & USB_PORT_STAT_C_ENABLE) { - printk("hub: port %d enable change\n", i + 1); - usb_clear_port_feature(dev, i + 1, - USB_PORT_FEAT_C_ENABLE); + dbg("port %d enable change, status %x", i + 1, portstatus); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); + + // EM interference sometimes causes bad shielded USB devices to + // be shutdown by the hub, this hack enables them again. + // Works at least with mouse driver. + if (!(portstatus & USB_PORT_STAT_ENABLE) && + (portstatus & USB_PORT_STAT_CONNECTION) && (dev->children[i])) { + err("already running port %i disabled by hub (EMI?), re-enabling...", + i + 1); + usb_hub_port_connect_change(dev, i); + } } - if (portchange & USB_PORT_STAT_C_SUSPEND) - printk("hub: port %d suspend change\n", i + 1); + if (portstatus & USB_PORT_STAT_SUSPEND) { + dbg("port %d suspend change", i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_SUSPEND); + } - if (portchange & USB_PORT_STAT_C_OVERCURRENT) - printk("hub: port %d over-current change\n", i + 1); + if (portchange & USB_PORT_STAT_C_OVERCURRENT) { + err("port %d over-current change", i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT); + usb_hub_power_on(hub); + } if (portchange & USB_PORT_STAT_C_RESET) { - printk("hub: port %d reset change\n", i + 1); - usb_clear_port_feature(dev, i + 1, - USB_PORT_FEAT_C_RESET); + dbg("port %d reset change", i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); } + } /* end for i */ -#if 0 - if (!portchange) - continue; - - if (usb_get_port_status(dev, i + 1, buf)) - return; - - portstatus = (buf[1] << 8) + buf[0]; - portchange = (buf[3] << 8) + buf[2]; - - printk("hub: port %d status\n", i + 1); - printk("hub: %sdevice present\n", (portstatus & 1) ? "" : "no "); - printk("hub: %s\n", (portstatus & 2) ? "enabled" : "disabled"); - printk("hub: %ssuspended\n", (portstatus & 4) ? "" : "not "); - printk("hub: %sover current\n", (portstatus & 8) ? "" : "not "); - printk("hub: has %spower\n", (portstatus & 0x100) ? "" : "no "); - printk("hub: %s speed\n", (portstatus & 0x200) ? "low" : "full"); -#endif + /* deal with hub status changes */ + if (usb_get_hub_status(dev, &hubsts) < 0) { + err("get_hub_status failed"); + } else { + hubstatus = le16_to_cpup(&hubsts.wHubStatus); + hubchange = le16_to_cpup(&hubsts.wHubChange); + if (hubchange & HUB_CHANGE_LOCAL_POWER) { + dbg("hub power change"); + usb_clear_hub_feature(dev, C_HUB_LOCAL_POWER); + } + if (hubchange & HUB_CHANGE_OVERCURRENT) { + dbg("hub overcurrent change"); + wait_ms(500); //Cool down + usb_clear_hub_feature(dev, C_HUB_OVER_CURRENT); + usb_hub_power_on(hub); } - tmp = next; -#if 0 - wait_ms(1000); -#endif } + } /* end while (1) */ +he_unlock: spin_unlock_irqrestore(&hub_event_lock, flags); } static int usb_hub_thread(void *__hub) { + khubd_running = 1; + lock_kernel(); /* * This thread doesn't need any user-level access, * so get rid of all our resources */ - printk("usb_hub_thread at %p\n", &usb_hub_thread); - exit_mm(current); - exit_files(current); - exit_fs(current); + exit_files(current); /* daemonize doesn't do exit_files */ + daemonize(); /* Setup a nice name */ strcpy(current->comm, "khubd"); /* Send me a signal to get me die (for debugging) */ do { - interruptible_sleep_on(&usb_hub_wait); usb_hub_events(); + interruptible_sleep_on(&khubd_wait); } while (!signal_pending(current)); - printk("usb_hub_thread exiting\n"); + dbg("usb_hub_thread exiting"); + khubd_running = 0; return 0; } static struct usb_driver hub_driver = { - "hub", - hub_probe, - hub_disconnect, - { NULL, NULL } + name: "hub", + probe: hub_probe, + ioctl: hub_ioctl, + disconnect: hub_disconnect }; /* * This should be a separate module. */ -int hub_init(void) +int usb_hub_init(void) { int pid; - INIT_LIST_HEAD(&hub_event_list); + if (usb_register(&hub_driver) < 0) { + err("Unable to register USB hub driver"); + return -1; + } - usb_register(&hub_driver); - pid = kernel_thread(usb_hub_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + pid = kernel_thread(usb_hub_thread, NULL, + CLONE_FS | CLONE_FILES | CLONE_SIGHAND); if (pid >= 0) { khubd_pid = pid; + return 0; } /* Fall through if kernel_thread failed */ usb_deregister(&hub_driver); - return 0; + return -1; } -void hub_cleanup(void) +void usb_hub_cleanup(void) { - if (khubd_pid >= 0) - kill_proc(khubd_pid, SIGINT, 1); + int ret; + + /* Kill the thread */ + ret = kill_proc(khubd_pid, SIGTERM, 1); + if (!ret) { + /* Wait 10 seconds */ + int count = 10 * 100; + while (khubd_running && --count) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + if (!count) + err("giving up on killing khubd"); + } + + /* + * Hub resources are freed for us by usb_deregister. It + * usb_driver_purge on every device which in turn calls that + * devices disconnect function if it is using this driver. + * The hub_disconnect function takes care of releasing the + * individual hub resources. -greg + */ usb_deregister(&hub_driver); +} /* usb_hub_cleanup() */ + +/* + * WARNING - If a driver calls usb_reset_device, you should simulate a + * disconnect() and probe() for other interfaces you doesn't claim. This + * is left up to the driver writer right now. This insures other drivers + * have a chance to re-setup their interface. + * + * Take a look at proc_resetdevice in devio.c for some sample code to + * do this. + */ +int usb_reset_device(struct usb_device *dev) +{ + struct usb_device *parent = dev->parent; + struct usb_device_descriptor descriptor; + int i, ret, port = -1; + + if (!parent) { + err("attempting to reset root hub!"); + return -EINVAL; + } + + for (i = 0; i < parent->maxchild; i++) + if (parent->children[i] == dev) { + port = i; + break; + } + + if (port < 0) + return -ENOENT; + + down(&usb_address0_sem); + + /* Send a reset to the device */ + usb_set_port_feature(parent, port + 1, USB_PORT_FEAT_RESET); + + wait_ms(200); + + usb_clear_port_feature(parent, port + 1, USB_PORT_FEAT_C_RESET); + + /* Reprogram the Address */ + ret = usb_set_address(dev); + if (ret < 0) { + err("USB device not accepting new address (error=%d)", ret); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + up(&usb_address0_sem); + return ret; + } + + wait_ms(10); /* Let the SET_ADDRESS settle */ + + up(&usb_address0_sem); + + /* + * Now we fetch the configuration descriptors for the device and + * see if anything has changed. If it has, we dump the current + * parsed descriptors and reparse from scratch. Then we leave + * the device alone for the caller to finish setting up. + * + * If nothing changed, we reprogram the configuration and then + * the alternate settings. + */ + ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &descriptor, + sizeof(descriptor)); + if (ret < 0) + return ret; + + le16_to_cpus(&descriptor.bcdUSB); + le16_to_cpus(&descriptor.idVendor); + le16_to_cpus(&descriptor.idProduct); + le16_to_cpus(&descriptor.bcdDevice); + + if (memcmp(&dev->descriptor, &descriptor, sizeof(descriptor))) { + usb_destroy_configuration(dev); + + ret = usb_get_device_descriptor(dev); + if (ret < sizeof(dev->descriptor)) { + if (ret < 0) + err("unable to get device descriptor (error=%d)", ret); + else + err("USB device descriptor short read (expected %i, got %i)", sizeof(dev->descriptor), ret); + + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + return -EIO; + } + + ret = usb_get_configuration(dev); + if (ret < 0) { + err("unable to get configuration (error=%d)", ret); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + return 1; + } + + dev->actconfig = dev->config; + usb_set_maxpacket(dev); + + return 1; + } else { + ret = usb_set_configuration(dev, + dev->actconfig->bConfigurationValue); + if (ret < 0) { + err("failed to set active configuration (error=%d)", + ret); + return ret; + } + + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { + struct usb_interface *intf = + &dev->actconfig->interface[i]; + struct usb_interface_descriptor *as = + &intf->altsetting[intf->act_altsetting]; + + ret = usb_set_interface(dev, as->bInterfaceNumber, + as->bAlternateSetting); + if (ret < 0) { + err("failed to set active alternate setting for interface %d (error=%d)", i, ret); + return ret; + } + } + + return 0; + } + + return 0; } + diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/hub.h linux-2.2.17/drivers/usb/hub.h --- linux-2.2.17-orig/drivers/usb/hub.h Wed Apr 21 05:41:59 1999 +++ linux-2.2.17/drivers/usb/hub.h Sun Sep 24 04:26:23 2000 @@ -4,7 +4,14 @@ #include /* - * Hub feature numbers + * Hub request types + */ + +#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE) +#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER) + +/* + * Hub Class feature numbers */ #define C_HUB_LOCAL_POWER 0 #define C_HUB_OVER_CURRENT 1 @@ -12,6 +19,7 @@ /* * Port feature numbers */ +#define USB_PORT_FEAT_CONNECTION 0 #define USB_PORT_FEAT_ENABLE 1 #define USB_PORT_FEAT_SUSPEND 2 #define USB_PORT_FEAT_OVER_CURRENT 3 @@ -24,7 +32,12 @@ #define USB_PORT_FEAT_C_OVER_CURRENT 19 #define USB_PORT_FEAT_C_RESET 20 -/* wPortStatus */ +struct usb_port_status { + __u16 wPortStatus; + __u16 wPortChange; +} __attribute__ ((packed)); + +/* wPortStatus bits */ #define USB_PORT_STAT_CONNECTION 0x0001 #define USB_PORT_STAT_ENABLE 0x0002 #define USB_PORT_STAT_SUSPEND 0x0004 @@ -33,18 +46,46 @@ #define USB_PORT_STAT_POWER 0x0100 #define USB_PORT_STAT_LOW_SPEED 0x0200 -/* wPortChange */ +/* wPortChange bits */ #define USB_PORT_STAT_C_CONNECTION 0x0001 #define USB_PORT_STAT_C_ENABLE 0x0002 #define USB_PORT_STAT_C_SUSPEND 0x0004 #define USB_PORT_STAT_C_OVERCURRENT 0x0008 #define USB_PORT_STAT_C_RESET 0x0010 -/* Characteristics */ +/* wHubCharacteristics (masks) */ #define HUB_CHAR_LPSM 0x0003 #define HUB_CHAR_COMPOUND 0x0004 #define HUB_CHAR_OCPM 0x0018 +struct usb_hub_status { + __u16 wHubStatus; + __u16 wHubChange; +} __attribute__ ((packed)); + +/* + *Hub Status & Hub Change bit masks + */ +#define HUB_STATUS_LOCAL_POWER 0x0001 +#define HUB_STATUS_OVERCURRENT 0x0002 + +#define HUB_CHANGE_LOCAL_POWER 0x0001 +#define HUB_CHANGE_OVERCURRENT 0x0002 + +#define HUB_DESCRIPTOR_MAX_SIZE 39 /* enough for 127 ports on a hub */ + +/* Hub descriptor */ +struct usb_hub_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bNbrPorts; + __u16 wHubCharacteristics; + __u8 bPwrOn2PwrGood; + __u8 bHubContrCurrent; + /* DeviceRemovable and PortPwrCtrlMask want to be variable-length + bitmaps that hold max 256 entries, but for now they're ignored */ +} __attribute__ ((packed)); + struct usb_device; typedef enum { @@ -67,6 +108,14 @@ /* Device structure */ struct usb_device *dev; + /* Interrupt polling pipe */ + struct urb *urb; + + char buffer[USB_MAXCHILDREN / 8]; + + /* List of hubs */ + struct list_head hub_list; + /* Temporary event list */ struct list_head event_list; @@ -77,4 +126,3 @@ }; #endif - diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/ibmcam.c linux-2.2.17/drivers/usb/ibmcam.c --- linux-2.2.17-orig/drivers/usb/ibmcam.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/ibmcam.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,3146 @@ +/* + * USB IBM C-It Video Camera driver + * + * Supports IBM C-It Video Camera. + * + * This driver is based on earlier work of: + * + * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Randy Dunlap + * + * 5/24/00 Removed optional (and unnecessary) locking of the driver while + * the device remains plugged in. Corrected race conditions in ibmcam_open + * and ibmcam_probe() routines using this as a guideline: + * + * (2) The big kernel lock is automatically released when a process sleeps + * in the kernel and is automatically reacquired on reschedule if the + * process had the lock originally. Any code that can be compiled as + * a module and is entered with the big kernel lock held *MUST* + * increment the use count to activate the indirect module protection + * before doing anything that might sleep. + * + * In practice, this means that all routines that live in modules and + * are invoked under the big kernel lock should do MOD_INC_USE_COUNT + * as their very first action. And all failure paths from that + * routine must do MOD_DEC_USE_COUNT before returning. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ibmcam.h" + +#define ENABLE_HEXDUMP 0 /* Enable if you need it */ +static int debug = 0; + +/* Completion states of the data parser */ +typedef enum { + scan_Continue, /* Just parse next item */ + scan_NextFrame, /* Frame done, send it to V4L */ + scan_Out, /* Not enough data for frame */ + scan_EndParse /* End parsing */ +} scan_state_t; + +/* Bit flags (options) */ +#define FLAGS_RETRY_VIDIOCSYNC (1 << 0) +#define FLAGS_MONOCHROME (1 << 1) +#define FLAGS_DISPLAY_HINTS (1 << 2) +#define FLAGS_OVERLAY_STATS (1 << 3) +#define FLAGS_FORCE_TESTPATTERN (1 << 4) +#define FLAGS_SEPARATE_FRAMES (1 << 5) +#define FLAGS_CLEAN_FRAMES (1 << 6) + +static int flags = 0; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */ + +/* This is the size of V4L frame that we provide */ +static const int imgwidth = V4L_FRAME_WIDTH_USED; +static const int imgheight = V4L_FRAME_HEIGHT; +static const int min_imgwidth = 8; +static const int min_imgheight = 4; + +static int lighting = 1; /* Medium */ + +#define SHARPNESS_MIN 0 +#define SHARPNESS_MAX 6 +static int sharpness = 4; /* Low noise, good details */ + +#define FRAMERATE_MIN 0 +#define FRAMERATE_MAX 6 +static int framerate = 2; /* Lower, reliable frame rate (8-12 fps) */ + +enum { + VIDEOSIZE_128x96 = 0, + VIDEOSIZE_176x144, + VIDEOSIZE_352x288, + VIDEOSIZE_320x240, + VIDEOSIZE_352x240, +}; + +static int videosize = VIDEOSIZE_352x288; + +/* + * The value of 'scratchbufsize' affects quality of the picture + * in many ways. Shorter buffers may cause loss of data when client + * is too slow. Larger buffers are memory-consuming and take longer + * to work with. This setting can be adjusted, but the default value + * should be OK for most desktop users. + */ +#define DEFAULT_SCRATCH_BUF_SIZE (0x10000) /* 64 KB */ +static const int scratchbufsize = DEFAULT_SCRATCH_BUF_SIZE; + +/* + * Here we define several initialization variables. They may + * be used to automatically set color, hue, brightness and + * contrast to desired values. This is particularly useful in + * case of webcams (which have no controls and no on-screen + * output) and also when a client V4L software is used that + * does not have some of those controls. In any case it's + * good to have startup values as options. + * + * These values are all in [0..255] range. This simplifies + * operation. Note that actual values of V4L variables may + * be scaled up (as much as << 8). User can see that only + * on overlay output, however, or through a V4L client. + */ +static int init_brightness = 128; +static int init_contrast = 192; +static int init_color = 128; +static int init_hue = 128; +static int hue_correction = 128; + +/* Settings for camera model 2 */ +static int init_model2_rg = -1; +static int init_model2_rg2 = -1; +static int init_model2_sat = -1; +static int init_model2_yb = -1; + +MODULE_PARM(debug, "i"); +MODULE_PARM(flags, "i"); +MODULE_PARM(framerate, "i"); +MODULE_PARM(lighting, "i"); +MODULE_PARM(sharpness, "i"); +MODULE_PARM(videosize, "i"); +MODULE_PARM(init_brightness, "i"); +MODULE_PARM(init_contrast, "i"); +MODULE_PARM(init_color, "i"); +MODULE_PARM(init_hue, "i"); +MODULE_PARM(hue_correction, "i"); + +MODULE_PARM(init_model2_rg, "i"); +MODULE_PARM(init_model2_rg2, "i"); +MODULE_PARM(init_model2_sat, "i"); +MODULE_PARM(init_model2_yb, "i"); + +MODULE_AUTHOR ("module author"); +MODULE_DESCRIPTION ("IBM/Xirlink C-it USB Camera Driver for Linux (c) 2000"); + +/* Still mysterious i2c commands */ +static const unsigned short unknown_88 = 0x0088; +static const unsigned short unknown_89 = 0x0089; +static const unsigned short bright_3x[3] = { 0x0031, 0x0032, 0x0033 }; +static const unsigned short contrast_14 = 0x0014; +static const unsigned short light_27 = 0x0027; +static const unsigned short sharp_13 = 0x0013; + +/* i2c commands for Model 2 cameras */ +static const unsigned short mod2_brightness = 0x001a; /* $5b .. $ee; default=$5a */ +static const unsigned short mod2_set_framerate = 0x001c; /* 0 (fast).. $1F (slow) */ +static const unsigned short mod2_color_balance_rg2 = 0x001e; /* 0 (red) .. $7F (green) */ +static const unsigned short mod2_saturation = 0x0020; /* 0 (b/w) - $7F (full color) */ +static const unsigned short mod2_color_balance_yb = 0x0022; /* 0..$7F, $50 is about right */ +static const unsigned short mod2_color_balance_rg = 0x0024; /* 0..$7F, $70 is about right */ +static const unsigned short mod2_sensitivity = 0x0028; /* 0 (min) .. $1F (max) */ + +#define MAX_IBMCAM 4 + +struct usb_ibmcam cams[MAX_IBMCAM]; + +/*******************************/ +/* Memory management functions */ +/*******************************/ + +#define MDEBUG(x) do { } while(0) /* Debug memory management */ + +static struct usb_driver ibmcam_driver; +static void usb_ibmcam_release(struct usb_ibmcam *ibmcam); + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if (pte_present(pte)) + ret = page_address(pte_page(pte)) | (adr & (PAGE_SIZE-1)); + } + } + MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); + return ret; +} + +static inline unsigned long uvirt_to_bus(unsigned long adr) +{ + unsigned long kva, ret; + + kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +static inline unsigned long kvirt_to_bus(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); + return ret; +} + +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr, page; + + /* Round it off to PAGE_SIZE */ + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + 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) { + page = kvirt_to_pa(adr); + mem_map_reserve(MAP_NR(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr, page; + + if (!mem) + return; + + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + adr=(unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_unreserve(MAP_NR(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + vfree(mem); +} + +#if ENABLE_HEXDUMP +static void ibmcam_hexdump(const unsigned char *data, int len) +{ + char tmp[80]; + int i, k; + + for (i=k=0; len > 0; i++, len--) { + if (i > 0 && (i%16 == 0)) { + printk("%s\n", tmp); + k=0; + } + k += sprintf(&tmp[k], "%02x ", data[i]); + } + if (k > 0) + printk("%s\n", tmp); +} +#endif + +/* + * usb_ibmcam_overlaychar() + * + * History: + * 1/2/00 Created. + */ +void usb_ibmcam_overlaychar( + struct usb_ibmcam *ibmcam, + struct ibmcam_frame *frame, + int x, int y, int ch) +{ + static const unsigned short digits[16] = { + 0xF6DE, /* 0 */ + 0x2492, /* 1 */ + 0xE7CE, /* 2 */ + 0xE79E, /* 3 */ + 0xB792, /* 4 */ + 0xF39E, /* 5 */ + 0xF3DE, /* 6 */ + 0xF492, /* 7 */ + 0xF7DE, /* 8 */ + 0xF79E, /* 9 */ + 0x77DA, /* a */ + 0xD75C, /* b */ + 0xF24E, /* c */ + 0xD6DC, /* d */ + 0xF34E, /* e */ + 0xF348 /* f */ + }; + unsigned short digit; + int ix, iy; + + if ((ibmcam == NULL) || (frame == NULL)) + return; + + if (ch >= '0' && ch <= '9') + ch -= '0'; + else if (ch >= 'A' && ch <= 'F') + ch = 10 + (ch - 'A'); + else if (ch >= 'a' && ch <= 'f') + ch = 10 + (ch - 'a'); + else + return; + digit = digits[ch]; + + for (iy=0; iy < 5; iy++) { + for (ix=0; ix < 3; ix++) { + if (digit & 0x8000) { + IBMCAM_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF); + } + digit = digit << 1; + } + } +} + +/* + * usb_ibmcam_overlaystring() + * + * History: + * 1/2/00 Created. + */ +void usb_ibmcam_overlaystring( + struct usb_ibmcam *ibmcam, + struct ibmcam_frame *frame, + int x, int y, const char *str) +{ + while (*str) { + usb_ibmcam_overlaychar(ibmcam, frame, x, y, *str); + str++; + x += 4; /* 3 pixels character + 1 space */ + } +} + +/* + * usb_ibmcam_overlaystats() + * + * Overlays important debugging information. + * + * History: + * 1/2/00 Created. + */ +void usb_ibmcam_overlaystats(struct usb_ibmcam *ibmcam, struct ibmcam_frame *frame) +{ + const int y_diff = 8; + char tmp[16]; + int x = 10; + int y = 10; + + sprintf(tmp, "%8x", ibmcam->frame_num); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->urb_count); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->urb_length); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->data_count); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->header_count); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->scratch_ovf_count); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->iso_skip_count); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->iso_err_count); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", ibmcam->vpic.colour); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", ibmcam->vpic.hue); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", ibmcam->vpic.brightness >> 8); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", ibmcam->vpic.contrast >> 12); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8d", ibmcam->vpic.whiteness >> 8); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; +} + +/* + * usb_ibmcam_testpattern() + * + * Procedure forms a test pattern (yellow grid on blue background). + * + * Parameters: + * fullframe: if TRUE then entire frame is filled, otherwise the procedure + * continues from the current scanline. + * pmode 0: fill the frame with solid blue color (like on VCR or TV) + * 1: Draw a colored grid + * + * History: + * 1/2/00 Created. + */ +void usb_ibmcam_testpattern(struct usb_ibmcam *ibmcam, int fullframe, int pmode) +{ + static const char proc[] = "usb_ibmcam_testpattern"; + struct ibmcam_frame *frame; + unsigned char *f; + int num_cell = 0; + int scan_length = 0; + static int num_pass = 0; + + if (ibmcam == NULL) { + printk(KERN_ERR "%s: ibmcam == NULL\n", proc); + return; + } + if ((ibmcam->curframe < 0) || (ibmcam->curframe >= IBMCAM_NUMFRAMES)) { + printk(KERN_ERR "%s: ibmcam->curframe=%d.\n", proc, ibmcam->curframe); + return; + } + + /* Grab the current frame */ + frame = &ibmcam->frame[ibmcam->curframe]; + + /* Optionally start at the beginning */ + if (fullframe) { + frame->curline = 0; + frame->scanlength = 0; + } + + /* Form every scan line */ + for (; frame->curline < imgheight; frame->curline++) { + int i; + + f = frame->data + (imgwidth * 3 * frame->curline); + for (i=0; i < imgwidth; i++) { + unsigned char cb=0x80; + unsigned char cg = 0; + unsigned char cr = 0; + + if (pmode == 1) { + if (frame->curline % 32 == 0) + cb = 0, cg = cr = 0xFF; + else if (i % 32 == 0) { + if (frame->curline % 32 == 1) + num_cell++; + cb = 0, cg = cr = 0xFF; + } else { + cb = ((num_cell*7) + num_pass) & 0xFF; + cg = ((num_cell*5) + num_pass*2) & 0xFF; + cr = ((num_cell*3) + num_pass*3) & 0xFF; + } + } else { + /* Just the blue screen */ + } + + *f++ = cb; + *f++ = cg; + *f++ = cr; + scan_length += 3; + } + } + + frame->grabstate = FRAME_DONE; + frame->scanlength += scan_length; + ++num_pass; + + /* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */ + usb_ibmcam_overlaystats(ibmcam, frame); +} + +static unsigned char *ibmcam_model1_find_header(unsigned char hdr_sig, unsigned char *data, int len) +{ + while (len >= 4) + { + if ((data[0] == 0x00) && (data[1] == 0xFF) && (data[2] == 0x00)) + { +#if 0 + /* This code helps to detect new frame markers */ + printk(KERN_DEBUG "Header sig: 00 FF 00 %02X\n", data[3]); +#endif + if (data[3] == hdr_sig) { + if (debug > 2) + printk(KERN_DEBUG "Header found.\n"); + return data+4; + } + } + ++data; + --len; + } + return NULL; +} + +static unsigned char *ibmcam_model2_find_header(unsigned char hdr_sig, unsigned char *data, int len) +{ + int marker_len = 0; + + switch (videosize) { + case VIDEOSIZE_176x144: + marker_len = 10; + break; + default: + marker_len = 2; + break; + } + while (len >= marker_len) + { + if ((data[0] == 0x00) && (data[1] == 0xFF)) + { +#if 0 + /* This code helps to detect new frame markers */ + static int pass = 0; + if (pass++ == 0) + ibmcam_hexdump(data, (len > 16) ? 16 : len); +#endif + if (debug > 2) + printk(KERN_DEBUG "Header found.\n"); + return data+marker_len; + } + ++data; + --len; + } + return NULL; +} + +/* How much data is left in the scratch buf? */ +#define scratch_left(x) (ibmcam->scratchlen - (int)((char *)x - (char *)ibmcam->scratch)) + +/* Grab the remaining */ +static void usb_ibmcam_align_scratch(struct usb_ibmcam *ibmcam, unsigned char *data) +{ + unsigned long left; + + left = scratch_left(data); + memmove(ibmcam->scratch, data, left); + ibmcam->scratchlen = left; +} + +/* + * usb_ibmcam_find_header() + * + * Locate one of supported header markers in the scratch buffer. + * Once found, remove all preceding bytes AND the marker (4 bytes) + * from the scratch buffer. Whatever follows must be video lines. + * + * History: + * 1/21/00 Created. + */ +static scan_state_t usb_ibmcam_find_header(struct usb_ibmcam *ibmcam) +{ + struct ibmcam_frame *frame; + unsigned char *data, *tmp; + + data = ibmcam->scratch; + frame = &ibmcam->frame[ibmcam->curframe]; + + if (ibmcam->camera_model == IBMCAM_MODEL_1) + tmp = ibmcam_model1_find_header(frame->hdr_sig, data, scratch_left(data)); + else if (ibmcam->camera_model == IBMCAM_MODEL_2) + tmp = ibmcam_model2_find_header(frame->hdr_sig, data, scratch_left(data)); + else + tmp = NULL; + + if (tmp == NULL) { + /* No header - entire scratch buffer is useless! */ + if (debug > 2) + printk(KERN_DEBUG "Skipping frame, no header\n"); + ibmcam->scratchlen = 0; + return scan_EndParse; + } + /* Header found */ + data = tmp; + + ibmcam->has_hdr = 1; + ibmcam->header_count++; + frame->scanstate = STATE_LINES; + frame->curline = 0; + + if (flags & FLAGS_FORCE_TESTPATTERN) { + usb_ibmcam_testpattern(ibmcam, 1, 1); + return scan_NextFrame; + } + usb_ibmcam_align_scratch(ibmcam, data); + return scan_Continue; +} + +/* + * usb_ibmcam_parse_lines() + * + * Parse one line (TODO: more than one!) from the scratch buffer, put + * decoded RGB value into the current frame buffer and add the written + * number of bytes (RGB) to the *pcopylen. + * + * History: + * 1/21/00 Created. + */ +static scan_state_t usb_ibmcam_parse_lines(struct usb_ibmcam *ibmcam, long *pcopylen) +{ + struct ibmcam_frame *frame; + unsigned char *data, *f, *chromaLine; + unsigned int len; + const int v4l_linesize = imgwidth * V4L_BYTES_PER_PIXEL; /* V4L line offset */ + const int hue_corr = (ibmcam->vpic.hue - 0x8000) >> 10; /* -32..+31 */ + const int hue2_corr = (hue_correction - 128) / 4; /* -32..+31 */ + const int ccm = 128; /* Color correction median - see below */ + int y, u, v, i, frame_done=0, mono_plane, color_corr; + + color_corr = (ibmcam->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ + RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1); + data = ibmcam->scratch; + frame = &ibmcam->frame[ibmcam->curframe]; + + len = frame->frmwidth * 3; /* 1 line of mono + 1 line of color */ + /*printk(KERN_DEBUG "len=%d. left=%d.\n",len,scratch_left(data));*/ + + mono_plane = ((frame->curline & 1) == 0); + + /* + * Lines are organized this way (or are they?) + * + * I420: + * ~~~~ + * ___________________________________ + * |-----Y-----|---UVUVUV...UVUV-----| \ + * |-----------+---------------------| \ + * |<-- 176 -->|<------ 176*2 ------>| Total 72. pairs of lines + * |... ... ...| / + * |___________|_____________________| / + * - odd line- ------- even line --- + * + * another format: + * ~~~~~~~~~~~~~~ + * ___________________________________ + * |-----Y-----|---UVUVUV...UVUV-----| \ + * |-----------+---------------------| \ + * |<-- 352 -->|<------ 352*2 ------>| Total 144. pairs of lines + * |... ... ...| / + * |___________|_____________________| / + * - odd line- ------- even line --- + */ + + /* Make sure there's enough data for the entire line */ + if (scratch_left(data) < (len+1024)) { + /*printk(KERN_DEBUG "out of data, need %u.\n", len);*/ + return scan_Out; + } + +#if 0 + { /* This code prints beginning of the source frame */ + static int pass = 0; + if ((pass++ % 3000) == 0) + ibmcam_hexdump(data, 16); + } +#endif + +#if 0 + if (frame->curline == 10 || frame->curline == 11) { + /* This code prints beginning of 10th (mono), 11th (chroma) line */ + static int pass = 0; + if ((pass % 100) == 0) + ibmcam_hexdump(data, 16); + if (frame->curline == 11) + pass++; + } +#endif + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Mind that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 1) >= V4L_FRAME_HEIGHT) + return scan_NextFrame; + + /* + * Now we are sure that entire line (representing all 'frame->frmwidth' + * pixels from the camera) is available in the scratch buffer. We + * start copying the line left-aligned to the V4L buffer (which + * might be larger - not smaller, hopefully). If the camera + * line is shorter then we should pad the V4L buffer with something + * (black in this case) to complete the line. + */ + f = frame->data + (v4l_linesize * frame->curline); + + /* + * chromaLine points to 1st pixel of the line with chrominance. + * If current line is monochrome then chromaLine points to next + * line after monochrome one. If current line has chrominance + * then chromaLine points to this very line. Such method allows + * to access chrominance data uniformly. + * + * To obtain chrominance data from the 'chromaLine' use this: + * v = chromaLine[0]; // 0-1:[0], 2-3:[4], 4-5:[8]... + * u = chromaLine[2]; // 0-1:[2], 2-3:[6], 4-5:[10]... + * + * Indices must be calculated this way: + * v_index = (i >> 1) << 2; + * u_index = (i >> 1) << 2 + 2; + * + * where 'i' is the column number [0..frame->frmwidth-1] + */ + chromaLine = data; + if (mono_plane) + chromaLine += frame->frmwidth; + + for (i = 0; i < frame->frmwidth; i++, data += (mono_plane ? 1 : 2)) + { + unsigned char rv, gv, bv; /* RGB components */ + + /* + * Search for potential Start-Of-Frame marker. It should + * not be here, of course, but if your formats don't match + * you might exceed the frame. We must match the marker to + * each byte of multi-byte data element if it is multi-byte. + */ +#if 1 + if ((ibmcam->camera_model == IBMCAM_MODEL_1) && (scratch_left(data) >= (4+2))) { + unsigned char *dp; + int j; + + for (j=0, dp=data; j < 2; j++, dp++) { + if ((dp[0] == 0x00) && (dp[1] == 0xFF) && + (dp[2] == 0x00) && (dp[3] == frame->hdr_sig)) { + ibmcam->has_hdr = 2; + frame_done++; + break; + } + } + } +#endif + + /* Check for various visual debugging hints (colorized pixels) */ + if ((flags & FLAGS_DISPLAY_HINTS) && (ibmcam->has_hdr)) { + /* + * This is bad and should not happen. This means that + * we somehow overshoot the line and encountered new + * frame! Obviously our camera/V4L frame size is out + * of whack. This cyan dot will help you to figure + * out where exactly the new frame arrived. + */ + if (ibmcam->has_hdr == 1) { + bv = 0; /* Yellow marker */ + gv = 0xFF; + rv = 0xFF; + } else { + bv = 0xFF; /* Cyan marker */ + gv = 0xFF; + rv = 0; + } + ibmcam->has_hdr = 0; + goto make_pixel; + } + + if (mono_plane || frame->order_yc) + y = data[0]; + else + y = data[1]; + + if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */ + rv = gv = bv = y; + else { + int off_0, off_2; + + off_0 = (i >> 1) << 2; + off_2 = off_0 + 2; + + if (frame->order_yc) { + off_0++; + off_2++; + } + if (!frame->order_uv) { + off_0 += 2; + off_2 -= 2; + } + u = chromaLine[off_0] + hue_corr; + v = chromaLine[off_2] + hue2_corr; + + /* Apply color correction */ + if (color_corr != 0) { + /* Magnify up to 2 times, reduce down to zero saturation */ + u = 128 + ((ccm + color_corr) * (u - 128)) / ccm; + v = 128 + ((ccm + color_corr) * (v - 128)) / ccm; + } + YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv); + } + + make_pixel: + /* + * The purpose of creating the pixel here, in one, + * dedicated place is that we may need to make the + * pixel wider and taller than it actually is. This + * may be used if camera generates small frames for + * sake of frame rate (or any other reason.) + * + * The output data consists of B, G, R bytes + * (in this order). + */ +#if USES_IBMCAM_PUTPIXEL + IBMCAM_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); +#else + *f++ = bv; + *f++ = gv; + *f++ = rv; +#endif + /* + * Typically we do not decide within a legitimate frame + * that we want to end the frame. However debugging code + * may detect marker of new frame within the data. Then + * this condition activates. The 'data' pointer is already + * pointing at the new marker, so we'd better leave it as is. + */ + if (frame_done) + break; /* End scanning of lines */ + } + /* + * Account for number of bytes that we wrote into output V4L frame. + * We do it here, after we are done with the scanline, because we + * may fill more than one output scanline if we do vertical + * enlargement. + */ + frame->curline++; + *pcopylen += v4l_linesize; + usb_ibmcam_align_scratch(ibmcam, data); + + if (frame_done || (frame->curline >= frame->frmheight)) + return scan_NextFrame; + else + return scan_Continue; +} + +/* + * usb_ibmcam_model2_parse_lines() + * + * This procedure deals with a weird RGB format that is produced by IBM + * camera model 2 in modes 320x240 and above; 'x' below is 159 or 175, + * depending on horizontal size of the picture: + * + * <--- 160 or 176 pairs of RA,RB bytes -----> + * *-----------------------------------------* \ + * | RA0 | RB0 | RA1 | RB1 | ... | RAx | RBx | \ + * |-----+-----+-----+-----+ ... +-----+-----| *- This is pair of horizontal lines, + * | B0 | G0 | B1 | G1 | ... | Bx | Gx | / total 240 or 288 lines (120 or 144 + * |=====+=====+=====+=====+ ... +=====+=====| / such pairs). + * + * Each group of FOUR bytes (RAi, RBi, Bi, Gi) where i=0..frame_width/2-1 + * defines ONE pixel. Therefore this format yields 176x144 "decoded" + * resolution at best. I do not know why camera sends such format - the + * previous model just used I420 and everyone was happy. + * + * I do not know what is the difference between RAi and RBi bytes. Both + * seemingly represent R component, but slightly vary in value (so that + * the picture looks a bit colored if one or another is used). I use + * them both as R component in attempt to at least partially recover the + * lost resolution. + */ +static scan_state_t usb_ibmcam_model2_parse_lines(struct usb_ibmcam *ibmcam, long *pcopylen) +{ + struct ibmcam_frame *frame; + unsigned char *data, *f, *la, *lb; + unsigned int len; + const int v4l_linesize = imgwidth * V4L_BYTES_PER_PIXEL; /* V4L line offset */ + int i, j, frame_done=0, color_corr; + + color_corr = (ibmcam->vpic.colour) >> 8; /* 0..+255 */ + + data = ibmcam->scratch; + frame = &ibmcam->frame[ibmcam->curframe]; + + /* Here we deal with pairs of horizontal lines */ + + len = frame->frmwidth * 2; /* 2 lines */ + /*printk(KERN_DEBUG "len=%d. left=%d.\n",len,scratch_left(data));*/ + + /* Make sure there's enough data for the entire line */ + if (scratch_left(data) < (len+32)) { + /*printk(KERN_DEBUG "out of data, need %u.\n", len);*/ + return scan_Out; + } + + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Mind that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 1) >= V4L_FRAME_HEIGHT) + return scan_NextFrame; + + if ((frame->curline & 1) == 0) { + la = data; + lb = data + frame->frmwidth; + } else { + la = data + frame->frmwidth; + lb = data; + } + + /* + * Now we are sure that entire line (representing all 'frame->frmwidth' + * pixels from the camera) is available in the scratch buffer. We + * start copying the line left-aligned to the V4L buffer (which + * might be larger - not smaller, hopefully). If the camera + * line is shorter then we should pad the V4L buffer with something + * (black in this case) to complete the line. + */ + f = frame->data + (v4l_linesize * frame->curline); + + /* Fill the 2-line strip */ + for (i = 0; i < frame->frmwidth; i++) { + int y, rv, gv, bv; /* RGB components */ + + j = i & (~1); + + /* Check for various visual debugging hints (colorized pixels) */ + if ((flags & FLAGS_DISPLAY_HINTS) && (ibmcam->has_hdr)) { + if (ibmcam->has_hdr == 1) { + bv = 0; /* Yellow marker */ + gv = 0xFF; + rv = 0xFF; + } else { + bv = 0xFF; /* Cyan marker */ + gv = 0xFF; + rv = 0; + } + ibmcam->has_hdr = 0; + goto make_pixel; + } + + /* + * Here I use RA and RB components, one per physical pixel. + * This causes fine vertical grid on the picture but may improve + * horizontal resolution. If you prefer replicating, use this: + * rv = la[j + 0]; ... or ... rv = la[j + 1]; + * then the pixel will be replicated. + */ + rv = la[i]; + gv = lb[j + 1]; + bv = lb[j + 0]; + + y = (rv + gv + bv) / 3; /* Brightness (badly calculated) */ + + if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */ + rv = gv = bv = y; + else if (color_corr != 128) { + + /* Calculate difference between color and brightness */ + rv -= y; + gv -= y; + bv -= y; + + /* Scale differences */ + rv = (rv * color_corr) / 128; + gv = (gv * color_corr) / 128; + bv = (bv * color_corr) / 128; + + /* Reapply brightness */ + rv += y; + gv += y; + bv += y; + + /* Watch for overflows */ + RESTRICT_TO_RANGE(rv, 0, 255); + RESTRICT_TO_RANGE(gv, 0, 255); + RESTRICT_TO_RANGE(bv, 0, 255); + } + + make_pixel: + IBMCAM_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); + IBMCAM_PUTPIXEL(frame, i, frame->curline+1, rv, gv, bv); + } + /* + * Account for number of bytes that we wrote into output V4L frame. + * We do it here, after we are done with the scanline, because we + * may fill more than one output scanline if we do vertical + * enlargement. + */ + frame->curline += 2; + *pcopylen += v4l_linesize * 2; + data += frame->frmwidth * 2; + usb_ibmcam_align_scratch(ibmcam, data); + + if (frame_done || (frame->curline >= frame->frmheight)) + return scan_NextFrame; + else + return scan_Continue; +} + +/* + * ibmcam_parse_data() + * + * Generic routine to parse the scratch buffer. It employs either + * usb_ibmcam_find_header() or usb_ibmcam_parse_lines() to do most + * of work. + * + * History: + * 1/21/00 Created. + */ +static void ibmcam_parse_data(struct usb_ibmcam *ibmcam) +{ + struct ibmcam_frame *frame; + unsigned char *data = ibmcam->scratch; + scan_state_t newstate; + long copylen = 0; + + /* Grab the current frame and the previous frame */ + frame = &ibmcam->frame[ibmcam->curframe]; + + /* printk(KERN_DEBUG "parsing %u.\n", ibmcam->scratchlen); */ + + while (1) { + + newstate = scan_Out; + if (scratch_left(data)) { + if (frame->scanstate == STATE_SCANNING) + newstate = usb_ibmcam_find_header(ibmcam); + else if (frame->scanstate == STATE_LINES) { + if ((ibmcam->camera_model == IBMCAM_MODEL_2) && + (videosize >= VIDEOSIZE_352x288)) { + newstate = usb_ibmcam_model2_parse_lines(ibmcam, ©len); + } + else { + newstate = usb_ibmcam_parse_lines(ibmcam, ©len); + } + } + } + if (newstate == scan_Continue) + continue; + else if ((newstate == scan_NextFrame) || (newstate == scan_Out)) + break; + else + return; /* scan_EndParse */ + } + + if (newstate == scan_NextFrame) { + frame->grabstate = FRAME_DONE; + ibmcam->curframe = -1; + ibmcam->frame_num++; + + /* Optionally display statistics on the screen */ + if (flags & FLAGS_OVERLAY_STATS) + usb_ibmcam_overlaystats(ibmcam, frame); + + /* This will cause the process to request another frame. */ + if (waitqueue_active(&frame->wq)) + wake_up_interruptible(&frame->wq); + } + + /* Update the frame's uncompressed length. */ + frame->scanlength += copylen; +} + +/* + * Make all of the blocks of data contiguous + */ +static int ibmcam_compress_isochronous(struct usb_ibmcam *ibmcam, urb_t *urb) +{ + unsigned char *cdata, *data, *data0; + int i, totlen = 0; + + data = data0 = ibmcam->scratch + ibmcam->scratchlen; + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* Detect and ignore errored packets */ + if (st < 0) { + if (debug >= 1) { + printk(KERN_ERR "ibmcam data error: [%d] len=%d, status=%X\n", + i, n, st); + } + ibmcam->iso_err_count++; + continue; + } + + /* Detect and ignore empty packets */ + if (n <= 0) { + ibmcam->iso_skip_count++; + continue; + } + + /* + * If camera continues to feed us with data but there is no + * consumption (if, for example, V4L client fell asleep) we + * may overflow the buffer. We have to move old data over to + * free room for new data. This is bad for old data. If we + * just drop new data then it's bad for new data... choose + * your favorite evil here. + */ + if ((ibmcam->scratchlen + n) > scratchbufsize) { +#if 0 + ibmcam->scratch_ovf_count++; + if (debug >= 3) + printk(KERN_ERR "ibmcam: scratch buf overflow! " + "scr_len: %d, n: %d\n", ibmcam->scratchlen, n ); + return totlen; +#else + int mv; + + ibmcam->scratch_ovf_count++; + if (debug >= 3) { + printk(KERN_ERR "ibmcam: scratch buf overflow! " + "scr_len: %d, n: %d\n", ibmcam->scratchlen, n ); + } + mv = (ibmcam->scratchlen + n) - scratchbufsize; + if (ibmcam->scratchlen >= mv) { + int newslen = ibmcam->scratchlen - mv; + memmove(ibmcam->scratch, ibmcam->scratch + mv, newslen); + ibmcam->scratchlen = newslen; + data = data0 = ibmcam->scratch + ibmcam->scratchlen; + } else { + printk(KERN_ERR "ibmcam: scratch buf too small\n"); + return totlen; + } +#endif + } + + /* Now we know that there is enough room in scratch buffer */ + memmove(data, cdata, n); + data += n; + totlen += n; + ibmcam->scratchlen += n; + } +#if 0 + if (totlen > 0) { + static int foo=0; + if (foo < 1) { + printk(KERN_DEBUG "+%d.\n", totlen); + ibmcam_hexdump(data0, (totlen > 64) ? 64:totlen); + ++foo; + } + } +#endif + return totlen; +} + +static void ibmcam_isoc_irq(struct urb *urb) +{ + int len; + struct usb_ibmcam *ibmcam = urb->context; + struct ibmcam_sbuf *sbuf; + int i; + + /* We don't want to do anything if we are about to be removed! */ + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return; + +#if 0 + if (urb->actual_length > 0) { + printk(KERN_DEBUG "ibmcam_isoc_irq: %p status %d, " + " errcount = %d, length = %d\n", urb, urb->status, + urb->error_count, urb->actual_length); + } else { + static int c = 0; + if (c++ % 100 == 0) + printk(KERN_DEBUG "ibmcam_isoc_irq: no data\n"); + } +#endif + + if (!ibmcam->streaming) { + if (debug >= 1) + printk(KERN_DEBUG "ibmcam: oops, not streaming, but interrupt\n"); + return; + } + + sbuf = &ibmcam->sbuf[ibmcam->cursbuf]; + + /* Copy the data received into our scratch buffer */ + len = ibmcam_compress_isochronous(ibmcam, urb); + + ibmcam->urb_count++; + ibmcam->urb_length = len; + ibmcam->data_count += len; + +#if 0 /* This code prints few initial bytes of ISO data: used to decode markers */ + if (ibmcam->urb_count % 64 == 1) { + if (ibmcam->urb_count == 1) { + ibmcam_hexdump(ibmcam->scratch, + (ibmcam->scratchlen > 32) ? 32 : ibmcam->scratchlen); + } + } +#endif + + /* If we collected enough data let's parse! */ + if (ibmcam->scratchlen) { + /* If we don't have a frame we're current working on, complain */ + if (ibmcam->curframe >= 0) + ibmcam_parse_data(ibmcam); + else { + if (debug >= 1) + printk(KERN_DEBUG "ibmcam: received data, but no frame available\n"); + } + } + + for (i = 0; i < FRAMES_PER_DESC; i++) { + sbuf->urb->iso_frame_desc[i].status = 0; + sbuf->urb->iso_frame_desc[i].actual_length = 0; + } + + /* Move to the next sbuf */ + ibmcam->cursbuf = (ibmcam->cursbuf + 1) % IBMCAM_NUMSBUF; + + return; +} + +/* + * usb_ibmcam_veio() + * + * History: + * 1/27/00 Added check for dev == NULL; this happens if camera is unplugged. + */ +static int usb_ibmcam_veio( + struct usb_ibmcam *ibmcam, + unsigned char req, + unsigned short value, + unsigned short index) +{ + static const char proc[] = "usb_ibmcam_veio"; + unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */; + int i; + + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return 0; + + if (req == 1) { + i = usb_control_msg( + ibmcam->dev, + usb_rcvctrlpipe(ibmcam->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + value, + index, + cp, + sizeof(cp), + HZ); +#if 0 + printk(KERN_DEBUG "USB => %02x%02x%02x%02x%02x%02x%02x%02x " + "(req=$%02x val=$%04x ind=$%04x)\n", + cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7], + req, value, index); +#endif + } else { + i = usb_control_msg( + ibmcam->dev, + usb_sndctrlpipe(ibmcam->dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + value, + index, + NULL, + 0, + HZ); + } + if (i < 0) { + printk(KERN_ERR "%s: ERROR=%d. Camera stopped - " + "reconnect or reload driver.\n", proc, i); + ibmcam->last_error = i; + } + return i; +} + +/* + * usb_ibmcam_calculate_fps() + * + * This procedure roughly calculates the real frame rate based + * on FPS code (framerate=NNN option). Actual FPS differs + * slightly depending on lighting conditions, so that actual frame + * rate is determined by the camera. Since I don't know how to ask + * the camera what FPS is now I have to use the FPS code instead. + * + * The FPS code is in range [0..6], 0 is slowest, 6 is fastest. + * Corresponding real FPS should be in range [3..30] frames per second. + * The conversion formula is obvious: + * + * real_fps = 3 + (fps_code * 4.5) + * + * History: + * 1/18/00 Created. + */ +static int usb_ibmcam_calculate_fps(void) +{ + return 3 + framerate*4 + framerate/2; +} + +/* + * usb_ibmcam_send_FF_04_02() + * + * This procedure sends magic 3-command prefix to the camera. + * The purpose of this prefix is not known. + * + * History: + * 1/2/00 Created. + */ +static void usb_ibmcam_send_FF_04_02(struct usb_ibmcam *ibmcam) +{ + usb_ibmcam_veio(ibmcam, 0, 0x00FF, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0004, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124); +} + +static void usb_ibmcam_send_00_04_06(struct usb_ibmcam *ibmcam) +{ + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0004, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0006, 0x0124); +} + +static void usb_ibmcam_send_x_00(struct usb_ibmcam *ibmcam, unsigned short x) +{ + usb_ibmcam_veio(ibmcam, 0, x, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); +} + +static void usb_ibmcam_send_x_00_05(struct usb_ibmcam *ibmcam, unsigned short x) +{ + usb_ibmcam_send_x_00(ibmcam, x); + usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); +} + +static void usb_ibmcam_send_x_00_05_02(struct usb_ibmcam *ibmcam, unsigned short x) +{ + usb_ibmcam_veio(ibmcam, 0, x, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124); +} + +static void usb_ibmcam_send_x_01_00_05(struct usb_ibmcam *ibmcam, unsigned short x) +{ + usb_ibmcam_veio(ibmcam, 0, x, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); +} + +static void usb_ibmcam_send_x_00_05_02_01(struct usb_ibmcam *ibmcam, unsigned short x) +{ + usb_ibmcam_veio(ibmcam, 0, x, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124); +} + +static void usb_ibmcam_send_x_00_05_02_08_01(struct usb_ibmcam *ibmcam, unsigned short x) +{ + usb_ibmcam_veio(ibmcam, 0, x, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0008, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124); +} + +static void usb_ibmcam_Packet_Format1(struct usb_ibmcam *ibmcam, unsigned char fkey, unsigned char val) +{ + usb_ibmcam_send_x_01_00_05 (ibmcam, unknown_88); + usb_ibmcam_send_x_00_05 (ibmcam, fkey); + usb_ibmcam_send_x_00_05_02_08_01(ibmcam, val); + usb_ibmcam_send_x_00_05 (ibmcam, unknown_88); + usb_ibmcam_send_x_00_05_02_01 (ibmcam, fkey); + usb_ibmcam_send_x_00_05 (ibmcam, unknown_89); + usb_ibmcam_send_x_00 (ibmcam, fkey); + usb_ibmcam_send_00_04_06 (ibmcam); + usb_ibmcam_veio (ibmcam, 1, 0x0000, 0x0126); + usb_ibmcam_send_FF_04_02 (ibmcam); +} + +static void usb_ibmcam_PacketFormat2(struct usb_ibmcam *ibmcam, unsigned char fkey, unsigned char val) +{ + usb_ibmcam_send_x_01_00_05 (ibmcam, unknown_88); + usb_ibmcam_send_x_00_05 (ibmcam, fkey); + usb_ibmcam_send_x_00_05_02 (ibmcam, val); +} + +static void usb_ibmcam_model2_Packet2(struct usb_ibmcam *ibmcam) +{ + usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x012d); + usb_ibmcam_veio(ibmcam, 0, 0xfea3, 0x0124); +} + +static void usb_ibmcam_model2_Packet1(struct usb_ibmcam *ibmcam, unsigned short v1, unsigned short v2) +{ + usb_ibmcam_veio(ibmcam, 0, 0x00aa, 0x012d); + usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x012e); + usb_ibmcam_veio(ibmcam, 0, v1, 0x012f); + usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x0130); + usb_ibmcam_veio(ibmcam, 0, 0xc719, 0x0124); + usb_ibmcam_veio(ibmcam, 0, v2, 0x0127); + + usb_ibmcam_model2_Packet2(ibmcam); +} + +/* + * usb_ibmcam_adjust_contrast() + * + * The contrast value changes from 0 (high contrast) to 15 (low contrast). + * This is in reverse to usual order of things (such as TV controls), so + * we reverse it again here. + * + * TODO: we probably don't need to send the setup 5 times... + * + * History: + * 1/2/00 Created. + */ +static void usb_ibmcam_adjust_contrast(struct usb_ibmcam *ibmcam) +{ + unsigned char new_contrast = ibmcam->vpic.contrast >> 12; + const int ntries = 5; + + if (new_contrast >= 16) + new_contrast = 15; + new_contrast = 15 - new_contrast; + if (new_contrast != ibmcam->vpic_old.contrast) { + ibmcam->vpic_old.contrast = new_contrast; + if (ibmcam->camera_model == IBMCAM_MODEL_1) { + int i; + for (i=0; i < ntries; i++) { + usb_ibmcam_Packet_Format1(ibmcam, contrast_14, new_contrast); + usb_ibmcam_send_FF_04_02(ibmcam); + } + } else { + /* Camera model 2 does not have this control; implemented in software. */ + } + } +} + +/* + * usb_ibmcam_change_lighting_conditions() + * + * Camera model 1: + * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low. + * + * Camera model 2: + * We have 16 levels of lighting, 0 for bright light and up to 15 for + * low light. But values above 5 or so are useless because camera is + * not really capable to produce anything worth viewing at such light. + * This setting may be altered only in certain camera state. + * + * Low lighting forces slower FPS. Lighting is set as a module parameter. + * + * History: + * 1/5/00 Created. + * 2/20/00 Added support for Model 2 cameras. + */ +static void usb_ibmcam_change_lighting_conditions(struct usb_ibmcam *ibmcam) +{ + static const char proc[] = "usb_ibmcam_change_lighting_conditions"; + + if (debug > 0) + printk(KERN_INFO "%s: Set lighting to %hu.\n", proc, lighting); + + if (ibmcam->camera_model == IBMCAM_MODEL_1) { + const int ntries = 5; + int i; + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, light_27, (unsigned short) lighting); + } else { + /* + * This command apparently requires camera to be stopped. My + * experiments showed that it -is- possible to alter the lighting + * conditions setting "on the fly", but why bother? This setting does + * not work reliably in all cases, so I decided simply to leave the + * setting where Xirlink put it - in the camera setup phase. This code + * is commented out because it does not work at -any- moment, so its + * presence makes no sense. You may use it for experiments. + */ +#if 0 + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x010c); /* Stop camera */ + usb_ibmcam_model2_Packet1(ibmcam, mod2_sensitivity, lighting); + usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c); /* Start camera */ +#endif + } +} + +/* + * usb_ibmcam_set_sharpness() + * + * Cameras model 1 have internal smoothing feature. It is controlled by value in + * range [0..6], where 0 is most smooth and 6 is most sharp (raw image, I guess). + * Recommended value is 4. Cameras model 2 do not have this feature at all. + */ +static void usb_ibmcam_set_sharpness(struct usb_ibmcam *ibmcam) +{ + static const char proc[] = "usb_ibmcam_set_sharpness"; + + if (ibmcam->camera_model == IBMCAM_MODEL_1) { + static const unsigned short sa[] = { 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a }; + unsigned short i, sv; + + RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX); + if (debug > 0) + printk(KERN_INFO "%s: Set sharpness to %hu.\n", proc, sharpness); + + sv = sa[sharpness - SHARPNESS_MIN]; + for (i=0; i < 2; i++) { + usb_ibmcam_send_x_01_00_05 (ibmcam, unknown_88); + usb_ibmcam_send_x_00_05 (ibmcam, sharp_13); + usb_ibmcam_send_x_00_05_02 (ibmcam, sv); + } + } else { + /* Camera model 2 does not have this control */ + } +} + +/* + * usb_ibmcam_set_brightness() + * + * This procedure changes brightness of the picture. + */ +static void usb_ibmcam_set_brightness(struct usb_ibmcam *ibmcam) +{ + static const char proc[] = "usb_ibmcam_set_brightness"; + static const unsigned short n = 1; + unsigned short i, j, bv[3]; + + bv[0] = bv[1] = bv[2] = ibmcam->vpic.brightness >> 10; + if (bv[0] == (ibmcam->vpic_old.brightness >> 10)) + return; + ibmcam->vpic_old.brightness = ibmcam->vpic.brightness; + + if (debug > 0) + printk(KERN_INFO "%s: Set brightness to (%hu,%hu,%hu)\n", + proc, bv[0], bv[1], bv[2]); + + if (ibmcam->camera_model == IBMCAM_MODEL_1) { + for (j=0; j < 3; j++) + for (i=0; i < n; i++) + usb_ibmcam_Packet_Format1(ibmcam, bright_3x[j], bv[j]); + } else { + i = ibmcam->vpic.brightness >> 12; /* 0 .. 15 */ + j = 0x60 + i * ((0xee - 0x60) / 16); /* 0x60 .. 0xee or so */ + usb_ibmcam_model2_Packet1(ibmcam, mod2_brightness, j); + } +} + +static void usb_ibmcam_model2_set_hue(struct usb_ibmcam *ibmcam) +{ + unsigned short hue = ibmcam->vpic.hue >> 9; /* 0 .. 7F */ + + usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg, hue); + /* usb_ibmcam_model2_Packet1(ibmcam, mod2_saturation, sat); */ +} + +/* + * usb_ibmcam_adjust_picture() + * + * This procedure gets called from V4L interface to update picture settings. + * Here we change brightness and contrast. + */ +static void usb_ibmcam_adjust_picture(struct usb_ibmcam *ibmcam) +{ + usb_ibmcam_adjust_contrast(ibmcam); + usb_ibmcam_set_brightness(ibmcam); + if (ibmcam->camera_model == IBMCAM_MODEL_2) { + usb_ibmcam_model2_set_hue(ibmcam); + } +} + +static int usb_ibmcam_model1_setup(struct usb_ibmcam *ibmcam) +{ + const int ntries = 5; + int i; + + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0128); + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100); + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100); /* LED On */ + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100); + usb_ibmcam_veio(ibmcam, 0, 0x81, 0x0100); /* LED Off */ + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100); + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100); /* LED On */ + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0108); + + usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0112); + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0115); + usb_ibmcam_veio(ibmcam, 0, 0x06, 0x0115); + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0116); + usb_ibmcam_veio(ibmcam, 0, 0x44, 0x0116); + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0116); + usb_ibmcam_veio(ibmcam, 0, 0x40, 0x0116); + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0115); + usb_ibmcam_veio(ibmcam, 0, 0x0e, 0x0115); + usb_ibmcam_veio(ibmcam, 0, 0x19, 0x012c); + + usb_ibmcam_Packet_Format1(ibmcam, 0x00, 0x1e); + usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x0d); + usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x09); + usb_ibmcam_Packet_Format1(ibmcam, 0x3b, 0x00); + usb_ibmcam_Packet_Format1(ibmcam, 0x28, 0x22); + usb_ibmcam_Packet_Format1(ibmcam, light_27, 0); + usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1f); + usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x08); + + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x2c, 0x00); + + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x30, 0x14); + + usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02); + usb_ibmcam_PacketFormat2(ibmcam, 0x01, 0xe1); + usb_ibmcam_PacketFormat2(ibmcam, 0x02, 0xcd); + usb_ibmcam_PacketFormat2(ibmcam, 0x03, 0xcd); + usb_ibmcam_PacketFormat2(ibmcam, 0x04, 0xfa); + usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff); + usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00); + + usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02); + usb_ibmcam_PacketFormat2(ibmcam, 0x0a, 0x37); + usb_ibmcam_PacketFormat2(ibmcam, 0x0b, 0xb8); + usb_ibmcam_PacketFormat2(ibmcam, 0x0c, 0xf3); + usb_ibmcam_PacketFormat2(ibmcam, 0x0d, 0xe3); + usb_ibmcam_PacketFormat2(ibmcam, 0x0e, 0x0d); + usb_ibmcam_PacketFormat2(ibmcam, 0x0f, 0xf2); + usb_ibmcam_PacketFormat2(ibmcam, 0x10, 0xd5); + usb_ibmcam_PacketFormat2(ibmcam, 0x11, 0xba); + usb_ibmcam_PacketFormat2(ibmcam, 0x12, 0x53); + usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff); + usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00); + + usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02); + usb_ibmcam_PacketFormat2(ibmcam, 0x16, 0x00); + usb_ibmcam_PacketFormat2(ibmcam, 0x17, 0x28); + usb_ibmcam_PacketFormat2(ibmcam, 0x18, 0x7d); + usb_ibmcam_PacketFormat2(ibmcam, 0x19, 0xbe); + usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff); + usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00); + + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x00, 0x18); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x13, 0x18); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x14, 0x06); + + /* This is default brightness */ + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x31, 0x37); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x32, 0x46); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x33, 0x55); + + usb_ibmcam_Packet_Format1(ibmcam, 0x2e, 0x04); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x2d, 0x04); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x29, 0x80); + usb_ibmcam_Packet_Format1(ibmcam, 0x2c, 0x01); + usb_ibmcam_Packet_Format1(ibmcam, 0x30, 0x17); + usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x08); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x34, 0x00); + + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0101); + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010a); + + switch (videosize) { + case VIDEOSIZE_128x96: + usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0103); + usb_ibmcam_veio(ibmcam, 0, 0x60, 0x0105); + usb_ibmcam_veio(ibmcam, 0, 0x0c, 0x010b); + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x0b, 0x011d); + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0129); + break; + case VIDEOSIZE_176x144: + usb_ibmcam_veio(ibmcam, 0, 0xb0, 0x0103); + usb_ibmcam_veio(ibmcam, 0, 0x8f, 0x0105); + usb_ibmcam_veio(ibmcam, 0, 0x06, 0x010b); + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x0d, 0x011d); + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0129); + break; + case VIDEOSIZE_352x288: + usb_ibmcam_veio(ibmcam, 0, 0xb0, 0x0103); + usb_ibmcam_veio(ibmcam, 0, 0x90, 0x0105); + usb_ibmcam_veio(ibmcam, 0, 0x02, 0x010b); + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x05, 0x011d); + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0129); + break; + } + + usb_ibmcam_veio(ibmcam, 0, 0xff, 0x012b); + + /* This is another brightness - don't know why */ + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x31, 0xc3); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x32, 0xd2); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x33, 0xe1); + + /* Default contrast */ + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, contrast_14, 0x0a); + + /* Default sharpness */ + for (i=0; i < 2; i++) + usb_ibmcam_PacketFormat2(ibmcam, sharp_13, 0x1a); /* Level 4 FIXME */ + + /* Default lighting conditions */ + usb_ibmcam_Packet_Format1(ibmcam, light_27, lighting); /* 0=Bright 2=Low */ + + /* Assorted init */ + + switch (videosize) { + case VIDEOSIZE_128x96: + usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1e); + usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x36, 0x0102); + usb_ibmcam_veio(ibmcam, 0, 0x1a, 0x0104); + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x2b, 0x011c); + usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a); /* Same everywhere */ +#if 0 + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0106); + usb_ibmcam_veio(ibmcam, 0, 0x38, 0x0107); +#else + usb_ibmcam_veio(ibmcam, 0, 0x02, 0x0106); + usb_ibmcam_veio(ibmcam, 0, 0x2a, 0x0107); +#endif + break; + case VIDEOSIZE_176x144: + usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1e); + usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x0102); + usb_ibmcam_veio(ibmcam, 0, 0x02, 0x0104); + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x2b, 0x011c); + usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0106); + usb_ibmcam_veio(ibmcam, 0, 0xca, 0x0107); + break; + case VIDEOSIZE_352x288: + usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1f); + usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x08, 0x0102); + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0104); + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x2f, 0x011c); + usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0106); + usb_ibmcam_veio(ibmcam, 0, 0xf6, 0x0107); + break; + } + return IBMCAM_IS_OPERATIONAL(ibmcam); +} + +static int usb_ibmcam_model2_setup(struct usb_ibmcam *ibmcam) +{ + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0100); /* LED on */ + usb_ibmcam_veio(ibmcam, 1, 0x0000, 0x0116); + usb_ibmcam_veio(ibmcam, 0, 0x0060, 0x0116); + usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0112); + usb_ibmcam_veio(ibmcam, 0, 0x00bc, 0x012c); + usb_ibmcam_veio(ibmcam, 0, 0x0008, 0x012b); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0108); + usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0133); + usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0102); + switch (videosize) { + case VIDEOSIZE_176x144: + usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103); /* All except 320x240 */ + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x0024, 0x0105); /* 176x144, 352x288 */ + usb_ibmcam_veio(ibmcam, 0, 0x00b9, 0x010a); /* Unique to this mode */ + usb_ibmcam_veio(ibmcam, 0, 0x0038, 0x0119); /* Unique to this mode */ + usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x0090, 0x0107); /* Unique to every mode*/ + break; + case VIDEOSIZE_320x240: + usb_ibmcam_veio(ibmcam, 0, 0x0028, 0x0103); /* Unique to this mode */ + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x001e, 0x0105); /* 320x240, 352x240 */ + usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a); /* All except 176x144 */ + usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119); /* All except 176x144 */ + usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x0098, 0x0107); /* Unique to every mode*/ + break; + case VIDEOSIZE_352x240: + usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103); /* All except 320x240 */ + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x001e, 0x0105); /* 320x240, 352x240 */ + usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a); /* All except 176x144 */ + usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119); /* All except 176x144 */ + usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x00da, 0x0107); /* Unique to every mode*/ + break; + case VIDEOSIZE_352x288: + usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103); /* All except 320x240 */ + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x0024, 0x0105); /* 176x144, 352x288 */ + usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a); /* All except 176x144 */ + usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119); /* All except 176x144 */ + usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x00fe, 0x0107); /* Unique to every mode*/ + break; + } + return IBMCAM_IS_OPERATIONAL(ibmcam); +} + +/* + * usb_ibmcam_model1_setup_after_video_if() + * + * This code adds finishing touches to the video data interface. + * Here we configure the frame rate and turn on the LED. + */ +static void usb_ibmcam_model1_setup_after_video_if(struct usb_ibmcam *ibmcam) +{ + unsigned short internal_frame_rate; + + RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX); + internal_frame_rate = FRAMERATE_MAX - framerate; /* 0=Fast 6=Slow */ + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100); /* LED On */ + usb_ibmcam_veio(ibmcam, 0, internal_frame_rate, 0x0111); + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0114); + usb_ibmcam_veio(ibmcam, 0, 0xc0, 0x010c); +} + +static void usb_ibmcam_model2_setup_after_video_if(struct usb_ibmcam *ibmcam) +{ + unsigned short setup_model2_rg, setup_model2_rg2, setup_model2_sat, setup_model2_yb; + + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0100); /* LED on */ + + switch (videosize) { + case VIDEOSIZE_176x144: + usb_ibmcam_veio(ibmcam, 0, 0x0050, 0x0111); + usb_ibmcam_veio(ibmcam, 0, 0x00d0, 0x0111); + break; + case VIDEOSIZE_320x240: + case VIDEOSIZE_352x240: + case VIDEOSIZE_352x288: + usb_ibmcam_veio(ibmcam, 0, 0x0040, 0x0111); + usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x0111); + break; + } + usb_ibmcam_veio(ibmcam, 0, 0x009b, 0x010f); + usb_ibmcam_veio(ibmcam, 0, 0x00bb, 0x010f); + + /* + * Hardware settings, may affect CMOS sensor; not user controls! + * ------------------------------------------------------------- + * 0x0004: no effect + * 0x0006: hardware effect + * 0x0008: no effect + * 0x000a: stops video stream, probably important h/w setting + * 0x000c: changes color in hardware manner (not user setting) + * 0x0012: changes number of colors (does not affect speed) + * 0x002a: no effect + * 0x002c: hardware setting (related to scan lines) + * 0x002e: stops video stream, probably important h/w setting + */ + usb_ibmcam_model2_Packet1(ibmcam, 0x000a, 0x005c); + usb_ibmcam_model2_Packet1(ibmcam, 0x0004, 0x0000); + usb_ibmcam_model2_Packet1(ibmcam, 0x0006, 0x00fb); + usb_ibmcam_model2_Packet1(ibmcam, 0x0008, 0x0000); + usb_ibmcam_model2_Packet1(ibmcam, 0x000c, 0x0009); + usb_ibmcam_model2_Packet1(ibmcam, 0x0012, 0x000a); + usb_ibmcam_model2_Packet1(ibmcam, 0x002a, 0x0000); + usb_ibmcam_model2_Packet1(ibmcam, 0x002c, 0x0000); + usb_ibmcam_model2_Packet1(ibmcam, 0x002e, 0x0008); + + /* + * Function 0x0030 pops up all over the place. Apparently + * it is a hardware control register, with every bit assigned to + * do something. + */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0000); + + /* + * Magic control of CMOS sensor. Only lower values like + * 0-3 work, and picture shifts left or right. Don't change. + */ + switch (videosize) { + case VIDEOSIZE_176x144: + usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0002); + usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0002); /* Horizontal shift */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x004a); /* Another hardware setting */ + break; + case VIDEOSIZE_320x240: + usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0009); + usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0005); /* Horizontal shift */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x0044); /* Another hardware setting */ + break; + case VIDEOSIZE_352x240: + /* This mode doesn't work as Windows programs it; changed to work */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0009); /* Windows sets this to 8 */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0003); /* Horizontal shift */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x0044); /* Windows sets this to 0x0045 */ + break; + case VIDEOSIZE_352x288: + usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0003); + usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0002); /* Horizontal shift */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x004a); /* Another hardware setting */ + break; + } + + usb_ibmcam_model2_Packet1(ibmcam, mod2_brightness, 0x005a); + + /* + * We have our own frame rate setting varying from 0 (slowest) to 6 (fastest). + * The camera model 2 allows frame rate in range [0..0x1F] where 0 is also the + * slowest setting. However for all practical reasons high settings make no + * sense because USB is not fast enough to support high FPS. Be aware that + * the picture datastream will be severely disrupted if you ask for + * frame rate faster than allowed for the video size - see below: + * + * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz): + * ----------------------------------------------------------------- + * 176x144: [6..31] + * 320x240: [8..31] + * 352x240: [10..31] + * 352x288: [16..31] I have to raise lower threshold for stability... + * + * As usual, slower FPS provides better sensitivity. + */ + { + short hw_fps=31, i_framerate; + + RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX); + i_framerate = FRAMERATE_MAX - framerate + FRAMERATE_MIN; + switch (videosize) { + case VIDEOSIZE_176x144: + hw_fps = 6 + i_framerate*4; + break; + case VIDEOSIZE_320x240: + hw_fps = 8 + i_framerate*3; + break; + case VIDEOSIZE_352x240: + hw_fps = 10 + i_framerate*2; + break; + case VIDEOSIZE_352x288: + hw_fps = 28 + i_framerate/2; + break; + } + if (debug > 0) + printk(KERN_DEBUG "Framerate (hardware): %hd.\n", hw_fps); + RESTRICT_TO_RANGE(hw_fps, 0, 31); + usb_ibmcam_model2_Packet1(ibmcam, mod2_set_framerate, hw_fps); + } + + /* + * This setting does not visibly affect pictures; left it here + * because it was present in Windows USB data stream. This function + * does not allow arbitrary values and apparently is a bit mask, to + * be activated only at appropriate time. Don't change it randomly! + */ + switch (videosize) { + case VIDEOSIZE_176x144: + usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x00c2); + break; + case VIDEOSIZE_320x240: + usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0044); + break; + case VIDEOSIZE_352x240: + usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0046); + break; + case VIDEOSIZE_352x288: + usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0048); + break; + } + + usb_ibmcam_model2_Packet1(ibmcam, mod2_sensitivity, lighting); + + if (init_model2_rg >= 0) { + RESTRICT_TO_RANGE(init_model2_rg, 0, 255); + setup_model2_rg = init_model2_rg; + } else + setup_model2_rg = 0x0070; + + if (init_model2_rg2 >= 0) { + RESTRICT_TO_RANGE(init_model2_rg2, 0, 255); + setup_model2_rg2 = init_model2_rg2; + } else + setup_model2_rg2 = 0x002f; + + if (init_model2_sat >= 0) { + RESTRICT_TO_RANGE(init_model2_sat, 0, 255); + setup_model2_sat = init_model2_sat; + } else + setup_model2_sat = 0x0034; + + if (init_model2_yb >= 0) { + RESTRICT_TO_RANGE(init_model2_yb, 0, 255); + setup_model2_yb = init_model2_yb; + } else + setup_model2_yb = 0x00a0; + + usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg2, setup_model2_rg2); + usb_ibmcam_model2_Packet1(ibmcam, mod2_saturation, setup_model2_sat); + usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_yb, setup_model2_yb); + usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg, setup_model2_rg); + + /* Hardware control command */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0004); + + usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c); /* Go camera, go! */ + usb_clear_halt(ibmcam->dev, ibmcam->video_endp); +} + +/* + * usb_ibmcam_setup_video_stop() + * + * This code tells camera to stop streaming. The interface remains + * configured and bandwidth - claimed. + */ +static void usb_ibmcam_setup_video_stop(struct usb_ibmcam *ibmcam) +{ + if (ibmcam->camera_model == IBMCAM_MODEL_1) { + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c); + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c); + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0114); + usb_ibmcam_veio(ibmcam, 0, 0xc0, 0x010c); + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c); + usb_ibmcam_send_FF_04_02(ibmcam); + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100); + usb_ibmcam_veio(ibmcam, 0, 0x81, 0x0100); /* LED Off */ + } else if (ibmcam->camera_model == IBMCAM_MODEL_2) { + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x010c); /* Stop the camera */ + + usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0004); + + usb_ibmcam_veio(ibmcam, 0, 0x0080, 0x0100); /* LED Off */ + usb_ibmcam_veio(ibmcam, 0, 0x0020, 0x0111); + usb_ibmcam_veio(ibmcam, 0, 0x00a0, 0x0111); + + usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0002); + + usb_ibmcam_veio(ibmcam, 0, 0x0020, 0x0111); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0112); + } +} + +/* + * usb_ibmcam_reinit_iso() + * + * This procedure sends couple of commands to the camera and then + * resets the video pipe. This sequence was observed to reinit the + * camera or, at least, to initiate ISO data stream. + * + * History: + * 1/2/00 Created. + */ +static void usb_ibmcam_reinit_iso(struct usb_ibmcam *ibmcam, int do_stop) +{ + if (ibmcam->camera_model == IBMCAM_MODEL_1) { + if (do_stop) + usb_ibmcam_setup_video_stop(ibmcam); + usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0114); + usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c); + usb_clear_halt(ibmcam->dev, ibmcam->video_endp); + usb_ibmcam_model1_setup_after_video_if(ibmcam); + } else if (ibmcam->camera_model == IBMCAM_MODEL_2) { + usb_ibmcam_model2_setup_after_video_if(ibmcam); + } +} + +/* + * ibmcam_init_isoc() + * + * History: + * 1/27/00 Used ibmcam->iface, ibmcam->ifaceAltActive instead of hardcoded values. + * Simplified by using for loop, allowed any number of URBs. + */ +static int ibmcam_init_isoc(struct usb_ibmcam *ibmcam) +{ + struct usb_device *dev = ibmcam->dev; + int i, err; + + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return -EFAULT; + + ibmcam->compress = 0; + ibmcam->curframe = -1; + ibmcam->cursbuf = 0; + ibmcam->scratchlen = 0; + + /* Alternate interface 1 is is the biggest frame size */ + i = usb_set_interface(dev, ibmcam->iface, ibmcam->ifaceAltActive); + if (i < 0) { + printk(KERN_ERR "usb_set_interface error\n"); + ibmcam->last_error = i; + return -EBUSY; + } + usb_ibmcam_change_lighting_conditions(ibmcam); + usb_ibmcam_set_sharpness(ibmcam); + usb_ibmcam_reinit_iso(ibmcam, 0); + + /* We double buffer the Iso lists */ + + for (i=0; i < IBMCAM_NUMSBUF; i++) { + int j, k; + urb_t *urb; + + urb = usb_alloc_urb(FRAMES_PER_DESC); + if (urb == NULL) { + printk(KERN_ERR "ibmcam_init_isoc: usb_init_isoc() failed.\n"); + return -ENOMEM; + } + ibmcam->sbuf[i].urb = urb; + urb->dev = dev; + urb->context = ibmcam; + urb->pipe = usb_rcvisocpipe(dev, ibmcam->video_endp); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = ibmcam->sbuf[i].data; + urb->complete = ibmcam_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = ibmcam->iso_packet_len * FRAMES_PER_DESC; + for (j=k=0; j < FRAMES_PER_DESC; j++, k += ibmcam->iso_packet_len) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = ibmcam->iso_packet_len; + } + } + + /* Link URBs into a ring so that they invoke each other infinitely */ + for (i=0; i < IBMCAM_NUMSBUF; i++) { + if ((i+1) < IBMCAM_NUMSBUF) + ibmcam->sbuf[i].urb->next = ibmcam->sbuf[i+1].urb; + else + ibmcam->sbuf[i].urb->next = ibmcam->sbuf[0].urb; + } + + /* Submit all URBs */ + for (i=0; i < IBMCAM_NUMSBUF; i++) { + err = usb_submit_urb(ibmcam->sbuf[i].urb); + if (err) + printk(KERN_ERR "ibmcam_init_isoc: usb_run_isoc(%d) ret %d\n", + i, err); + } + + ibmcam->streaming = 1; + /* printk(KERN_DEBUG "streaming=1 ibmcam->video_endp=$%02x\n", ibmcam->video_endp); */ + return 0; +} + +/* + * ibmcam_stop_isoc() + * + * This procedure stops streaming and deallocates URBs. Then it + * activates zero-bandwidth alt. setting of the video interface. + * + * History: + * 1/22/00 Corrected order of actions to work after surprise removal. + * 1/27/00 Used ibmcam->iface, ibmcam->ifaceAltInactive instead of hardcoded values. + */ +static void ibmcam_stop_isoc(struct usb_ibmcam *ibmcam) +{ + static const char proc[] = "ibmcam_stop_isoc"; + int i, j; + + if (!ibmcam->streaming || (ibmcam->dev == NULL)) + return; + + /* Unschedule all of the iso td's */ + for (i=0; i < IBMCAM_NUMSBUF; i++) { + j = usb_unlink_urb(ibmcam->sbuf[i].urb); + if (j < 0) + printk(KERN_ERR "%s: usb_unlink_urb() error %d.\n", proc, j); + } + /* printk(KERN_DEBUG "streaming=0\n"); */ + ibmcam->streaming = 0; + + /* Delete them all */ + for (i=0; i < IBMCAM_NUMSBUF; i++) + usb_free_urb(ibmcam->sbuf[i].urb); + + if (!ibmcam->remove_pending) { + usb_ibmcam_setup_video_stop(ibmcam); + + /* Set packet size to 0 */ + j = usb_set_interface(ibmcam->dev, ibmcam->iface, ibmcam->ifaceAltInactive); + if (j < 0) { + printk(KERN_ERR "%s: usb_set_interface() error %d.\n", proc, j); + ibmcam->last_error = j; + } + } +} + +/* + * ibmcam_new_frame() + * + * History: + * 29-Mar-00 Added copying of previous frame into the current one. + */ +static int ibmcam_new_frame(struct usb_ibmcam *ibmcam, int framenum) +{ + struct ibmcam_frame *frame; + int n, width, height; + + /* If we're not grabbing a frame right now and the other frame is */ + /* ready to be grabbed into, then use it instead */ + if (ibmcam->curframe != -1) + return 0; + + n = (framenum - 1 + IBMCAM_NUMFRAMES) % IBMCAM_NUMFRAMES; + if (ibmcam->frame[n].grabstate == FRAME_READY) + framenum = n; + + frame = &ibmcam->frame[framenum]; + + frame->grabstate = FRAME_GRABBING; + frame->scanstate = STATE_SCANNING; + frame->scanlength = 0; /* Accumulated in ibmcam_parse_data() */ + ibmcam->curframe = framenum; + + /* + * Normally we would want to copy previous frame into the current one + * before we even start filling it with data; this allows us to stop + * filling at any moment; top portion of the frame will be new and + * bottom portion will stay as it was in previous frame. If we don't + * do that then missing chunks of video stream will result in flickering + * portions of old data whatever it was before. + * + * If we choose not to copy previous frame (to, for example, save few + * bus cycles - the frame can be pretty large!) then we have an option + * to clear the frame before using. If we experience losses in this + * mode then missing picture will be black (no flickering). + * + * Finally, if user chooses not to clean the current frame before + * filling it with data then the old data will be visible if we fail + * to refill entire frame with new data. + */ + if (!(flags & FLAGS_SEPARATE_FRAMES)) { + /* This copies previous frame into this one to mask losses */ + memmove(frame->data, ibmcam->frame[1-framenum].data, MAX_FRAME_SIZE); + } else { + if (flags & FLAGS_CLEAN_FRAMES) { + /* This provides a "clean" frame but slows things down */ + memset(frame->data, 0, MAX_FRAME_SIZE); + } + } + switch (videosize) { + case VIDEOSIZE_128x96: + frame->frmwidth = 128; + frame->frmheight = 96; + frame->order_uv = 1; /* U Y V Y ... */ + frame->hdr_sig = 0x06; /* 00 FF 00 06 */ + break; + case VIDEOSIZE_176x144: + frame->frmwidth = 176; + frame->frmheight = 144; + frame->order_uv = 1; /* U Y V Y ... */ + frame->hdr_sig = 0x0E; /* 00 FF 00 0E */ + break; + case VIDEOSIZE_320x240: /* For model 2 only */ + frame->frmwidth = 320; + frame->frmheight = 240; + break; + case VIDEOSIZE_352x240: /* For model 2 only */ + frame->frmwidth = 352; + frame->frmheight = 240; + break; + case VIDEOSIZE_352x288: + frame->frmwidth = 352; + frame->frmheight = 288; + frame->order_uv = 0; /* V Y U Y ... */ + frame->hdr_sig = 0x00; /* 00 FF 00 00 */ + break; + } + frame->order_yc = (ibmcam->camera_model == IBMCAM_MODEL_2); + + width = frame->width; + RESTRICT_TO_RANGE(width, min_imgwidth, imgwidth); + width &= ~7; /* Multiple of 8 */ + + height = frame->height; + RESTRICT_TO_RANGE(height, min_imgheight, imgheight); + height &= ~3; /* Multiple of 4 */ + + return 0; +} + +/* + * ibmcam_open() + * + * This is part of Video 4 Linux API. The driver can be opened by one + * client only (checks internal counter 'ibmcam->user'). The procedure + * then allocates buffers needed for video processing. + * + * History: + * 1/22/00 Rewrote, moved scratch buffer allocation here. Now the + * camera is also initialized here (once per connect), at + * expense of V4L client (it waits on open() call). + * 1/27/00 Used IBMCAM_NUMSBUF as number of URB buffers. + * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + */ +static int ibmcam_open(struct video_device *dev, int flags) +{ + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; + const int sb_size = FRAMES_PER_DESC * ibmcam->iso_packet_len; + int i, err = 0; + + MOD_INC_USE_COUNT; + down(&ibmcam->lock); + + if (ibmcam->user) + err = -EBUSY; + else { + /* Clean pointers so we know if we allocated something */ + for (i=0; i < IBMCAM_NUMSBUF; i++) + ibmcam->sbuf[i].data = NULL; + + /* Allocate memory for the frame buffers */ + ibmcam->fbuf_size = IBMCAM_NUMFRAMES * MAX_FRAME_SIZE; + ibmcam->fbuf = rvmalloc(ibmcam->fbuf_size); + ibmcam->scratch = kmalloc(scratchbufsize, GFP_KERNEL); + ibmcam->scratchlen = 0; + if ((ibmcam->fbuf == NULL) || (ibmcam->scratch == NULL)) + err = -ENOMEM; + else { + /* Allocate all buffers */ + for (i=0; i < IBMCAM_NUMFRAMES; i++) { + ibmcam->frame[i].grabstate = FRAME_UNUSED; + ibmcam->frame[i].data = ibmcam->fbuf + i*MAX_FRAME_SIZE; + /* + * Set default sizes in case IOCTL (VIDIOCMCAPTURE) + * is not used (using read() instead). + */ + ibmcam->frame[i].width = imgwidth; + ibmcam->frame[i].height = imgheight; + ibmcam->frame[i].bytes_read = 0; + } + for (i=0; i < IBMCAM_NUMSBUF; i++) { + ibmcam->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL); + if (ibmcam->sbuf[i].data == NULL) { + err = -ENOMEM; + break; + } + } + } + if (err) { + /* Have to free all that memory */ + if (ibmcam->fbuf != NULL) { + rvfree(ibmcam->fbuf, ibmcam->fbuf_size); + ibmcam->fbuf = NULL; + } + if (ibmcam->scratch != NULL) { + kfree(ibmcam->scratch); + ibmcam->scratch = NULL; + } + for (i=0; i < IBMCAM_NUMSBUF; i++) { + if (ibmcam->sbuf[i].data != NULL) { + kfree (ibmcam->sbuf[i].data); + ibmcam->sbuf[i].data = NULL; + } + } + } + } + + /* If so far no errors then we shall start the camera */ + if (!err) { + err = ibmcam_init_isoc(ibmcam); + if (!err) { + /* Send init sequence only once, it's large! */ + if (!ibmcam->initialized) { + int setup_ok = 0; + if (ibmcam->camera_model == IBMCAM_MODEL_1) + setup_ok = usb_ibmcam_model1_setup(ibmcam); + else if (ibmcam->camera_model == IBMCAM_MODEL_2) + setup_ok = usb_ibmcam_model2_setup(ibmcam); + if (setup_ok) + ibmcam->initialized = 1; + else + err = -EBUSY; + } + if (!err) + ibmcam->user++; + } + } + up(&ibmcam->lock); + if (err) + MOD_DEC_USE_COUNT; + return err; +} + +/* + * ibmcam_close() + * + * This is part of Video 4 Linux API. The procedure + * stops streaming and deallocates all buffers that were earlier + * allocated in ibmcam_open(). + * + * History: + * 1/22/00 Moved scratch buffer deallocation here. + * 1/27/00 Used IBMCAM_NUMSBUF as number of URB buffers. + * 5/24/00 Moved MOD_DEC_USE_COUNT outside of code that can sleep. + */ +static void ibmcam_close(struct video_device *dev) +{ + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; + int i; + + down(&ibmcam->lock); + + ibmcam_stop_isoc(ibmcam); + + rvfree(ibmcam->fbuf, ibmcam->fbuf_size); + kfree(ibmcam->scratch); + for (i=0; i < IBMCAM_NUMSBUF; i++) + kfree(ibmcam->sbuf[i].data); + + ibmcam->user--; + + if (ibmcam->remove_pending) { + printk(KERN_INFO "ibmcam_close: Final disconnect.\n"); + usb_ibmcam_release(ibmcam); + } + up(&ibmcam->lock); + MOD_DEC_USE_COUNT; +} + +static int ibmcam_init_done(struct video_device *dev) +{ + return 0; +} + +static long ibmcam_write(struct video_device *dev, const char *buf, unsigned long count, int noblock) +{ + return -EINVAL; +} + +/* + * ibmcam_ioctl() + * + * This is part of Video 4 Linux API. The procedure handles ioctl() calls. + * + * History: + * 1/22/00 Corrected VIDIOCSPICT to reject unsupported settings. + */ +static int ibmcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; + + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return -EFAULT; + + switch (cmd) { + case VIDIOCGCAP: + { + if (copy_to_user(arg, &ibmcam->vcap, sizeof(ibmcam->vcap))) + return -EFAULT; + return 0; + } + case VIDIOCGCHAN: + { + if (copy_to_user(arg, &ibmcam->vchan, sizeof(ibmcam->vchan))) + return -EFAULT; + return 0; + } + case VIDIOCSCHAN: + { + int v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if ((v < 0) || (v >= 3)) /* 3 grades of lighting conditions */ + return -EINVAL; + if (v != ibmcam->vchan.channel) { + ibmcam->vchan.channel = v; + usb_ibmcam_change_lighting_conditions(ibmcam); + } + return 0; + } + case VIDIOCGPICT: + { + if (copy_to_user(arg, &ibmcam->vpic, sizeof(ibmcam->vpic))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture tmp; + /* + * Use temporary 'video_picture' structure to preserve our + * own settings (such as color depth, palette) that we + * aren't allowing everyone (V4L client) to change. + */ + if (copy_from_user(&tmp, arg, sizeof(tmp))) + return -EFAULT; + ibmcam->vpic.brightness = tmp.brightness; + ibmcam->vpic.hue = tmp.hue; + ibmcam->vpic.colour = tmp.colour; + ibmcam->vpic.contrast = tmp.contrast; + usb_ibmcam_adjust_picture(ibmcam); + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + + if (copy_from_user(&vw, arg, sizeof(vw))) + return -EFAULT; + if (vw.flags) + return -EINVAL; + if (vw.clipcount) + return -EINVAL; + if (vw.height != imgheight) + return -EINVAL; + if (vw.width != imgwidth) + return -EINVAL; + + ibmcam->compress = 0; + + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + + vw.x = 0; + vw.y = 0; + vw.width = imgwidth; + vw.height = imgheight; + vw.chromakey = 0; + vw.flags = usb_ibmcam_calculate_fps(); + + if (copy_to_user(arg, &vw, sizeof(vw))) + return -EFAULT; + + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf vm; + + memset(&vm, 0, sizeof(vm)); + vm.size = MAX_FRAME_SIZE * 2; + vm.frames = 2; + vm.offsets[0] = 0; + vm.offsets[1] = MAX_FRAME_SIZE; + + if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) + return -EFAULT; + + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + + if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) + return -EFAULT; + + if (debug >= 1) + printk(KERN_DEBUG "frame: %d, size: %dx%d, format: %d\n", + vm.frame, vm.width, vm.height, vm.format); + + if (vm.format != VIDEO_PALETTE_RGB24) + return -EINVAL; + + if ((vm.frame != 0) && (vm.frame != 1)) + return -EINVAL; + + if (ibmcam->frame[vm.frame].grabstate == FRAME_GRABBING) + return -EBUSY; + + /* Don't compress if the size changed */ + if ((ibmcam->frame[vm.frame].width != vm.width) || + (ibmcam->frame[vm.frame].height != vm.height)) + ibmcam->compress = 0; + + ibmcam->frame[vm.frame].width = vm.width; + ibmcam->frame[vm.frame].height = vm.height; + + /* Mark it as ready */ + ibmcam->frame[vm.frame].grabstate = FRAME_READY; + + return ibmcam_new_frame(ibmcam, vm.frame); + } + case VIDIOCSYNC: + { + int frame; + + if (copy_from_user((void *)&frame, arg, sizeof(int))) + return -EFAULT; + + if (debug >= 1) + printk(KERN_DEBUG "ibmcam: syncing to frame %d\n", frame); + + switch (ibmcam->frame[frame].grabstate) { + case FRAME_UNUSED: + return -EINVAL; + case FRAME_READY: + case FRAME_GRABBING: + case FRAME_ERROR: + { + int ntries; + redo: + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return -EIO; + ntries = 0; + do { + interruptible_sleep_on(&ibmcam->frame[frame].wq); + if (signal_pending(current)) { + if (flags & FLAGS_RETRY_VIDIOCSYNC) { + /* Polling apps will destroy frames with that! */ + ibmcam_new_frame(ibmcam, frame); + usb_ibmcam_testpattern(ibmcam, 1, 0); + ibmcam->curframe = -1; + ibmcam->frame_num++; + + /* This will request another frame. */ + if (waitqueue_active(&ibmcam->frame[frame].wq)) + wake_up_interruptible(&ibmcam->frame[frame].wq); + return 0; + } else { + /* Standard answer: not ready yet! */ + return -EINTR; + } + } + } while (ibmcam->frame[frame].grabstate == FRAME_GRABBING); + + if (ibmcam->frame[frame].grabstate == FRAME_ERROR) { + int ret = ibmcam_new_frame(ibmcam, frame); + if (ret < 0) + return ret; + goto redo; + } + } + case FRAME_DONE: + ibmcam->frame[frame].grabstate = FRAME_UNUSED; + break; + } + + ibmcam->frame[frame].grabstate = FRAME_UNUSED; + + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer vb; + + memset(&vb, 0, sizeof(vb)); + vb.base = NULL; /* frame buffer not supported, not used */ + + if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) + return -EFAULT; + + return 0; + } + case VIDIOCKEY: + return 0; + + case VIDIOCCAPTURE: + return -EINVAL; + + case VIDIOCSFBUF: + + case VIDIOCGTUNER: + case VIDIOCSTUNER: + + case VIDIOCGFREQ: + case VIDIOCSFREQ: + + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static long ibmcam_read(struct video_device *dev, char *buf, unsigned long count, int noblock) +{ + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; + int frmx = -1; + volatile struct ibmcam_frame *frame; + + if (debug >= 1) + printk(KERN_DEBUG "ibmcam_read: %ld bytes, noblock=%d\n", count, noblock); + + if (!IBMCAM_IS_OPERATIONAL(ibmcam) || (buf == NULL)) + return -EFAULT; + + /* See if a frame is completed, then use it. */ + if (ibmcam->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */ + frmx = 0; + else if (ibmcam->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */ + frmx = 1; + + if (noblock && (frmx == -1)) + return -EAGAIN; + + /* If no FRAME_DONE, look for a FRAME_GRABBING state. */ + /* See if a frame is in process (grabbing), then use it. */ + if (frmx == -1) { + if (ibmcam->frame[0].grabstate == FRAME_GRABBING) + frmx = 0; + else if (ibmcam->frame[1].grabstate == FRAME_GRABBING) + frmx = 1; + } + + /* If no frame is active, start one. */ + if (frmx == -1) + ibmcam_new_frame(ibmcam, frmx = 0); + + frame = &ibmcam->frame[frmx]; + +restart: + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return -EIO; + while (frame->grabstate == FRAME_GRABBING) { + interruptible_sleep_on((void *)&frame->wq); + if (signal_pending(current)) + return -EINTR; + } + + if (frame->grabstate == FRAME_ERROR) { + frame->bytes_read = 0; + if (ibmcam_new_frame(ibmcam, frmx)) + printk(KERN_ERR "ibmcam_read: ibmcam_new_frame error\n"); + goto restart; + } + + if (debug >= 1) + printk(KERN_DEBUG "ibmcam_read: frmx=%d, bytes_read=%ld, scanlength=%ld\n", + frmx, frame->bytes_read, frame->scanlength); + + /* copy bytes to user space; we allow for partials reads */ + if ((count + frame->bytes_read) > frame->scanlength) + count = frame->scanlength - frame->bytes_read; + + if (copy_to_user(buf, frame->data + frame->bytes_read, count)) + return -EFAULT; + + frame->bytes_read += count; + if (debug >= 1) + printk(KERN_DEBUG "ibmcam_read: {copy} count used=%ld, new bytes_read=%ld\n", + count, frame->bytes_read); + + if (frame->bytes_read >= frame->scanlength) { /* All data has been read */ + frame->bytes_read = 0; + + /* Mark it as available to be used again. */ + ibmcam->frame[frmx].grabstate = FRAME_UNUSED; + if (ibmcam_new_frame(ibmcam, frmx ? 0 : 1)) + printk(KERN_ERR "ibmcam_read: ibmcam_new_frame returned error\n"); + } + + return count; +} + +static int ibmcam_mmap(struct video_device *dev, const char *adr, unsigned long size) +{ + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; + unsigned long start = (unsigned long)adr; + unsigned long page, pos; + + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return -EFAULT; + + if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + return -EINVAL; + + pos = (unsigned long)ibmcam->fbuf; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +static struct video_device ibmcam_template = { + "CPiA USB Camera", + VID_TYPE_CAPTURE, + VID_HARDWARE_CPIA, + ibmcam_open, + ibmcam_close, + ibmcam_read, + ibmcam_write, + NULL, + ibmcam_ioctl, + ibmcam_mmap, + ibmcam_init_done, + NULL, + 0, + 0 +}; + +static void usb_ibmcam_configure_video(struct usb_ibmcam *ibmcam) +{ + if (ibmcam == NULL) + return; + + RESTRICT_TO_RANGE(init_brightness, 0, 255); + RESTRICT_TO_RANGE(init_contrast, 0, 255); + RESTRICT_TO_RANGE(init_color, 0, 255); + RESTRICT_TO_RANGE(init_hue, 0, 255); + RESTRICT_TO_RANGE(hue_correction, 0, 255); + + memset(&ibmcam->vpic, 0, sizeof(ibmcam->vpic)); + memset(&ibmcam->vpic_old, 0x55, sizeof(ibmcam->vpic_old)); + + ibmcam->vpic.colour = init_color << 8; + ibmcam->vpic.hue = init_hue << 8; + ibmcam->vpic.brightness = init_brightness << 8; + ibmcam->vpic.contrast = init_contrast << 8; + ibmcam->vpic.whiteness = 105 << 8; /* This one isn't used */ + ibmcam->vpic.depth = 24; + ibmcam->vpic.palette = VIDEO_PALETTE_RGB24; + + memset(&ibmcam->vcap, 0, sizeof(ibmcam->vcap)); + strcpy(ibmcam->vcap.name, "IBM USB Camera"); + ibmcam->vcap.type = VID_TYPE_CAPTURE; + ibmcam->vcap.channels = 1; + ibmcam->vcap.audios = 0; + ibmcam->vcap.maxwidth = imgwidth; + ibmcam->vcap.maxheight = imgheight; + ibmcam->vcap.minwidth = min_imgwidth; + ibmcam->vcap.minheight = min_imgheight; + + memset(&ibmcam->vchan, 0, sizeof(ibmcam->vchan)); + ibmcam->vchan.flags = 0; + ibmcam->vchan.tuners = 0; + ibmcam->vchan.channel = 0; + ibmcam->vchan.type = VIDEO_TYPE_CAMERA; + strcpy(ibmcam->vchan.name, "Camera"); +} + +/* + * ibmcam_find_struct() + * + * This code searches the array of preallocated (static) structures + * and returns index of the first one that isn't in use. Returns -1 + * if there are no free structures. + * + * History: + * 1/27/00 Created. + */ +static int ibmcam_find_struct(void) +{ + int i, u; + + for (u = 0; u < MAX_IBMCAM; u++) { + struct usb_ibmcam *ibmcam = &cams[u]; + if (!ibmcam->ibmcam_used) /* This one is free */ + { + ibmcam->ibmcam_used = 1; /* In use now */ + for (i=0; i < IBMCAM_NUMFRAMES; i++) + init_waitqueue_head(&ibmcam->frame[i].wq); + init_MUTEX(&ibmcam->lock); /* to 1 == available */ + ibmcam->dev = NULL; + memcpy(&ibmcam->vdev, &ibmcam_template, sizeof(ibmcam_template)); + return u; + } + } + return -1; +} + +/* + * usb_ibmcam_probe() + * + * This procedure queries device descriptor and accepts the interface + * if it looks like IBM C-it camera. + * + * History: + * 1/22/00 Moved camera init code to ibmcam_open() + * 1/27/00 Changed to use static structures, added locking. + * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + */ +static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_ibmcam *ibmcam = NULL; + const unsigned char *p_rev; + const struct usb_interface_descriptor *interface; + const struct usb_endpoint_descriptor *endpoint; + int devnum, model=0; + + if (debug >= 1) + printk(KERN_DEBUG "ibmcam_probe(%p,%u.)\n", dev, ifnum); + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return NULL; + + /* Is it an IBM camera? */ + if ((dev->descriptor.idVendor != 0x0545) || + (dev->descriptor.idProduct != 0x8080)) + return NULL; + + /* Check the version/revision */ + p_rev = (const unsigned char *) &dev->descriptor.bcdDevice; + if (p_rev[1] == 0x00 && p_rev[0] == 0x02) { + if (ifnum != 2) + return NULL; + printk(KERN_INFO "IBM USB camera found (model 1).\n"); + model = IBMCAM_MODEL_1; + } else if (p_rev[1] == 0x03 && p_rev[0] == 0x0A) { + if (ifnum != 0) + return NULL; + printk(KERN_INFO "IBM USB camera found (model 2).\n"); + model = IBMCAM_MODEL_2; + } else { + printk(KERN_ERR "IBM camera revision=%02x.%02x not supported\n", + p_rev[1], p_rev[0]); + return NULL; + } + + /* Validate found interface: must have one ISO endpoint */ + interface = &dev->actconfig->interface[ifnum].altsetting[0]; + if (interface->bNumEndpoints != 1) { + printk(KERN_ERR "IBM camera: interface %d. has %u. endpoints!\n", + ifnum, (unsigned)(interface->bNumEndpoints)); + return NULL; + } + endpoint = &interface->endpoint[0]; + if ((endpoint->bmAttributes & 0x03) != 0x01) { + printk(KERN_ERR "IBM camera: interface %d. has non-ISO endpoint!\n", ifnum); + return NULL; + } + if ((endpoint->bEndpointAddress & 0x80) == 0) { + printk(KERN_ERR "IBM camera: interface %d. has ISO OUT endpoint!\n", ifnum); + return NULL; + } + + /* Validate options */ + if (model == IBMCAM_MODEL_1) { + RESTRICT_TO_RANGE(lighting, 0, 2); + RESTRICT_TO_RANGE(videosize, VIDEOSIZE_128x96, VIDEOSIZE_352x288); + } else { + RESTRICT_TO_RANGE(lighting, 0, 15); + RESTRICT_TO_RANGE(videosize, VIDEOSIZE_176x144, VIDEOSIZE_352x240); + } + + /* Code below may sleep, need to lock module while we are here */ + MOD_INC_USE_COUNT; + + devnum = ibmcam_find_struct(); + if (devnum == -1) { + printk(KERN_INFO "IBM USB camera driver: Too many devices!\n"); + ibmcam = NULL; /* Do not free, it's preallocated */ + goto probe_done; + } + ibmcam = &cams[devnum]; + + down(&ibmcam->lock); + ibmcam->camera_model = model; + ibmcam->remove_pending = 0; + ibmcam->last_error = 0; + ibmcam->dev = dev; + ibmcam->iface = ifnum; + ibmcam->ifaceAltInactive = 0; + ibmcam->ifaceAltActive = 1; + ibmcam->video_endp = endpoint->bEndpointAddress; + ibmcam->iso_packet_len = 1014; + ibmcam->compress = 0; + ibmcam->user=0; + + usb_ibmcam_configure_video(ibmcam); + up (&ibmcam->lock); + + if (video_register_device(&ibmcam->vdev, VFL_TYPE_GRABBER) == -1) { + printk(KERN_ERR "video_register_device failed\n"); + ibmcam = NULL; /* Do not free, it's preallocated */ + } + if (debug > 1) + printk(KERN_DEBUG "video_register_device() successful\n"); +probe_done: + MOD_DEC_USE_COUNT; + return ibmcam; +} + +/* + * usb_ibmcam_release() + * + * This code does final release of struct usb_ibmcam. This happens + * after the device is disconnected -and- all clients closed their files. + * + * History: + * 1/27/00 Created. + */ +static void usb_ibmcam_release(struct usb_ibmcam *ibmcam) +{ + video_unregister_device(&ibmcam->vdev); + if (debug > 0) + printk(KERN_DEBUG "usb_ibmcam_release: Video unregistered.\n"); + ibmcam->ibmcam_used = 0; +} + +/* + * usb_ibmcam_disconnect() + * + * This procedure stops all driver activity, deallocates interface-private + * structure (pointed by 'ptr') and after that driver should be removable + * with no ill consequences. + * + * This code handles surprise removal. The ibmcam->user is a counter which + * increments on open() and decrements on close(). If we see here that + * this counter is not 0 then we have a client who still has us opened. + * We set ibmcam->remove_pending flag as early as possible, and after that + * all access to the camera will gracefully fail. These failures should + * prompt client to (eventually) close the video device, and then - in + * ibmcam_close() - we decrement ibmcam->ibmcam_used and usage counter. + * + * History: + * 1/22/00 Added polling of MOD_IN_USE to delay removal until all users gone. + * 1/27/00 Reworked to allow pending disconnects; see ibmcam_close() + * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + */ +static void usb_ibmcam_disconnect(struct usb_device *dev, void *ptr) +{ + static const char proc[] = "usb_ibmcam_disconnect"; + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *) ptr; + + MOD_INC_USE_COUNT; + + if (debug > 0) + printk(KERN_DEBUG "%s(%p,%p.)\n", proc, dev, ptr); + + down(&ibmcam->lock); + ibmcam->remove_pending = 1; /* Now all ISO data will be ignored */ + + /* At this time we ask to cancel outstanding URBs */ + ibmcam_stop_isoc(ibmcam); + + ibmcam->dev = NULL; /* USB device is no more */ + + if (ibmcam->user) + printk(KERN_INFO "%s: In use, disconnect pending.\n", proc); + else + usb_ibmcam_release(ibmcam); + up(&ibmcam->lock); + printk(KERN_INFO "IBM USB camera disconnected.\n"); + + MOD_DEC_USE_COUNT; +} + +static struct usb_driver ibmcam_driver = { + "ibmcam", + usb_ibmcam_probe, + usb_ibmcam_disconnect, + { NULL, NULL } +}; + +/* + * usb_ibmcam_init() + * + * This code is run to initialize the driver. + * + * History: + * 1/27/00 Reworked to use statically allocated usb_ibmcam structures. + */ +int usb_ibmcam_init(void) +{ + unsigned u; + + /* Initialize struct */ + for (u = 0; u < MAX_IBMCAM; u++) { + struct usb_ibmcam *ibmcam = &cams[u]; + memset (ibmcam, 0, sizeof(struct usb_ibmcam)); + } + return usb_register(&ibmcam_driver); +} + +void usb_ibmcam_cleanup(void) +{ + usb_deregister(&ibmcam_driver); +} + +#ifdef MODULE +int init_module(void) +{ + return usb_ibmcam_init(); +} + +void cleanup_module(void) +{ + usb_ibmcam_cleanup(); +} +#endif diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/ibmcam.h linux-2.2.17/drivers/usb/ibmcam.h --- linux-2.2.17-orig/drivers/usb/ibmcam.h Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/ibmcam.h Sun Sep 24 04:26:23 2000 @@ -0,0 +1,240 @@ +/* + * Header file for USB IBM C-It Video Camera driver. + * + * Supports IBM C-It Video Camera. + * + * This driver is based on earlier work of: + * + * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Randy Dunlap + */ + +#ifndef __LINUX_IBMCAM_H +#define __LINUX_IBMCAM_H + +#include + +#define USES_IBMCAM_PUTPIXEL 0 /* 0=Fast/oops 1=Slow/secure */ + +/* Video Size 384 x 288 x 3 bytes for RGB */ +/* 384 because xawtv tries to grab 384 even though we tell it 352 is our max */ +#define V4L_FRAME_WIDTH 384 +#define V4L_FRAME_WIDTH_USED 352 +#define V4L_FRAME_HEIGHT 288 +#define V4L_BYTES_PER_PIXEL 3 +#define MAX_FRAME_SIZE (V4L_FRAME_WIDTH * V4L_FRAME_HEIGHT * V4L_BYTES_PER_PIXEL) + +/* Camera capabilities (maximum) */ +#define CAMERA_IMAGE_WIDTH 352 +#define CAMERA_IMAGE_HEIGHT 288 +#define CAMERA_IMAGE_LINE_SZ ((CAMERA_IMAGE_WIDTH * 3) / 2) /* Bytes */ +#define CAMERA_URB_FRAMES 32 +#define CAMERA_MAX_ISO_PACKET 1023 /* 1022 actually sent by camera */ + +#define IBMCAM_NUMFRAMES 2 +#define IBMCAM_NUMSBUF 2 + +#define FRAMES_PER_DESC (CAMERA_URB_FRAMES) +#define FRAME_SIZE_PER_DESC (CAMERA_MAX_ISO_PACKET) + +/* This macro restricts an int variable to an inclusive range */ +#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } + +/* + * This macro performs bounds checking - use it when working with + * new formats, or else you may get oopses all over the place. + * If pixel falls out of bounds then it gets shoved back (as close + * to place of offence as possible) and is painted bright red. + */ +#define IBMCAM_PUTPIXEL(fr, ix, iy, vr, vg, vb) { \ + register unsigned char *pf; \ + int limiter = 0, mx, my; \ + mx = ix; \ + my = iy; \ + if (mx < 0) { \ + mx=0; \ + limiter++; \ + } else if (mx >= 352) { \ + mx=351; \ + limiter++; \ + } \ + if (my < 0) { \ + my = 0; \ + limiter++; \ + } else if (my >= V4L_FRAME_HEIGHT) { \ + my = V4L_FRAME_HEIGHT - 1; \ + limiter++; \ + } \ + pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*352 + (ix)); \ + if (limiter) { \ + *pf++ = 0; \ + *pf++ = 0; \ + *pf++ = 0xFF; \ + } else { \ + *pf++ = (vb); \ + *pf++ = (vg); \ + *pf++ = (vr); \ + } \ +} + +/* + * We use macros to do YUV -> RGB conversion because this is + * very important for speed and totally unimportant for size. + * + * YUV -> RGB Conversion + * --------------------- + * + * B = 1.164*(Y-16) + 2.018*(V-128) + * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128) + * R = 1.164*(Y-16) + 1.596*(U-128) + * + * If you fancy integer arithmetics (as you should), hear this: + * + * 65536*B = 76284*(Y-16) + 132252*(V-128) + * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128) + * 65536*R = 76284*(Y-16) + 104595*(U-128) + * + * Make sure the output values are within [0..255] range. + */ +#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x))) +#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \ + int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \ + mm_y = (my) - 16; \ + mm_u = (mu) - 128; \ + mm_v = (mv) - 128; \ + mm_yc= mm_y * 76284; \ + mm_b = (mm_yc + 132252*mm_v ) >> 16; \ + mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \ + mm_r = (mm_yc + 104595*mm_u ) >> 16; \ + mb = LIMIT_RGB(mm_b); \ + mg = LIMIT_RGB(mm_g); \ + mr = LIMIT_RGB(mm_r); \ +} + +/* Debugging aid */ +#define IBMCAM_SAY_AND_WAIT(what) { \ + wait_queue_head_t wq; \ + init_waitqueue_head(&wq); \ + printk(KERN_INFO "Say: %s\n", what); \ + interruptible_sleep_on_timeout (&wq, HZ*3); \ +} + +/* + * This macro checks if ibmcam is still operational. The 'ibmcam' + * pointer must be valid, ibmcam->dev must be valid, we are not + * removing the device and the device has not erred on us. + */ +#define IBMCAM_IS_OPERATIONAL(ibm_cam) (\ + (ibm_cam != NULL) && \ + ((ibm_cam)->dev != NULL) && \ + ((ibm_cam)->last_error == 0) && \ + (!(ibm_cam)->remove_pending)) + +enum { + STATE_SCANNING, /* Scanning for header */ + STATE_LINES, /* Parsing lines */ +}; + +enum { + FRAME_UNUSED, /* Unused (no MCAPTURE) */ + FRAME_READY, /* Ready to start grabbing */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_ERROR, /* Something bad happened while processing */ +}; + +struct usb_device; + +struct ibmcam_sbuf { + char *data; + urb_t *urb; +}; + +struct ibmcam_frame { + char *data; /* Frame buffer */ + int order_uv; /* True=UV False=VU */ + int order_yc; /* True=Yc False=cY ('c'=either U or V) */ + unsigned char hdr_sig; /* "00 FF 00 ??" where 'hdr_sig' is '??' */ + + int width; /* Width application is expecting */ + int height; /* Height */ + + int frmwidth; /* Width the frame actually is */ + int frmheight; /* Height */ + + volatile int grabstate; /* State of grabbing */ + int scanstate; /* State of scanning */ + + int curline; /* Line of frame we're working on */ + + long scanlength; /* uncompressed, raw data length of frame */ + long bytes_read; /* amount of scanlength that has been read from *data */ + + wait_queue_head_t wq; /* Processes waiting */ +}; + +#define IBMCAM_MODEL_1 1 /* XVP-501, 3 interfaces, rev. 0.02 */ +#define IBMCAM_MODEL_2 2 /* KSX-X9903, 2 interfaces, rev. 3.0a */ + +struct usb_ibmcam { + struct video_device vdev; + + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; /* Video interface number */ + unsigned char ifaceAltActive, ifaceAltInactive; /* Alt settings */ + + struct semaphore lock; + int user; /* user count for exclusive use */ + + int ibmcam_used; /* Is this structure in use? */ + int initialized; /* Had we already sent init sequence? */ + int camera_model; /* What type of IBM camera we got? */ + int streaming; /* Are we streaming Isochronous? */ + int grabbing; /* Are we grabbing? */ + int last_error; /* What calamity struck us? */ + + int compress; /* Should the next frame be compressed? */ + + char *fbuf; /* Videodev buffer area */ + int fbuf_size; /* Videodev buffer size */ + + int curframe; + struct ibmcam_frame frame[IBMCAM_NUMFRAMES]; /* Double buffering */ + + int cursbuf; /* Current receiving sbuf */ + struct ibmcam_sbuf sbuf[IBMCAM_NUMSBUF]; /* Double buffering */ + volatile int remove_pending; /* If set then about to exit */ + + /* + * Scratch space from the Isochronous pipe. + * Scratch buffer should contain at least one pair of lines + * (CAMERA_IMAGE_LINE_SZ). We set it to two pairs here. + * This will be approximately 2 KB. HOWEVER in reality this + * buffer must be as large as hundred of KB because otherwise + * you'll get lots of overflows because V4L client may request + * frames not as uniformly as USB sources them. + */ + unsigned char *scratch; + int scratchlen; + + struct video_picture vpic, vpic_old; /* Picture settings */ + struct video_capability vcap; /* Video capabilities */ + struct video_channel vchan; /* May be used for tuner support */ + unsigned char video_endp; /* 0x82 for IBM camera */ + int has_hdr; + int frame_num; + int iso_packet_len; /* Videomode-dependent, saves bus bandwidth */ + + /* Statistics that can be overlayed on screen */ + unsigned long urb_count; /* How many URBs we received so far */ + unsigned long urb_length; /* Length of last URB */ + unsigned long data_count; /* How many bytes we received */ + unsigned long header_count; /* How many frame headers we found */ + unsigned long scratch_ovf_count;/* How many times we overflowed scratch */ + unsigned long iso_skip_count; /* How many empty ISO packets received */ + unsigned long iso_err_count; /* How many bad ISO packets received */ +}; + +#endif /* __LINUX_IBMCAM_H */ diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/inits.h linux-2.2.17/drivers/usb/inits.h --- linux-2.2.17-orig/drivers/usb/inits.h Fri Apr 30 08:20:30 1999 +++ linux-2.2.17/drivers/usb/inits.h Wed Dec 31 16:00:00 1969 @@ -1,6 +0,0 @@ -int bp_mouse_init(void); -int usb_kbd_init(void); -int usb_audio_init(void); -int hub_init(void); -void hub_cleanup(void); -void usb_mouse_cleanup(void); diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/inode.c linux-2.2.17/drivers/usb/inode.c --- linux-2.2.17-orig/drivers/usb/inode.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/inode.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,708 @@ +/*****************************************************************************/ + +/* + * inode.c -- Inode/Dentry functions for the USB device file system. + * + * Copyright (C) 2000 + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * 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. + * + * $Id: inode.c,v 1.3 2000/01/11 13:58:25 tom Exp $ + * + * History: + * 0.1 04.01.2000 Created + */ + +/*****************************************************************************/ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +static LIST_HEAD(superlist); + +struct special { + const char *name; + struct inode_operations *iops; + struct list_head inodes; +}; + +static struct inode_operations usbdevfs_devices_inode_operations = { + default_file_ops: &usbdevfs_devices_fops, +}; + +static struct inode_operations usbdevfs_drivers_inode_operations = { + default_file_ops: &usbdevfs_drivers_fops, +}; + +static struct inode_operations usbdevfs_device_inode_operations = { + default_file_ops: &usbdevfs_device_file_operations, +}; + +static struct special special[] = { + { "devices", &usbdevfs_devices_inode_operations, }, + { "drivers", &usbdevfs_drivers_inode_operations, } +}; + +#define NRSPECIAL (sizeof(special)/sizeof(special[0])) + +/* --------------------------------------------------------------------- */ + +static int dnumber(struct dentry *dentry) +{ + const char *name; + unsigned int s; + + if (dentry->d_name.len != 3) + return -1; + name = dentry->d_name.name; + if (name[0] < '0' || name[0] > '9' || + name[1] < '0' || name[1] > '9' || + name[2] < '0' || name[2] > '9') + return -1; + s = name[0] - '0'; + s = s * 10 + name[1] - '0'; + s = s * 10 + name[2] - '0'; + return s; +} + +/* + * utility functions; should be called with the kernel lock held + * to protect against busses/devices appearing/disappearing + */ + +static void new_dev_inode(struct usb_device *dev, struct super_block *sb) +{ + struct inode *inode; + unsigned int devnum = dev->devnum; + unsigned int busnum = dev->bus->busnum; + + if (devnum < 1 || devnum > 127 || busnum > 255) + return; + inode = iget(sb, IDEVICE | (busnum << 8) | devnum); + if (!inode) { + printk(KERN_ERR "usbdevfs: cannot create inode for bus %u device %u\n", busnum, devnum); + return; + } + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_uid = sb->u.usbdevfs_sb.devuid; + inode->i_gid = sb->u.usbdevfs_sb.devgid; + inode->i_mode = sb->u.usbdevfs_sb.devmode | S_IFREG; + inode->i_op = &usbdevfs_device_inode_operations; + inode->i_size = sizeof(struct usb_device_descriptor); + inode->u.usbdev_i.p.dev = dev; + list_add_tail(&inode->u.usbdev_i.slist, &sb->u.usbdevfs_sb.ilist); + list_add_tail(&inode->u.usbdev_i.dlist, &dev->inodes); +} + +static void recurse_new_dev_inode(struct usb_device *dev, struct super_block *sb) +{ + unsigned int i; + + if (!dev) + return; + new_dev_inode(dev, sb); + for (i = 0; i < dev->maxchild; i++) { + if (!dev->children[i]) + continue; + recurse_new_dev_inode(dev->children[i], sb); + } +} + +static void new_bus_inode(struct usb_bus *bus, struct super_block *sb) +{ + struct inode *inode; + unsigned int busnum = bus->busnum; + + if (busnum > 255) + return; + inode = iget(sb, IBUS | (busnum << 8)); + if (!inode) { + printk(KERN_ERR "usbdevfs: cannot create inode for bus %u\n", busnum); + return; + } + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_uid = sb->u.usbdevfs_sb.busuid; + inode->i_gid = sb->u.usbdevfs_sb.busgid; + inode->i_mode = sb->u.usbdevfs_sb.busmode | S_IFDIR; + inode->i_op = &usbdevfs_bus_inode_operations; + inode->u.usbdev_i.p.bus = bus; + list_add_tail(&inode->u.usbdev_i.slist, &sb->u.usbdevfs_sb.ilist); + list_add_tail(&inode->u.usbdev_i.dlist, &bus->inodes); +} + +static void free_inode(struct inode *inode) +{ + inode->u.usbdev_i.p.bus = NULL; + inode->u.usbdev_i.p.dev = NULL; + inode->i_mode &= ~S_IRWXUGO; + inode->i_uid = inode->i_gid = 0; + inode->i_size = 0; + list_del(&inode->u.usbdev_i.slist); + INIT_LIST_HEAD(&inode->u.usbdev_i.slist); + list_del(&inode->u.usbdev_i.dlist); + INIT_LIST_HEAD(&inode->u.usbdev_i.dlist); + iput(inode); +} + +static struct usb_bus *usbdevfs_findbus(int busnr) +{ + struct list_head *list; + struct usb_bus *bus; + + for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) { + bus = list_entry(list, struct usb_bus, bus_list); + if (bus->busnum == busnr) + return bus; + } + return NULL; +} + +#if 0 +static struct usb_device *finddev(struct usb_device *dev, int devnr) +{ + unsigned int i; + struct usb_device *d2; + + if (!dev) + return NULL; + if (dev->devnum == devnr) + return dev; + for (i = 0; i < dev->maxchild; i++) { + if (!dev->children[i]) + continue; + if ((d2 = finddev(dev->children[i], devnr))) + return d2; + } + return NULL; +} + +static struct usb_device *usbdevfs_finddevice(struct usb_bus *bus, int devnr) +{ + return finddev(bus->root_hub, devnr); +} +#endif + +/* --------------------------------------------------------------------- */ + +static int usbdevfs_revalidate(struct dentry *dentry, int flags) +{ + struct inode *inode = dentry->d_inode; + + if (!inode) + return 0; + if (ITYPE(inode->i_ino) == IBUS && !inode->u.usbdev_i.p.bus) + return 0; + if (ITYPE(inode->i_ino) == IDEVICE && !inode->u.usbdev_i.p.dev) + return 0; + return 1; +} + +static struct dentry_operations usbdevfs_dentry_operations = { + d_revalidate: usbdevfs_revalidate, +}; + +static struct dentry *usbdevfs_root_lookup(struct inode *dir, struct dentry *dentry) +{ + int busnr; + unsigned long ino = 0; + unsigned int i; + struct inode *inode; + + /* sanity check */ + if (dir->i_ino != IROOT) + return ERR_PTR(-EINVAL); + dentry->d_op = &usbdevfs_dentry_operations; + busnr = dnumber(dentry); + if (busnr >= 0 && busnr <= 255) + ino = IBUS | (busnr << 8); + if (!ino) { + for (i = 0; i < NRSPECIAL; i++) { + if (strlen(special[i].name) == dentry->d_name.len && + !strncmp(special[i].name, dentry->d_name.name, dentry->d_name.len)) { + ino = ISPECIAL | (i + IROOT + 1); + break; + } + } + } + if (!ino) + return ERR_PTR(-ENOENT); + inode = iget(dir->i_sb, ino); + if (!inode) + return ERR_PTR(-EINVAL); + if (inode && ITYPE(ino) == IBUS && inode->u.usbdev_i.p.bus == NULL) { + iput(inode); + inode = NULL; + } + d_add(dentry, inode); + return NULL; +} + +static struct dentry *usbdevfs_bus_lookup(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode; + int devnr; + + /* sanity check */ + if (ITYPE(dir->i_ino) != IBUS) + return ERR_PTR(-EINVAL); + dentry->d_op = &usbdevfs_dentry_operations; + devnr = dnumber(dentry); + if (devnr < 1 || devnr > 127) + return ERR_PTR(-ENOENT); + inode = iget(dir->i_sb, IDEVICE | (dir->i_ino & (0xff << 8)) | devnr); + if (!inode) + return ERR_PTR(-EINVAL); + if (inode && inode->u.usbdev_i.p.dev == NULL) { + iput(inode); + inode = NULL; + } + d_add(dentry, inode); + return NULL; +} + +static int usbdevfs_root_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_dentry->d_inode; + unsigned long ino = inode->i_ino; + struct special *spec; + struct list_head *list; + struct usb_bus *bus; + char numbuf[8]; + unsigned int i; + + /* sanity check */ + if (ino != IROOT) + return -EINVAL; + i = filp->f_pos; + switch (i) { + case 0: + if (filldir(dirent, ".", 1, i, IROOT) < 0) + return 0; + filp->f_pos++; + i++; + /* fall through */ + + case 1: + if (filldir(dirent, "..", 2, i, IROOT) < 0) + return 0; + filp->f_pos++; + i++; + /* fall through */ + + default: + + while (i >= 2 && i < 2+NRSPECIAL) { + spec = &special[filp->f_pos-2]; + if (filldir(dirent, spec->name, strlen(spec->name), i, ISPECIAL | (filp->f_pos-2+IROOT)) < 0) + return 0; + filp->f_pos++; + i++; + } + if (i < 2+NRSPECIAL) + return 0; + i -= 2+NRSPECIAL; + lock_kernel(); + for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) { + if (i > 0) { + i--; + continue; + } + bus = list_entry(list, struct usb_bus, bus_list); + sprintf(numbuf, "%03d", bus->busnum); + if (filldir(dirent, numbuf, 3, filp->f_pos, IBUS | ((bus->busnum & 0xff) << 8)) < 0) + break; + filp->f_pos++; + } + unlock_kernel(); + return 0; + } +} + +static int bus_readdir(struct usb_device *dev, unsigned long ino, int pos, struct file *filp, void *dirent, filldir_t filldir) +{ + char numbuf[8]; + unsigned int i; + + if (!dev) + return pos; + sprintf(numbuf, "%03d", dev->devnum); + if (pos > 0) + pos--; + else { + if (filldir(dirent, numbuf, 3, filp->f_pos, ino | (dev->devnum & 0xff)) < 0) + return -1; + filp->f_pos++; + } + for (i = 0; i < dev->maxchild; i++) { + if (!dev->children[i]) + continue; + pos = bus_readdir(dev->children[i], ino, pos, filp, dirent, filldir); + if (pos < 0) + return -1; + } + return pos; +} + +static int usbdevfs_bus_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_dentry->d_inode; + unsigned long ino = inode->i_ino; + struct usb_bus *bus; + + /* sanity check */ + if (ITYPE(ino) != IBUS) + return -EINVAL; + switch ((unsigned int)filp->f_pos) { + case 0: + if (filldir(dirent, ".", 1, filp->f_pos, ino) < 0) + return 0; + filp->f_pos++; + /* fall through */ + + case 1: + if (filldir(dirent, "..", 2, filp->f_pos, IROOT) < 0) + return 0; + filp->f_pos++; + /* fall through */ + + default: + lock_kernel(); + bus = usbdevfs_findbus(IBUSNR(ino)); + bus_readdir(bus->root_hub, IDEVICE | ((bus->busnum & 0xff) << 8), filp->f_pos-2, filp, dirent, filldir); + unlock_kernel(); + return 0; + } +} + +static struct file_operations usbdevfs_root_file_operations = { + readdir: usbdevfs_root_readdir, +}; + +static struct inode_operations usbdevfs_root_inode_operations = { + default_file_ops: &usbdevfs_root_file_operations, + lookup: usbdevfs_root_lookup, +}; + +static struct file_operations usbdevfs_bus_file_operations = { + readdir: usbdevfs_bus_readdir, +}; + +static struct inode_operations usbdevfs_bus_inode_operations = { + default_file_ops: &usbdevfs_bus_file_operations, + lookup: usbdevfs_bus_lookup, +}; + +static void usbdevfs_read_inode(struct inode *inode) +{ + struct special *spec; + + inode->i_ctime = inode->i_mtime = inode->i_atime = CURRENT_TIME; + inode->i_mode = S_IFREG; + inode->i_gid = inode->i_uid = 0; + INIT_LIST_HEAD(&inode->u.usbdev_i.dlist); + INIT_LIST_HEAD(&inode->u.usbdev_i.slist); + inode->u.usbdev_i.p.dev = NULL; + inode->u.usbdev_i.p.bus = NULL; + switch (ITYPE(inode->i_ino)) { + case ISPECIAL: + if (inode->i_ino == IROOT) { + inode->i_op = &usbdevfs_root_inode_operations; + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; + return; + } + if (inode->i_ino <= IROOT || inode->i_ino > IROOT+NRSPECIAL) + return; + spec = &special[inode->i_ino-(IROOT+1)]; + inode->i_op = spec->iops; + return; + + case IDEVICE: + return; + + case IBUS: + return; + + default: + return; + } +} + +static void usbdevfs_put_super(struct super_block *sb) +{ + list_del(&sb->u.usbdevfs_sb.slist); + INIT_LIST_HEAD(&sb->u.usbdevfs_sb.slist); + while (!list_empty(&sb->u.usbdevfs_sb.ilist)) + free_inode(list_entry(sb->u.usbdevfs_sb.ilist.next, struct inode, u.usbdev_i.slist)); +} + +static int usbdevfs_statfs(struct super_block *sb, struct statfs *buf, int x) +{ + buf->f_type = USBDEVICE_SUPER_MAGIC; + buf->f_bsize = PAGE_SIZE/sizeof(long); /* ??? */ + buf->f_bfree = 0; + buf->f_bavail = 0; + buf->f_ffree = 0; + buf->f_namelen = NAME_MAX; + return 0; +} + +static struct super_operations usbdevfs_sops = { + read_inode: usbdevfs_read_inode, + put_super: usbdevfs_put_super, + statfs: usbdevfs_statfs, +}; + +struct super_block *usbdevfs_read_super(struct super_block *s, void *data, int silent) +{ + struct inode *root_inode, *inode; + struct list_head *blist; + struct usb_bus *bus; + unsigned int i; + uid_t devuid = 0, busuid = 0, listuid = 0; + gid_t devgid = 0, busgid = 0, listgid = 0; + umode_t devmode = S_IWUSR | S_IRUGO, busmode = S_IXUGO | S_IRUGO, listmode = S_IRUGO; + char *curopt = NULL, *value; + + /* parse options */ + if (data) + curopt = strtok(data, ","); + for (; curopt; curopt = strtok(NULL, ",")) { + if ((value = strchr(curopt, '=')) != NULL) + *value++ = 0; + if (!strcmp(curopt, "devuid")) { + if (!value || !value[0]) + goto opterr; + devuid = simple_strtoul(value, &value, 0); + if (*value) + goto opterr; + } + if (!strcmp(curopt, "devgid")) { + if (!value || !value[0]) + goto opterr; + devgid = simple_strtoul(value, &value, 0); + if (*value) + goto opterr; + } + if (!strcmp(curopt, "devmode")) { + if (!value || !value[0]) + goto opterr; + devmode = simple_strtoul(value, &value, 0) & S_IRWXUGO; + if (*value) + goto opterr; + } + if (!strcmp(curopt, "busuid")) { + if (!value || !value[0]) + goto opterr; + busuid = simple_strtoul(value, &value, 0); + if (*value) + goto opterr; + } + if (!strcmp(curopt, "busgid")) { + if (!value || !value[0]) + goto opterr; + busgid = simple_strtoul(value, &value, 0); + if (*value) + goto opterr; + } + if (!strcmp(curopt, "busmode")) { + if (!value || !value[0]) + goto opterr; + busmode = simple_strtoul(value, &value, 0) & S_IRWXUGO; + if (*value) + goto opterr; + } + if (!strcmp(curopt, "listuid")) { + if (!value || !value[0]) + goto opterr; + listuid = simple_strtoul(value, &value, 0); + if (*value) + goto opterr; + } + if (!strcmp(curopt, "listgid")) { + if (!value || !value[0]) + goto opterr; + listgid = simple_strtoul(value, &value, 0); + if (*value) + goto opterr; + } + if (!strcmp(curopt, "listmode")) { + if (!value || !value[0]) + goto opterr; + listmode = simple_strtoul(value, &value, 0) & S_IRWXUGO; + if (*value) + goto opterr; + } + } + /* fill superblock */ + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = USBDEVICE_SUPER_MAGIC; + s->s_op = &usbdevfs_sops; + INIT_LIST_HEAD(&s->u.usbdevfs_sb.slist); + INIT_LIST_HEAD(&s->u.usbdevfs_sb.ilist); + s->u.usbdevfs_sb.devuid = devuid; + s->u.usbdevfs_sb.devgid = devgid; + s->u.usbdevfs_sb.devmode = devmode; + s->u.usbdevfs_sb.busuid = busuid; + s->u.usbdevfs_sb.busgid = busgid; + s->u.usbdevfs_sb.busmode = busmode; + root_inode = iget(s, IROOT); + if (!root_inode) + goto out_no_root; + s->s_root = d_alloc_root(root_inode, NULL); + if (!s->s_root) + goto out_no_root; + list_add_tail(&s->u.usbdevfs_sb.slist, &superlist); + for (i = 0; i < NRSPECIAL; i++) { + if (!(inode = iget(s, IROOT+1+i))) + continue; + inode->i_uid = listuid; + inode->i_gid = listgid; + inode->i_mode = listmode | S_IFREG; + list_add_tail(&inode->u.usbdev_i.slist, &s->u.usbdevfs_sb.ilist); + list_add_tail(&inode->u.usbdev_i.dlist, &special[i].inodes); + } + lock_kernel(); + for (blist = usb_bus_list.next; blist != &usb_bus_list; blist = blist->next) { + bus = list_entry(blist, struct usb_bus, bus_list); + new_bus_inode(bus, s); + recurse_new_dev_inode(bus->root_hub, s); + } + unlock_kernel(); + return s; + + out_no_root: + printk("usbdevfs_read_super: get root inode failed\n"); + iput(root_inode); + return NULL; + + opterr: + printk(KERN_WARNING "usbdevfs: mount parameter error\n"); + return NULL; +} + +static DECLARE_FSTYPE(usbdevice_fs_type, "usbdevfs", usbdevfs_read_super, 0); + +/* --------------------------------------------------------------------- */ + +void usbdevfs_add_bus(struct usb_bus *bus) +{ + struct list_head *slist; + + lock_kernel(); + for (slist = superlist.next; slist != &superlist; slist = slist->next) + new_bus_inode(bus, list_entry(slist, struct super_block, u.usbdevfs_sb.slist)); + unlock_kernel(); + usbdevfs_conn_disc_event(); +} + +void usbdevfs_remove_bus(struct usb_bus *bus) +{ + lock_kernel(); + while (!list_empty(&bus->inodes)) + free_inode(list_entry(bus->inodes.next, struct inode, u.usbdev_i.dlist)); + unlock_kernel(); + usbdevfs_conn_disc_event(); +} + +void usbdevfs_add_device(struct usb_device *dev) +{ + struct list_head *slist; + + lock_kernel(); + for (slist = superlist.next; slist != &superlist; slist = slist->next) + new_dev_inode(dev, list_entry(slist, struct super_block, u.usbdevfs_sb.slist)); + unlock_kernel(); + usbdevfs_conn_disc_event(); +} + +void usbdevfs_remove_device(struct usb_device *dev) +{ + struct dev_state *ds; + struct siginfo sinfo; + + lock_kernel(); + while (!list_empty(&dev->inodes)) + free_inode(list_entry(dev->inodes.next, struct inode, u.usbdev_i.dlist)); + while (!list_empty(&dev->filelist)) { + ds = list_entry(dev->filelist.next, struct dev_state, list); + list_del(&ds->list); + INIT_LIST_HEAD(&ds->list); + down_write(&ds->devsem); + ds->dev = NULL; + up_write(&ds->devsem); + if (ds->discsignr) { + sinfo.si_signo = SIGPIPE; + sinfo.si_errno = EPIPE; + sinfo.si_code = SI_ASYNCIO; + sinfo.si_addr = ds->disccontext; + send_sig_info(ds->discsignr, &sinfo, ds->disctask); + } + } + unlock_kernel(); + usbdevfs_conn_disc_event(); +} + +/* --------------------------------------------------------------------- */ + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *usbdir = NULL; +#endif + +int __init usbdevfs_init(void) +{ + int ret; + + for (ret = 0; ret < NRSPECIAL; ret++) { + INIT_LIST_HEAD(&special[ret].inodes); + } + if ((ret = usb_register(&usbdevfs_driver))) + return ret; + if ((ret = register_filesystem(&usbdevice_fs_type))) + usb_deregister(&usbdevfs_driver); +#ifdef CONFIG_PROC_FS + /* create mount point for usbdevfs */ + usbdir = proc_mkdir("usb", proc_bus); +#endif + return ret; +} + +void __exit usbdevfs_cleanup(void) +{ + usb_deregister(&usbdevfs_driver); + unregister_filesystem(&usbdevice_fs_type); +#ifdef CONFIG_PROC_FS + if (usbdir) + remove_proc_entry("usb", proc_bus); +#endif +} + +#if 0 +module_init(usbdevfs_init); +module_exit(usbdevfs_cleanup); +#endif diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/input.c linux-2.2.17/drivers/usb/input.c --- linux-2.2.17-orig/drivers/usb/input.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/input.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,408 @@ +/* + * $Id: input.c,v 1.7 2000/05/28 17:31:36 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * The input layer module itself + * + * Sponsored by SuSE + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); + +EXPORT_SYMBOL(input_register_device); +EXPORT_SYMBOL(input_unregister_device); +EXPORT_SYMBOL(input_register_handler); +EXPORT_SYMBOL(input_unregister_handler); +EXPORT_SYMBOL(input_register_minor); +EXPORT_SYMBOL(input_unregister_minor); +EXPORT_SYMBOL(input_open_device); +EXPORT_SYMBOL(input_close_device); +EXPORT_SYMBOL(input_event); + +#define INPUT_MAJOR 13 +#define INPUT_DEVICES 256 + +static struct input_dev *input_dev = NULL; +static struct input_handler *input_handler = NULL; +static struct input_handler *input_table[8] = { NULL, /* ... */ }; +static devfs_handle_t input_devfs_handle = NULL; +static int input_number = 0; +static long input_devices[NBITS(INPUT_DEVICES)] = { 0, /* ... */ }; + +void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct input_handle *handle = dev->handle; + +/* + * Filter non-events, and bad input values out. + */ + + if (type > EV_MAX || !test_bit(type, dev->evbit)) + return; + + switch (type) { + + case EV_KEY: + + if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value) + return; + + if (value == 2) break; + + change_bit(code, dev->key); + + if (test_bit(EV_REP, dev->evbit) && dev->timer.function) { + if (value) { + mod_timer(&dev->timer, jiffies + dev->rep[REP_DELAY]); + dev->repeat_key = code; + break; + } + if (dev->repeat_key == code) + del_timer(&dev->timer); + } + + break; + + case EV_ABS: + + if (code > ABS_MAX || !test_bit(code, dev->absbit)) + return; + + if (dev->absfuzz[code]) { + if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) && + (value < dev->abs[code] + (dev->absfuzz[code] >> 1))) + return; + + if ((value > dev->abs[code] - dev->absfuzz[code]) && + (value < dev->abs[code] + dev->absfuzz[code])) + value = (dev->abs[code] * 3 + value) >> 2; + + if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) && + (value < dev->abs[code] + (dev->absfuzz[code] << 1))) + value = (dev->abs[code] + value) >> 1; + } + + if (dev->abs[code] == value) + return; + + dev->abs[code] = value; + break; + + case EV_REL: + + if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0)) + return; + + break; + + case EV_LED: + + if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value) + return; + + change_bit(code, dev->led); + if (dev->event) dev->event(dev, type, code, value); + + break; + + case EV_SND: + + if (code > SND_MAX || !test_bit(code, dev->sndbit) || !!test_bit(code, dev->snd) == value) + return; + + change_bit(code, dev->snd); + if (dev->event) dev->event(dev, type, code, value); + + break; + + case EV_REP: + + if (code > REP_MAX || dev->rep[code] == value) return; + + dev->rep[code] = value; + if (dev->event) dev->event(dev, type, code, value); + + break; + } +/* + * Add randomness. + */ + +#if 0 /* BUG */ + add_input_randomness(((unsigned long) dev) ^ (type << 24) ^ (code << 16) ^ value); +#endif + +/* + * Distribute the event to handler modules. + */ + + while (handle) { + if (handle->open) + handle->handler->event(handle, type, code, value); + handle = handle->dnext; + } +} + +static void input_repeat_key(unsigned long data) +{ + struct input_dev *dev = (void *) data; + input_event(dev, EV_KEY, dev->repeat_key, 2); + mod_timer(&dev->timer, jiffies + dev->rep[REP_PERIOD]); +} + +int input_open_device(struct input_handle *handle) +{ + handle->open++; + if (handle->dev->open) + return handle->dev->open(handle->dev); + return 0; +} + +void input_close_device(struct input_handle *handle) +{ + if (handle->dev->close) + handle->dev->close(handle->dev); + handle->open--; +} + +static void input_link_handle(struct input_handle *handle) +{ + handle->dnext = handle->dev->handle; + handle->hnext = handle->handler->handle; + handle->dev->handle = handle; + handle->handler->handle = handle; +} + +static void input_unlink_handle(struct input_handle *handle) +{ + struct input_handle **handleptr; + + handleptr = &handle->dev->handle; + while (*handleptr && (*handleptr != handle)) + handleptr = &((*handleptr)->dnext); + *handleptr = (*handleptr)->dnext; + + handleptr = &handle->handler->handle; + while (*handleptr && (*handleptr != handle)) + handleptr = &((*handleptr)->hnext); + *handleptr = (*handleptr)->hnext; +} + +void input_register_device(struct input_dev *dev) +{ + struct input_handler *handler = input_handler; + struct input_handle *handle; + +/* + * Initialize repeat timer to default values. + */ + + init_timer(&dev->timer); + dev->timer.data = (long) dev; + dev->timer.function = input_repeat_key; + dev->rep[REP_DELAY] = HZ/4; + dev->rep[REP_PERIOD] = HZ/33; + +/* + * Add the device. + */ + + if (input_number >= INPUT_DEVICES) { + printk(KERN_WARNING "input: ran out of input device numbers!\n"); + dev->number = input_number; + } else { + dev->number = find_first_zero_bit(input_devices, INPUT_DEVICES); + set_bit(dev->number, input_devices); + } + + dev->next = input_dev; + input_dev = dev; + input_number++; + +/* + * Notify handlers. + */ + + while (handler) { + if ((handle = handler->connect(handler, dev))) + input_link_handle(handle); + handler = handler->next; + } +} + +void input_unregister_device(struct input_dev *dev) +{ + struct input_handle *handle = dev->handle; + struct input_dev **devptr = &input_dev; + struct input_handle *dnext; + +/* + * Kill any pending repeat timers. + */ + + del_timer(&dev->timer); + +/* + * Notify handlers. + */ + + while (handle) { + dnext = handle->dnext; + input_unlink_handle(handle); + handle->handler->disconnect(handle); + handle = dnext; + } + +/* + * Remove the device. + */ + + while (*devptr && (*devptr != dev)) + devptr = &((*devptr)->next); + *devptr = (*devptr)->next; + + input_number--; + + if (dev->number < INPUT_DEVICES) + clear_bit(dev->number, input_devices); +} + +void input_register_handler(struct input_handler *handler) +{ + struct input_dev *dev = input_dev; + struct input_handle *handle; + +/* + * Add minors if needed. + */ + + if (handler->fops != NULL) + input_table[handler->minor >> 5] = handler; + +/* + * Add the handler. + */ + + handler->next = input_handler; + input_handler = handler; + +/* + * Notify it about all existing devices. + */ + + while (dev) { + if ((handle = handler->connect(handler, dev))) + input_link_handle(handle); + dev = dev->next; + } +} + +void input_unregister_handler(struct input_handler *handler) +{ + struct input_handler **handlerptr = &input_handler; + struct input_handle *handle = handler->handle; + struct input_handle *hnext; + +/* + * Tell the handler to disconnect from all devices it keeps open. + */ + + while (handle) { + hnext = handle->hnext; + input_unlink_handle(handle); + handler->disconnect(handle); + handle = hnext; + } + +/* + * Remove it. + */ + + while (*handlerptr && (*handlerptr != handler)) + handlerptr = &((*handlerptr)->next); + + *handlerptr = (*handlerptr)->next; + +/* + * Remove minors. + */ + + if (handler->fops != NULL) + input_table[handler->minor >> 5] = NULL; +} + +static int input_open_file(struct inode *inode, struct file *file) +{ + struct input_handler *handler = input_table[MINOR(inode->i_rdev) >> 5]; + + if (!handler || !handler->fops || !handler->fops->open) + return -ENODEV; + + file->f_op = handler->fops; + + return handler->fops->open(inode, file); +} + +static struct file_operations input_fops = { + open: input_open_file, +}; + +devfs_handle_t input_register_minor(char *name, int minor, int minor_base) +{ + char devfs_name[16]; + sprintf(devfs_name, name, minor); + return devfs_register(input_devfs_handle, devfs_name, 0, DEVFS_FL_DEFAULT, INPUT_MAJOR, minor + minor_base, + S_IFCHR | S_IRUGO | S_IWUSR, 0, 0, &input_fops, NULL); +} + +void input_unregister_minor(devfs_handle_t handle) +{ + devfs_unregister(handle); +} + +static int __init input_init(void) +{ + if (devfs_register_chrdev(INPUT_MAJOR, "input", &input_fops)) { + printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR); + return -EBUSY; + } + input_devfs_handle = devfs_mk_dir(NULL, "input", 5, NULL); + return 0; +} + +static void __exit input_exit(void) +{ + devfs_unregister(input_devfs_handle); + if (devfs_unregister_chrdev(INPUT_MAJOR, "input")) + printk(KERN_ERR "input: can't unregister char major %d", INPUT_MAJOR); +} + +module_init(input_init); +module_exit(input_exit); diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/joydev.c linux-2.2.17/drivers/usb/joydev.c --- linux-2.2.17-orig/drivers/usb/joydev.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/joydev.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,487 @@ +/* + * $Id: joydev.c,v 1.7 2000/05/29 09:01:52 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * Copyright (c) 1999 Colin Van Dyke + * + * Joystick device driver for the input driver suite. + * + * Sponsored by SuSE and Intel + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JOYDEV_MINOR_BASE 0 +#define JOYDEV_MINORS 32 +#define JOYDEV_BUFFER_SIZE 64 + +struct joydev { + int used; + int open; + int minor; + struct input_handle handle; + wait_queue_head_t wait; + devfs_handle_t devfs; + struct joydev *next; + struct joydev_list *list; + struct js_corr corr[ABS_MAX]; + struct JS_DATA_SAVE_TYPE glue; + int nabs; + int nkey; + __u16 keymap[KEY_MAX - BTN_MISC]; + __u16 keypam[KEY_MAX - BTN_MISC]; + __u8 absmap[ABS_MAX]; + __u8 abspam[ABS_MAX]; +}; + +struct joydev_list { + struct js_event buffer[JOYDEV_BUFFER_SIZE]; + int head; + int tail; + int startup; + struct fasync_struct *fasync; + struct joydev *joydev; + struct joydev_list *next; +}; + +static struct joydev *joydev_table[JOYDEV_MINORS]; + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_SUPPORTED_DEVICE("input/js"); + +static int joydev_correct(int value, struct js_corr *corr) +{ + switch (corr->type) { + case JS_CORR_NONE: + break; + case JS_CORR_BROKEN: + value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : + ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : + ((corr->coef[2] * (value - corr->coef[0])) >> 14); + break; + default: + return 0; + } + + if (value < -32767) return -32767; + if (value > 32767) return 32767; + + return value; +} + +static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) +{ + struct joydev *joydev = handle->private; + struct joydev_list *list = joydev->list; + struct js_event event; + + switch (type) { + + case EV_KEY: + if (code < BTN_MISC || value == 2) return; + event.type = JS_EVENT_BUTTON; + event.number = joydev->keymap[code - BTN_MISC]; + event.value = value; + break; + + case EV_ABS: + event.type = JS_EVENT_AXIS; + event.number = joydev->absmap[code]; + event.value = joydev_correct(value, &joydev->corr[event.number]); + break; + + default: + return; + } + + event.time = jiffies * (1000 / HZ); + + while (list) { + + memcpy(list->buffer + list->head, &event, sizeof(struct js_event)); + + if (list->startup == joydev->nabs + joydev->nkey) + if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1))) + list->startup = 0; + + kill_fasync(list->fasync, SIGIO); + + list = list->next; + } + + wake_up_interruptible(&joydev->wait); +} + +static int joydev_fasync(int fd, struct file *file, int on) +{ + int retval; + struct joydev_list *list = file->private_data; + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; +} + +static int joydev_release(struct inode * inode, struct file * file) +{ + struct joydev_list *list = file->private_data; + struct joydev_list **listptr = &list->joydev->list; + + joydev_fasync(-1, file, 0); + + while (*listptr && (*listptr != list)) + listptr = &((*listptr)->next); + *listptr = (*listptr)->next; + + if (!--list->joydev->open) + input_close_device(&list->joydev->handle); + + if (!--list->joydev->used) { + input_unregister_minor(list->joydev->devfs); + joydev_table[list->joydev->minor] = NULL; + kfree(list->joydev); + } + + kfree(list); + + return 0; +} + +static int joydev_open(struct inode *inode, struct file *file) +{ + struct joydev_list *list; + int i = MINOR(inode->i_rdev) - JOYDEV_MINOR_BASE; + + if (i > JOYDEV_MINORS || !joydev_table[i]) + return -ENODEV; + + if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL))) { + return -ENOMEM; + } + memset(list, 0, sizeof(struct joydev_list)); + + list->joydev = joydev_table[i]; + list->next = joydev_table[i]->list; + joydev_table[i]->list = list; + + file->private_data = list; + + list->joydev->used++; + + if (!list->joydev->open++) + input_open_device(&list->joydev->handle); + + return 0; +} + +static ssize_t joydev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct joydev_list *list = file->private_data; + struct joydev *joydev = list->joydev; + struct input_dev *input = joydev->handle.dev; + int retval = 0; + + if (count < sizeof(struct js_event)) + return -EINVAL; + + if (count == sizeof(struct JS_DATA_TYPE)) { + + struct JS_DATA_TYPE data; + + data.buttons = ((joydev->nkey > 0 && test_bit(joydev->keypam[0], input->key)) ? 1 : 0) | + ((joydev->nkey > 1 && test_bit(joydev->keypam[1], input->key)) ? 2 : 0); + data.x = ((joydev_correct(input->abs[ABS_X], &joydev->corr[0]) / 256) + 128) >> joydev->glue.JS_CORR.x; + data.y = ((joydev_correct(input->abs[ABS_Y], &joydev->corr[1]) / 256) + 128) >> joydev->glue.JS_CORR.y; + + if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) + return -EFAULT; + + list->startup = 0; + list->tail = list->head; + + return sizeof(struct JS_DATA_TYPE); + } + + if (list->head == list->tail && list->startup == joydev->nabs + joydev->nkey) { + + add_wait_queue(&list->joydev->wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + while (list->head == list->tail) { + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + schedule(); + } + + current->state = TASK_RUNNING; + remove_wait_queue(&list->joydev->wait, &wait); + } + + if (retval) + return retval; + + while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) { + + struct js_event event; + + event.time = jiffies * (1000/HZ); + + if (list->startup < joydev->nkey) { + event.type = JS_EVENT_BUTTON | JS_EVENT_INIT; + event.value = !!test_bit(joydev->keypam[list->startup], input->key); + event.number = list->startup; + } else { + event.type = JS_EVENT_AXIS | JS_EVENT_INIT; + event.value = joydev_correct(input->abs[joydev->abspam[list->startup - joydev->nkey]], + &joydev->corr[list->startup - joydev->nkey]); + event.number = list->startup - joydev->nkey; + } + + if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) + return -EFAULT; + + list->startup++; + retval += sizeof(struct js_event); + } + + while (list->head != list->tail && retval + sizeof(struct js_event) <= count) { + + if (copy_to_user(buf + retval, list->buffer + list->tail, sizeof(struct js_event))) + return -EFAULT; + + list->tail = (list->tail + 1) & (JOYDEV_BUFFER_SIZE - 1); + retval += sizeof(struct js_event); + } + + return retval; +} + +/* No kernel lock - fine */ +static unsigned int joydev_poll(struct file *file, poll_table *wait) +{ + struct joydev_list *list = file->private_data; + poll_wait(file, &list->joydev->wait, wait); + if (list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey) + return POLLIN | POLLRDNORM; + return 0; +} + +static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct joydev_list *list = file->private_data; + struct joydev *joydev = list->joydev; + struct input_dev *dev = joydev->handle.dev; + + + switch (cmd) { + + case JS_SET_CAL: + return copy_from_user(&joydev->glue.JS_CORR, (struct JS_DATA_TYPE *) arg, + sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; + case JS_GET_CAL: + return copy_to_user((struct JS_DATA_TYPE *) arg, &joydev->glue.JS_CORR, + sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; + case JS_SET_TIMEOUT: + return get_user(joydev->glue.JS_TIMEOUT, (int *) arg); + case JS_GET_TIMEOUT: + return put_user(joydev->glue.JS_TIMEOUT, (int *) arg); + case JS_SET_TIMELIMIT: + return get_user(joydev->glue.JS_TIMELIMIT, (long *) arg); + case JS_GET_TIMELIMIT: + return put_user(joydev->glue.JS_TIMELIMIT, (long *) arg); + case JS_SET_ALL: + return copy_from_user(&joydev->glue, (struct JS_DATA_SAVE_TYPE *) arg, + sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; + case JS_GET_ALL: + return copy_to_user((struct JS_DATA_SAVE_TYPE *) arg, &joydev->glue, + sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; + + case JSIOCGVERSION: + return put_user(JS_VERSION, (__u32 *) arg); + case JSIOCGAXES: + return put_user(joydev->nabs, (__u8 *) arg); + case JSIOCGBUTTONS: + return put_user(joydev->nkey, (__u8 *) arg); + case JSIOCSCORR: + return copy_from_user(joydev->corr, (struct js_corr *) arg, + sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0; + case JSIOCGCORR: + return copy_to_user((struct js_corr *) arg, joydev->corr, + sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0; + default: + if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { + int len; + if (!dev->name) return 0; + len = strlen(dev->name) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + if (copy_to_user((char *) arg, dev->name, len)) return -EFAULT; + return len; + } + } + return -EINVAL; +} + +static struct file_operations joydev_fops = { + read: joydev_read, + write: joydev_write, + poll: joydev_poll, + open: joydev_open, + release: joydev_release, + ioctl: joydev_ioctl, + fasync: joydev_fasync, +}; + +static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev) +{ + struct joydev *joydev; + int i, j, minor; + + if (!(test_bit(EV_KEY, dev->evbit) && test_bit(EV_ABS, dev->evbit) && + test_bit(ABS_X, dev->absbit) && test_bit(ABS_Y, dev->absbit) && + (test_bit(BTN_TRIGGER, dev->keybit) || test_bit(BTN_A, dev->keybit) + || test_bit(BTN_1, dev->keybit)))) return NULL; + + for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++); + if (joydev_table[minor]) { + printk(KERN_ERR "joydev: no more free joydev devices\n"); + return NULL; + } + + if (!(joydev = kmalloc(sizeof(struct joydev), GFP_KERNEL))) + return NULL; + memset(joydev, 0, sizeof(struct joydev)); + + init_waitqueue_head(&joydev->wait); + + joydev->minor = minor; + joydev_table[minor] = joydev; + + joydev->handle.dev = dev; + joydev->handle.handler = handler; + joydev->handle.private = joydev; + + joydev->used = 1; + + for (i = 0; i < ABS_MAX; i++) + if (test_bit(i, dev->absbit)) { + joydev->absmap[i] = joydev->nabs; + joydev->abspam[joydev->nabs] = i; + joydev->nabs++; + } + + for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC; i++) + if (test_bit(i + BTN_MISC, dev->keybit)) { + joydev->keymap[i] = joydev->nkey; + joydev->keypam[joydev->nkey] = i + BTN_MISC; + joydev->nkey++; + } + + for (i = 0; i < BTN_JOYSTICK - BTN_MISC; i++) + if (test_bit(i + BTN_MISC, dev->keybit)) { + joydev->keymap[i] = joydev->nkey; + joydev->keypam[joydev->nkey] = i + BTN_MISC; + joydev->nkey++; + } + + for (i = 0; i < joydev->nabs; i++) { + j = joydev->abspam[i]; + if (dev->absmax[j] == dev->absmin[j]) { + joydev->corr[i].type = JS_CORR_NONE; + continue; + } + joydev->corr[i].type = JS_CORR_BROKEN; + joydev->corr[i].prec = dev->absfuzz[j]; + joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j]; + joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j]; + joydev->corr[i].coef[2] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]); + joydev->corr[i].coef[3] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]); + } + + joydev->devfs = input_register_minor("js%d", minor, JOYDEV_MINOR_BASE); + + printk(KERN_INFO "js%d: Joystick device for input%d\n", minor, dev->number); + + return &joydev->handle; +} + +static void joydev_disconnect(struct input_handle *handle) +{ + struct joydev *joydev = handle->private; + + if (joydev->open) + input_close_device(handle); + + if (!--joydev->used) { + input_unregister_minor(joydev->devfs); + joydev_table[joydev->minor] = NULL; + kfree(joydev); + } +} + +static struct input_handler joydev_handler = { + event: joydev_event, + connect: joydev_connect, + disconnect: joydev_disconnect, + fops: &joydev_fops, + minor: JOYDEV_MINOR_BASE, +}; + +static int __init joydev_init(void) +{ + input_register_handler(&joydev_handler); + return 0; +} + +static void __exit joydev_exit(void) +{ + input_unregister_handler(&joydev_handler); +} + +module_init(joydev_init); +module_exit(joydev_exit); diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/keybdev.c linux-2.2.17/drivers/usb/keybdev.c --- linux-2.2.17-orig/drivers/usb/keybdev.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/keybdev.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,198 @@ +/* + * $Id: keybdev.c,v 1.3 2000/05/28 17:31:36 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Input driver to keyboard driver binding. + * + * Sponsored by SuSE + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__i386__) || defined(__alpha__) || defined(__mips__) + +static int x86_sysrq_alt = 0; + +static unsigned short x86_keycodes[256] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 43, 85, 86, 87, 88,115,119,120,121,375,123, 90, + 284,285,309,298,312, 91,327,328,329,331,333,335,336,337,338,339, + 367,294,293,286,350, 92,334,512,116,377,109,111,373,347,348,349, + 360, 93, 94, 95, 98,376,100,101,357,316,354,304,289,102,351,355, + 103,104,105,275,281,272,306,106,274,107,288,364,358,363,362,361, + 291,108,381,290,287,292,279,305,280, 99,112,257,258,113,270,114, + 118,117,125,374,379,259,260,261,262,263,264,265,266,267,268,269, + 271,273,276,277,278,282,283,295,296,297,299,300,301,302,303,307, + 308,310,313,314,315,317,318,319,320,321,322,323,324,325,326,330, + 332,340,341,342,343,344,345,346,356,359,365,368,369,370,371,372 }; + +static int emulate_raw(unsigned int keycode, int down) +{ + if (keycode > 255 || !x86_keycodes[keycode]) + return -1; + + if (keycode == KEY_PAUSE) { + handle_scancode(0xe1, 1); + handle_scancode(0x1d, down); + handle_scancode(0x45, down); + return 0; + } + + if (keycode == KEY_SYSRQ && x86_sysrq_alt) { + handle_scancode(0x54, down); + return 0; + } + + if (x86_keycodes[keycode] & 0x100) + handle_scancode(0xe0, 1); + + handle_scancode(x86_keycodes[keycode] & 0x7f, down); + + if (keycode == KEY_SYSRQ) { + handle_scancode(0xe0, 1); + handle_scancode(0x37, down); + } + + if (keycode == KEY_LEFTALT || keycode == KEY_RIGHTALT) + x86_sysrq_alt = down; + + return 0; +} + +#elif defined(CONFIG_MAC_KEYBOARD) + +static unsigned char mac_keycodes[128] = + { 0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48, + 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54,128, 1, + 2, 3, 5, 4, 38, 40, 37, 41, 39, 50, 56, 42, 6, 7, 8, 9, + 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96, + 97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83, + 84, 85, 82, 65, 42, 0, 10,103,111, 0, 0, 0, 0, 0, 0, 0, + 76,125, 75,105,124, 0,115, 62,116, 59, 60,119, 61,121,114,117, + 0, 0, 0, 0,127, 81, 0,113, 0, 0, 0, 0, 0, 55, 55 }; + +static int emulate_raw(unsigned int keycode, int down) +{ + if (keycode > 127 || !mac_keycodes[keycode]) + return -1; + + handle_scancode(mac_keycodes[keycode] & 0x7f, down); + + return 0; +} + +#endif + +static struct input_handler keybdev_handler; + +void keybdev_ledfunc(unsigned int led) +{ + struct input_handle *handle; + + for (handle = keybdev_handler.handle; handle; handle = handle->hnext) { + + input_event(handle->dev, EV_LED, LED_SCROLLL, !!(led & 0x01)); + input_event(handle->dev, EV_LED, LED_NUML, !!(led & 0x02)); + input_event(handle->dev, EV_LED, LED_CAPSL, !!(led & 0x04)); + + } +} + +void keybdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int down) +{ + if (type != EV_KEY) return; + + if (emulate_raw(code, down)) + printk(KERN_WARNING "keyboard.c: can't emulate rawmode for keycode %d\n", code); + + mark_bh(KEYBOARD_BH); +} + +static struct input_handle *keybdev_connect(struct input_handler *handler, struct input_dev *dev) +{ + struct input_handle *handle; + int i; + + if (!test_bit(EV_KEY, dev->evbit)) + return NULL; + + for (i = KEY_RESERVED; i < BTN_MISC; i++) + if (test_bit(i, dev->keybit)) break; + + if (i == BTN_MISC) + return NULL; + + if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL))) + return NULL; + memset(handle, 0, sizeof(struct input_handle)); + + handle->dev = dev; + handle->handler = handler; + + input_open_device(handle); + + printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number); + + return handle; +} + +static void keybdev_disconnect(struct input_handle *handle) +{ + printk(KERN_INFO "keybdev.c: Removing keyboard: input%d\n", handle->dev->number); + input_close_device(handle); + kfree(handle); +} + +static struct input_handler keybdev_handler = { + event: keybdev_event, + connect: keybdev_connect, + disconnect: keybdev_disconnect, +}; + +static int __init keybdev_init(void) +{ + input_register_handler(&keybdev_handler); + kbd_ledfunc = keybdev_ledfunc; + return 0; +} + +static void __exit keybdev_exit(void) +{ + kbd_ledfunc = NULL; + input_unregister_handler(&keybdev_handler); +} + +module_init(keybdev_init); +module_exit(keybdev_exit); diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/keyboard.c linux-2.2.17/drivers/usb/keyboard.c --- linux-2.2.17-orig/drivers/usb/keyboard.c Mon Apr 26 13:35:01 1999 +++ linux-2.2.17/drivers/usb/keyboard.c Wed Dec 31 16:00:00 1969 @@ -1,226 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "usb.h" - -#define PCKBD_PRESSED 0x00 -#define PCKBD_RELEASED 0x80 -#define PCKBD_NEEDS_E0 0x80 - -#define USBKBD_MODIFIER_BASE 120 -#define USBKBD_KEYCODE_OFFSET 2 -#define USBKBD_KEYCODE_COUNT 6 - -#define USBKBD_VALID_KEYCODE(key) ((unsigned char)(key) > 3) -#define USBKBD_FIND_KEYCODE(down, key, count) \ - ((unsigned char*) memscan((down), (key), (count)) < ((down) + (count))) - -#define USBKBD_REPEAT_DELAY (HZ / 4) -#define USBKBD_REPEAT_RATE (HZ / 20) - -struct usb_keyboard -{ - struct usb_device *dev; - unsigned long down[2]; - unsigned char repeat_key; - struct timer_list repeat_timer; - struct list_head list; -}; - -extern unsigned char usb_kbd_map[]; - -static int usb_kbd_probe(struct usb_device *dev); -static void usb_kbd_disconnect(struct usb_device *dev); -static void usb_kbd_repeat(unsigned long dummy); - -static LIST_HEAD(usb_kbd_list); - -static struct usb_driver usb_kbd_driver = -{ - "keyboard", - usb_kbd_probe, - usb_kbd_disconnect, - {NULL, NULL} -}; - - -static void -usb_kbd_handle_key(unsigned char key, int down) -{ - int scancode = (int) usb_kbd_map[key]; - if(scancode) - { - if(scancode & PCKBD_NEEDS_E0) - { - handle_scancode(0xe0, 1); - } - handle_scancode((scancode & ~PCKBD_NEEDS_E0), down); - } -} - -static void -usb_kbd_repeat(unsigned long dev_id) -{ - struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id; - - unsigned long flags; - save_flags(flags); - cli(); - - if(kbd->repeat_key) - { - usb_kbd_handle_key(kbd->repeat_key, 1); - - /* reset repeat timer */ - kbd->repeat_timer.function = usb_kbd_repeat; - kbd->repeat_timer.expires = jiffies + USBKBD_REPEAT_RATE; - kbd->repeat_timer.data = (unsigned long) kbd; - kbd->repeat_timer.prev = NULL; - kbd->repeat_timer.next = NULL; - add_timer(&kbd->repeat_timer); - } - - restore_flags(flags); -} - -static int -usb_kbd_irq(int state, void *buffer, void *dev_id) -{ - struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id; - unsigned long *down = (unsigned long*) buffer; - - if(kbd->down[0] != down[0] || kbd->down[1] != down[1]) - { - unsigned char *olddown, *newdown; - unsigned char modsdelta, key; - int i; - - /* handle modifier change */ - modsdelta = (*(unsigned char*) down ^ *(unsigned char*) kbd->down); - if(modsdelta) - { - for(i = 0; i < 8; i++) - { - if(modsdelta & 0x01) - { - int pressed = (*(unsigned char*) down >> i) & 0x01; - usb_kbd_handle_key( - i + USBKBD_MODIFIER_BASE, - pressed); - } - modsdelta >>= 1; - } - } - - olddown = (unsigned char*) kbd->down + USBKBD_KEYCODE_OFFSET; - newdown = (unsigned char*) down + USBKBD_KEYCODE_OFFSET; - - /* handle released keys */ - for(i = 0; i < USBKBD_KEYCODE_COUNT; i++) - { - key = olddown[i]; - if(USBKBD_VALID_KEYCODE(key) - && !USBKBD_FIND_KEYCODE(newdown, key, USBKBD_KEYCODE_COUNT)) - { - usb_kbd_handle_key(key, 0); - } - } - - /* handle pressed keys */ - kbd->repeat_key = 0; - for(i = 0; i < USBKBD_KEYCODE_COUNT; i++) - { - key = newdown[i]; - if(USBKBD_VALID_KEYCODE(key) - && !USBKBD_FIND_KEYCODE(olddown, key, USBKBD_KEYCODE_COUNT)) - { - usb_kbd_handle_key(key, 1); - kbd->repeat_key = key; - } - } - - /* set repeat timer if any keys were pressed */ - if(kbd->repeat_key) - { - del_timer(&kbd->repeat_timer); - kbd->repeat_timer.function = usb_kbd_repeat; - kbd->repeat_timer.expires = jiffies + USBKBD_REPEAT_DELAY; - kbd->repeat_timer.data = (unsigned long) kbd; - kbd->repeat_timer.prev = NULL; - kbd->repeat_timer.next = NULL; - add_timer(&kbd->repeat_timer); - } - - kbd->down[0] = down[0]; - kbd->down[1] = down[1]; - } - - return 1; -} - -static int -usb_kbd_probe(struct usb_device *dev) -{ - struct usb_interface_descriptor *interface; - struct usb_endpoint_descriptor *endpoint; - struct usb_keyboard *kbd; - - interface = &dev->config[0].interface[0]; - endpoint = &interface->endpoint[0]; - - if(interface->bInterfaceClass != 3 - || interface->bInterfaceSubClass != 1 - || interface->bInterfaceProtocol != 1) - { - return -1; - } - - printk(KERN_INFO "USB HID boot protocol keyboard detected.\n"); - - kbd = kmalloc(sizeof(struct usb_keyboard), GFP_KERNEL); - if(kbd) - { - memset(kbd, 0, sizeof(*kbd)); - kbd->dev = dev; - dev->private = kbd; - - usb_set_configuration(dev, dev->config[0].bConfigurationValue); - usb_set_protocol(dev, 0); - usb_set_idle(dev, 0, 0); - - usb_request_irq(dev, - usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), - usb_kbd_irq, - endpoint->bInterval, - kbd); - - list_add(&kbd->list, &usb_kbd_list); - } - - return 0; -} - -static void -usb_kbd_disconnect(struct usb_device *dev) -{ - struct usb_keyboard *kbd = (struct usb_keyboard*) dev->private; - if(kbd) - { - dev->private = NULL; - list_del(&kbd->list); - del_timer(&kbd->repeat_timer); - kfree(kbd); - } - - printk(KERN_INFO "USB HID boot protocol keyboard removed.\n"); -} - -int -usb_kbd_init(void) -{ - usb_register(&usb_kbd_driver); - return 0; -} diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/keymap.c linux-2.2.17/drivers/usb/keymap.c --- linux-2.2.17-orig/drivers/usb/keymap.c Mon Apr 26 12:19:05 1999 +++ linux-2.2.17/drivers/usb/keymap.c Wed Dec 31 16:00:00 1969 @@ -1,50 +0,0 @@ -unsigned char usb_kbd_map[256] = -{ - 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x2e, 0x20, - 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, - - 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14, - 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x02, 0x03, - - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x1c, 0x01, 0xd3, 0x0f, 0x39, 0x0c, 0x0d, 0x1a, - - 0x1b, 0x2b, 0x00, 0x27, 0x28, 0x29, 0x33, 0x34, - 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, - - 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0xb7, 0x46, - 0x00, 0xd2, 0xc7, 0xc9, 0x63, 0xcf, 0xd1, 0xcd, - - 0xcb, 0xd0, 0xc8, 0x45, 0xb5, 0x37, 0x4a, 0x4e, - 0x9c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47, - - 0x48, 0x49, 0x52, 0x53, 0x00, 0x6d, 0x00, 0x00, - 0xbd, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1d, 0x2a, 0x38, 0xdb, 0x9d, 0x36, 0xb8, 0xdc, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/maps/fixup.map linux-2.2.17/drivers/usb/maps/fixup.map --- linux-2.2.17-orig/drivers/usb/maps/fixup.map Fri Apr 16 15:38:24 1999 +++ linux-2.2.17/drivers/usb/maps/fixup.map Wed Dec 31 16:00:00 1969 @@ -1,31 +0,0 @@ -# misc fixes -keycode 0 = Pause -keycode 29 = Control -keycode 99 = Remove -keycode 42 = Shift -keycode 54 = Shift_R -keycode 109 = Application - -# E0 keys (or'ed with 0x80) -keycode 156 = KP_Enter -keycode 157 = Control_R -keycode 181 = KP_Divide -keycode 183 = Print_Screen -keycode 184 = Alt_R -keycode 189 = F13 -keycode 190 = F14 -keycode 193 = F17 -keycode 198 = Break -keycode 199 = Home -keycode 200 = Up -keycode 201 = Prior -keycode 203 = Left -keycode 205 = Right -keycode 207 = End -keycode 208 = Down -keycode 209 = Next -keycode 210 = Insert -keycode 211 = Delete -keycode 219 = Window -keycode 220 = Window_R -keycode 221 = Menu diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/maps/serial.map linux-2.2.17/drivers/usb/maps/serial.map --- linux-2.2.17-orig/drivers/usb/maps/serial.map Mon Apr 19 11:47:54 1999 +++ linux-2.2.17/drivers/usb/maps/serial.map Wed Dec 31 16:00:00 1969 @@ -1,370 +0,0 @@ -keymaps 0-2,4-6,8-9,12 -keycode 1 = Escape - alt keycode 1 = Meta_Escape - shift alt keycode 1 = Meta_Escape - control alt keycode 1 = Meta_Escape -keycode 2 = one exclam - alt keycode 2 = Meta_one - shift alt keycode 2 = Meta_exclam -keycode 3 = two at at nul nul - alt keycode 3 = Meta_two - shift alt keycode 3 = Meta_at - control alt keycode 3 = Meta_nul -keycode 4 = three numbersign - control keycode 4 = Escape - alt keycode 4 = Meta_three - shift alt keycode 4 = Meta_numbersign -keycode 5 = four dollar dollar Control_backslash - alt keycode 5 = Meta_four - shift alt keycode 5 = Meta_dollar - control alt keycode 5 = Meta_Control_backslash -keycode 6 = five percent - control keycode 6 = Control_bracketright - alt keycode 6 = Meta_five - shift alt keycode 6 = Meta_percent -keycode 7 = six asciicircum - control keycode 7 = Control_asciicircum - alt keycode 7 = Meta_six - shift alt keycode 7 = Meta_asciicircum -keycode 8 = seven ampersand braceleft Control_underscore - alt keycode 8 = Meta_seven - shift alt keycode 8 = Meta_ampersand - control alt keycode 8 = Meta_Control_underscore -keycode 9 = eight asterisk bracketleft Delete - alt keycode 9 = Meta_eight - shift alt keycode 9 = Meta_asterisk - control alt keycode 9 = Meta_Delete -keycode 10 = nine parenleft bracketright - alt keycode 10 = Meta_nine - shift alt keycode 10 = Meta_parenleft -keycode 11 = zero parenright braceright - alt keycode 11 = Meta_zero - shift alt keycode 11 = Meta_parenright -keycode 12 = minus underscore backslash Control_underscore Control_underscore - alt keycode 12 = Meta_minus - shift alt keycode 12 = Meta_underscore - control alt keycode 12 = Meta_Control_underscore -keycode 13 = equal plus - alt keycode 13 = Meta_equal - shift alt keycode 13 = Meta_plus -keycode 14 = Delete - alt keycode 14 = Meta_Delete - shift alt keycode 14 = Meta_Delete - control alt keycode 14 = Meta_Delete -keycode 15 = Tab - alt keycode 15 = Meta_Tab - shift alt keycode 15 = Meta_Tab - control alt keycode 15 = Meta_Tab -keycode 16 = q -keycode 17 = w -keycode 18 = e -keycode 19 = r -keycode 20 = t -keycode 21 = y -keycode 22 = u -keycode 23 = i -keycode 24 = o -keycode 25 = p -keycode 26 = bracketleft braceleft - control keycode 26 = Escape - alt keycode 26 = Meta_bracketleft - shift alt keycode 26 = Meta_braceleft -keycode 27 = bracketright braceright asciitilde Control_bracketright - alt keycode 27 = Meta_bracketright - shift alt keycode 27 = Meta_braceright - control alt keycode 27 = Meta_Control_bracketright -keycode 28 = Return - alt keycode 28 = Meta_Control_m -keycode 29 = Control -keycode 30 = a -keycode 31 = s -keycode 32 = d -keycode 33 = f -keycode 34 = g -keycode 35 = h -keycode 36 = j -keycode 37 = k -keycode 38 = l -keycode 39 = semicolon colon - alt keycode 39 = Meta_semicolon - shift alt keycode 39 = Meta_colon -keycode 40 = apostrophe quotedbl - control keycode 40 = Control_g - alt keycode 40 = Meta_apostrophe - shift alt keycode 40 = Meta_quotedbl -keycode 41 = grave asciitilde - control keycode 41 = nul - alt keycode 41 = Meta_grave - shift alt keycode 41 = Meta_asciitilde -keycode 42 = Shift -keycode 43 = backslash bar - control keycode 43 = Control_backslash - alt keycode 43 = Meta_backslash - shift alt keycode 43 = Meta_bar -keycode 44 = z -keycode 45 = x -keycode 46 = c -keycode 47 = v -keycode 48 = b -keycode 49 = n -keycode 50 = m -keycode 51 = comma less - alt keycode 51 = Meta_comma - shift alt keycode 51 = Meta_less -keycode 52 = period greater - alt keycode 52 = Meta_period - shift alt keycode 52 = Meta_greater -keycode 53 = slash question - control keycode 53 = Delete - alt keycode 53 = Meta_slash - shift alt keycode 53 = Meta_question -keycode 54 = Shift -keycode 55 = KP_Multiply - altgr keycode 55 = Hex_C -keycode 56 = Alt -keycode 57 = space - control keycode 57 = nul - alt keycode 57 = Meta_space - shift alt keycode 57 = Meta_space - control alt keycode 57 = Meta_nul -keycode 58 = Caps_Lock -keycode 59 = F1 F13 Console_13 F25 - alt keycode 59 = Console_1 - control alt keycode 59 = Console_1 -keycode 60 = F2 F14 Console_14 F26 - alt keycode 60 = Console_2 - control alt keycode 60 = Console_2 -keycode 61 = F3 F15 Console_15 F27 - alt keycode 61 = Console_3 - control alt keycode 61 = Console_3 -keycode 62 = F4 F16 Console_16 F28 - alt keycode 62 = Console_4 - control alt keycode 62 = Console_4 -keycode 63 = F5 F17 Console_17 F29 - alt keycode 63 = Console_5 - control alt keycode 63 = Console_5 -keycode 64 = F6 F18 Console_18 F30 - alt keycode 64 = Console_6 - control alt keycode 64 = Console_6 -keycode 65 = F7 F19 Console_19 F31 - alt keycode 65 = Console_7 - control alt keycode 65 = Console_7 -keycode 66 = F8 F20 Console_20 F32 - alt keycode 66 = Console_8 - control alt keycode 66 = Console_8 -keycode 67 = F9 F21 Console_21 F33 - alt keycode 67 = Console_9 - control alt keycode 67 = Console_9 -keycode 68 = F10 F22 Console_22 F34 - alt keycode 68 = Console_10 - control alt keycode 68 = Console_10 -keycode 69 = Num_Lock - altgr keycode 69 = Hex_E -keycode 70 = Scroll_Lock Show_Memory Show_Registers Show_State - alt keycode 70 = Scroll_Lock -keycode 71 = KP_7 - altgr keycode 71 = Hex_7 - alt keycode 71 = Ascii_7 -keycode 72 = KP_8 - altgr keycode 72 = Hex_8 - alt keycode 72 = Ascii_8 -keycode 73 = KP_9 - altgr keycode 73 = Hex_9 - alt keycode 73 = Ascii_9 -keycode 74 = KP_Subtract -keycode 75 = KP_4 - altgr keycode 75 = Hex_4 - alt keycode 75 = Ascii_4 -keycode 76 = KP_5 - altgr keycode 76 = Hex_5 - alt keycode 76 = Ascii_5 -keycode 77 = KP_6 - altgr keycode 77 = Hex_6 - alt keycode 77 = Ascii_6 -keycode 78 = KP_Add -keycode 79 = KP_1 - altgr keycode 79 = Hex_1 - alt keycode 79 = Ascii_1 -keycode 80 = KP_2 - altgr keycode 80 = Hex_2 - alt keycode 80 = Ascii_2 -keycode 81 = KP_3 - altgr keycode 81 = Hex_3 - alt keycode 81 = Ascii_3 -keycode 82 = KP_0 - altgr keycode 82 = Hex_0 - alt keycode 82 = Ascii_0 -keycode 83 = KP_Period - altgr control keycode 83 = Boot - control alt keycode 83 = Boot -keycode 84 = Last_Console -keycode 85 = -keycode 86 = less greater bar - alt keycode 86 = Meta_less - shift alt keycode 86 = Meta_greater -keycode 87 = F11 F23 Console_23 F35 - alt keycode 87 = Console_11 - control alt keycode 87 = Console_11 -keycode 88 = F12 F24 Console_24 F36 - alt keycode 88 = Console_12 - control alt keycode 88 = Console_12 -keycode 89 = -keycode 90 = -keycode 91 = -keycode 92 = -keycode 93 = -keycode 94 = -keycode 95 = -keycode 96 = KP_Enter -keycode 97 = Control -keycode 98 = KP_Divide - altgr keycode 98 = Hex_B -keycode 99 = Control_backslash - alt keycode 99 = Meta_Control_backslash - shift alt keycode 99 = Meta_Control_backslash - control alt keycode 99 = Meta_Control_backslash -keycode 100 = AltGr -keycode 101 = Break -keycode 102 = Find -keycode 103 = Up - alt keycode 103 = KeyboardSignal -keycode 104 = Prior - shift keycode 104 = Scroll_Backward -keycode 105 = Left - alt keycode 105 = Decr_Console -keycode 106 = Right - alt keycode 106 = Incr_Console -keycode 107 = Select -keycode 108 = Down -keycode 109 = Next - shift keycode 109 = Scroll_Forward -keycode 110 = Insert -keycode 111 = Remove - altgr control keycode 111 = Boot - control alt keycode 111 = Boot -keycode 112 = Macro - altgr control keycode 112 = VoidSymbol - shift alt keycode 112 = VoidSymbol -keycode 113 = F13 - altgr control keycode 113 = VoidSymbol - shift alt keycode 113 = VoidSymbol -keycode 114 = F14 - altgr control keycode 114 = VoidSymbol - shift alt keycode 114 = VoidSymbol -keycode 115 = Help - altgr control keycode 115 = VoidSymbol - shift alt keycode 115 = VoidSymbol -keycode 116 = Do - altgr control keycode 116 = VoidSymbol - shift alt keycode 116 = VoidSymbol -keycode 117 = F17 - altgr control keycode 117 = VoidSymbol - shift alt keycode 117 = VoidSymbol -keycode 118 = KP_MinPlus - altgr control keycode 118 = VoidSymbol - shift alt keycode 118 = VoidSymbol -keycode 119 = Pause -keycode 120 = -keycode 121 = -keycode 122 = -keycode 123 = -keycode 124 = -keycode 125 = -keycode 126 = -keycode 127 = -string F1 = "\033[[A" -string F2 = "\033[[B" -string F3 = "\033[[C" -string F4 = "\033[[D" -string F5 = "\033[[E" -string F6 = "\033[17~" -string F7 = "\033[18~" -string F8 = "\033[19~" -string F9 = "\033[20~" -string F10 = "\033[21~" -string F11 = "\033[23~" -string F12 = "\033[24~" -string F13 = "\033[25~" -string F14 = "\033[26~" -string F15 = "\033[28~" -string F16 = "\033[29~" -string F17 = "\033[31~" -string F18 = "\033[32~" -string F19 = "\033[33~" -string F20 = "\033[34~" -string Find = "\033[1~" -string Insert = "\033[2~" -string Remove = "\033[3~" -string Select = "\033[4~" -string Prior = "\033[5~" -string Next = "\033[6~" -string Macro = "\033[M" -string Pause = "\033[P" -compose '`' 'A' to 'À' -compose '`' 'a' to 'à' -compose '\'' 'A' to 'Á' -compose '\'' 'a' to 'á' -compose '^' 'A' to 'Â' -compose '^' 'a' to 'â' -compose '~' 'A' to 'Ã' -compose '~' 'a' to 'ã' -compose '"' 'A' to 'Ä' -compose '"' 'a' to 'ä' -compose 'O' 'A' to 'Å' -compose 'o' 'a' to 'å' -compose '0' 'A' to 'Å' -compose '0' 'a' to 'å' -compose 'A' 'A' to 'Å' -compose 'a' 'a' to 'å' -compose 'A' 'E' to 'Æ' -compose 'a' 'e' to 'æ' -compose ',' 'C' to 'Ç' -compose ',' 'c' to 'ç' -compose '`' 'E' to 'È' -compose '`' 'e' to 'è' -compose '\'' 'E' to 'É' -compose '\'' 'e' to 'é' -compose '^' 'E' to 'Ê' -compose '^' 'e' to 'ê' -compose '"' 'E' to 'Ë' -compose '"' 'e' to 'ë' -compose '`' 'I' to 'Ì' -compose '`' 'i' to 'ì' -compose '\'' 'I' to 'Í' -compose '\'' 'i' to 'í' -compose '^' 'I' to 'Î' -compose '^' 'i' to 'î' -compose '"' 'I' to 'Ï' -compose '"' 'i' to 'ï' -compose '-' 'D' to 'Ð' -compose '-' 'd' to 'ð' -compose '~' 'N' to 'Ñ' -compose '~' 'n' to 'ñ' -compose '`' 'O' to 'Ò' -compose '`' 'o' to 'ò' -compose '\'' 'O' to 'Ó' -compose '\'' 'o' to 'ó' -compose '^' 'O' to 'Ô' -compose '^' 'o' to 'ô' -compose '~' 'O' to 'Õ' -compose '~' 'o' to 'õ' -compose '"' 'O' to 'Ö' -compose '"' 'o' to 'ö' -compose '/' 'O' to 'Ø' -compose '/' 'o' to 'ø' -compose '`' 'U' to 'Ù' -compose '`' 'u' to 'ù' -compose '\'' 'U' to 'Ú' -compose '\'' 'u' to 'ú' -compose '^' 'U' to 'Û' -compose '^' 'u' to 'û' -compose '"' 'U' to 'Ü' -compose '"' 'u' to 'ü' -compose '\'' 'Y' to 'Ý' -compose '\'' 'y' to 'ý' -compose 'T' 'H' to 'Þ' -compose 't' 'h' to 'þ' -compose 's' 's' to 'ß' -compose '"' 'y' to 'ÿ' -compose 's' 'z' to 'ß' -compose 'i' 'j' to 'ÿ' diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/maps/usb.map linux-2.2.17/drivers/usb/maps/usb.map --- linux-2.2.17-orig/drivers/usb/maps/usb.map Fri Apr 16 15:38:24 1999 +++ linux-2.2.17/drivers/usb/maps/usb.map Wed Dec 31 16:00:00 1969 @@ -1,233 +0,0 @@ -# USB kernel keymap. -keymaps 0-2,4-5,8,12 - -keycode 4 = a - altgr keycode 30 = Hex_A -keycode 5 = b - altgr keycode 48 = Hex_B -keycode 6 = c - altgr keycode 46 = Hex_C -keycode 7 = d - altgr keycode 32 = Hex_D -keycode 8 = e - altgr keycode 18 = Hex_E -keycode 9 = f - altgr keycode 33 = Hex_F -keycode 10 = g -keycode 11 = h -keycode 12 = i -keycode 13 = j -keycode 14 = k -keycode 15 = l -keycode 16 = m -keycode 17 = n -keycode 18 = o -keycode 19 = p -keycode 20 = q -keycode 21 = r -keycode 22 = s -keycode 23 = t -keycode 24 = u -keycode 25 = v -keycode 26 = w -keycode 27 = x -keycode 28 = y -keycode 29 = z -keycode 30 = one exclam - alt keycode 2 = Meta_one -keycode 31 = two at - control keycode 3 = nul - shift control keycode 3 = nul - alt keycode 3 = Meta_two -keycode 32 = three numbersign - control keycode 4 = Escape - alt keycode 4 = Meta_three -keycode 33 = four dollar - control keycode 5 = Control_backslash - alt keycode 5 = Meta_four -keycode 34 = five percent - control keycode 6 = Control_bracketright - alt keycode 6 = Meta_five -keycode 35 = six asciicircum - control keycode 7 = Control_asciicircum - alt keycode 7 = Meta_six -keycode 36 = seven ampersand - control keycode 8 = Control_underscore - alt keycode 8 = Meta_seven -keycode 37 = eight asterisk - control keycode 9 = Delete - alt keycode 9 = Meta_eight -keycode 38 = nine parenleft - alt keycode 10 = Meta_nine -keycode 39 = zero parenright - alt keycode 11 = Meta_zero -keycode 40 = Return - alt keycode 28 = Meta_Control_m -keycode 41 = Escape Escape - alt keycode 1 = Meta_Escape -keycode 42 = Delete Delete - control keycode 14 = BackSpace - alt keycode 14 = Meta_Delete -keycode 43 = Tab Tab - alt keycode 15 = Meta_Tab -keycode 44 = space space - control keycode 57 = nul - alt keycode 57 = Meta_space -keycode 45 = minus underscore backslash - control keycode 12 = Control_underscore - shift control keycode 12 = Control_underscore - alt keycode 12 = Meta_minus -keycode 46 = equal plus - alt keycode 13 = Meta_equal -keycode 47 = bracketleft braceleft - control keycode 26 = Escape - alt keycode 26 = Meta_bracketleft -keycode 48 = bracketright braceright asciitilde - control keycode 27 = Control_bracketright - alt keycode 27 = Meta_bracketright -keycode 49 = backslash bar - control keycode 43 = Control_backslash - alt keycode 43 = Meta_backslash -keycode 50 = -keycode 51 = semicolon colon - alt keycode 39 = Meta_semicolon -keycode 52 = apostrophe quotedbl - control keycode 40 = Control_g - alt keycode 40 = Meta_apostrophe -keycode 53 = grave asciitilde - control keycode 41 = nul - alt keycode 41 = Meta_grave -keycode 54 = comma less - alt keycode 51 = Meta_comma -keycode 55 = period greater - control keycode 52 = Compose - alt keycode 52 = Meta_period -keycode 56 = slash question - control keycode 53 = Delete - alt keycode 53 = Meta_slash -keycode 57 = Caps_Lock -keycode 58 = F1 F11 Console_13 - control keycode 59 = F1 - alt keycode 59 = Console_1 - control alt keycode 59 = Console_1 -keycode 59 = F2 F12 Console_14 - control keycode 60 = F2 - alt keycode 60 = Console_2 - control alt keycode 60 = Console_2 -keycode 60 = F3 F13 Console_15 - control keycode 61 = F3 - alt keycode 61 = Console_3 - control alt keycode 61 = Console_3 -keycode 61 = F4 F14 Console_16 - control keycode 62 = F4 - alt keycode 62 = Console_4 - control alt keycode 62 = Console_4 -keycode 62 = F5 F15 Console_17 - control keycode 63 = F5 - alt keycode 63 = Console_5 - control alt keycode 63 = Console_5 -keycode 63 = F6 F16 Console_18 - control keycode 64 = F6 - alt keycode 64 = Console_6 - control alt keycode 64 = Console_6 -keycode 64 = F7 F17 Console_19 - control keycode 65 = F7 - alt keycode 65 = Console_7 - control alt keycode 65 = Console_7 -keycode 65 = F8 F18 Console_20 - control keycode 66 = F8 - alt keycode 66 = Console_8 - control alt keycode 66 = Console_8 -keycode 66 = F9 F19 Console_21 - control keycode 67 = F9 - alt keycode 67 = Console_9 - control alt keycode 67 = Console_9 -keycode 67 = F10 F20 Console_22 - control keycode 68 = F10 - alt keycode 68 = Console_10 - control alt keycode 68 = Console_10 -keycode 68 = F11 F11 Console_23 - control keycode 87 = F11 - alt keycode 87 = Console_11 - control alt keycode 87 = Console_11 -keycode 69 = F12 F12 Console_24 - control keycode 88 = F12 - alt keycode 88 = Console_12 - control alt keycode 88 = Console_12 -keycode 70 = Print_Screen -keycode 71 = Scroll_Lock Show_Memory Show_Registers - control keycode 70 = Show_State - alt keycode 70 = Scroll_Lock -keycode 72 = Pause -keycode 73 = Insert -keycode 74 = Home -keycode 75 = Prior - shift keycode 104 = Scroll_Backward -keycode 76 = Remove -# altgr control keycode 111 = Boot - control alt keycode 111 = Boot -keycode 77 = End -keycode 78 = Next - shift keycode 109 = Scroll_Forward -keycode 79 = Right - alt keycode 106 = Incr_Console -keycode 80 = Left - alt keycode 105 = Decr_Console -keycode 81 = Down -keycode 82 = Up -keycode 83 = Num_Lock - shift keycode 69 = Bare_Num_Lock -keycode 84 = KP_Divide -keycode 85 = KP_Multiply -keycode 86 = KP_Subtract -keycode 87 = KP_Add -keycode 88 = KP_Enter -keycode 89 = KP_1 - alt keycode 79 = Ascii_1 - altgr keycode 79 = Hex_1 -keycode 90 = KP_2 - alt keycode 80 = Ascii_2 - altgr keycode 80 = Hex_2 -keycode 91 = KP_3 - alt keycode 81 = Ascii_3 - altgr keycode 81 = Hex_3 -keycode 92 = KP_4 - alt keycode 75 = Ascii_4 - altgr keycode 75 = Hex_4 -keycode 93 = KP_5 - alt keycode 76 = Ascii_5 - altgr keycode 76 = Hex_5 -keycode 94 = KP_6 - alt keycode 77 = Ascii_6 - altgr keycode 77 = Hex_6 -keycode 95 = KP_7 - alt keycode 71 = Ascii_7 - altgr keycode 71 = Hex_7 -keycode 96 = KP_8 - alt keycode 72 = Ascii_8 - altgr keycode 72 = Hex_8 -keycode 97 = KP_9 - alt keycode 73 = Ascii_9 - altgr keycode 73 = Hex_9 -keycode 98 = KP_0 - alt keycode 82 = Ascii_0 - altgr keycode 82 = Hex_0 -keycode 99 = KP_Period -# altgr control keycode 83 = Boot - control alt keycode 83 = Boot -keycode 100 = -keycode 101 = Application -keycode 102 = -keycode 103 = -keycode 104 = F13 -keycode 105 = F14 - -# modifiers -keycode 120 = Control -keycode 121 = Shift -keycode 122 = Alt -keycode 123 = Window -keycode 124 = Control_R -keycode 125 = Shift_R -keycode 126 = Alt_R -keycode 127 = Window_R diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/mdc800.c linux-2.2.17/drivers/usb/mdc800.c --- linux-2.2.17-orig/drivers/usb/mdc800.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/mdc800.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,935 @@ +/* + * copyright (C) 1999/2000 by Henning Zabel + * + * 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. + */ + + +/* + * USB-Kernel Driver for the Mustek MDC800 Digital Camera + * (c) 1999/2000 Henning Zabel + * + * + * The driver brings the USB functions of the MDC800 to Linux. + * To use the Camera you must support the USB Protocoll of the camera + * to the Kernel Node. + * The Driver uses a misc device Node. Create it with : + * mknod /dev/mustek c 180 32 + * + * The driver supports only one camera. + * + * version 0.7.3 + * bugfix : The mdc800->state field gets set to READY after the + * the diconnect function sets it to NOT_CONNECTED. This makes the + * driver running like the camera is connected and causes some + * hang ups. + * + * version 0.7.1 + * MOD_INC and MOD_DEC are changed in usb_probe to prevent load/unload + * problems when compiled as Module. + * (04/04/2000) + * + * The mdc800 driver gets assigned the USB Minor 32-47. The Registration + * was updated to use these values. + * (26/03/2000) + * + * The Init und Exit Module Function are updated. + * (01/03/2000) + * + * version 0.7.0 + * Rewrite of the driver : The driver now uses URB's. The old stuff + * has been removed. + * + * version 0.6.0 + * Rewrite of this driver: The Emulation of the rs232 protocoll + * has been removed from the driver. A special executeCommand function + * for this driver is included to gphoto. + * The driver supports two kind of communication to bulk endpoints. + * Either with the dev->bus->ops->bulk... or with callback function. + * (09/11/1999) + * + * version 0.5.0: + * first Version that gets a version number. Most of the needed + * functions work. + * (20/10/1999) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define VERSION "0.7.3" +#define RELEASE_DATE "(24/04/2000)" + +/* Vendor and Product Information */ +#define MDC800_VENDOR_ID 0x055f +#define MDC800_PRODUCT_ID 0xa800 + +/* Timeouts (msec) */ +#define TO_DOWNLOAD_GET_READY 1500 +#define TO_DOWNLOAD_GET_BUSY 1500 +#define TO_WRITE_GET_READY 1000 +#define TO_DEFAULT_COMMAND 5000 +#define TO_READ_FROM_IRQ TO_DEFAULT_COMMAND +#define TO_GET_READY TO_DEFAULT_COMMAND + +/* Minor Number of the device (create with mknod /dev/mustek c 180 32) */ +#define MDC800_DEVICE_MINOR_BASE 32 + + +/************************************************************************** + Data and structs +***************************************************************************/ + + +typedef enum { + NOT_CONNECTED, READY, WORKING, DOWNLOAD +} mdc800_state; + + +/* Data for the driver */ +struct mdc800_data +{ + struct usb_device * dev; // Device Data + mdc800_state state; + + unsigned int endpoint [4]; + + purb_t irq_urb; + wait_queue_head_t irq_wait; + char* irq_urb_buffer; + + int camera_busy; // is camera busy ? + int camera_request_ready; // Status to synchronize with irq + char camera_response [8]; // last Bytes send after busy + + purb_t write_urb; + char* write_urb_buffer; + wait_queue_head_t write_wait; + + + purb_t download_urb; + char* download_urb_buffer; + wait_queue_head_t download_wait; + int download_left; // Bytes left to download ? + + + /* Device Data */ + char out [64]; // Answer Buffer + int out_ptr; // Index to the first not readen byte + int out_count; // Bytes in the buffer + + int open; // Camera device open ? + int rw_lock; // Block read <-> write + + char in [8]; // Command Input Buffer + int in_count; + + int pic_index; // Cache for the Imagesize (-1 for nothing cached ) + int pic_len; +}; + + +/* Specification of the Endpoints */ +static struct usb_endpoint_descriptor mdc800_ed [4] = +{ + { 0,0, 0x01, 0x02, 8, 0,0,0 }, + { 0,0, 0x82, 0x03, 8, 0,0,0 }, + { 0,0, 0x03, 0x02, 64, 0,0,0 }, + { 0,0, 0x84, 0x02, 64, 0,0,0 } +}; + + +/* The Variable used by the driver */ +static struct mdc800_data* mdc800=0; + + +/*************************************************************************** + The USB Part of the driver +****************************************************************************/ + +static int mdc800_endpoint_equals (struct usb_endpoint_descriptor *a,struct usb_endpoint_descriptor *b) +{ + return ( + ( a->bEndpointAddress == b->bEndpointAddress ) + && ( a->bmAttributes == b->bmAttributes ) + && ( a->wMaxPacketSize == b->wMaxPacketSize ) + ); +} + + +/* + * Checks wether the camera responds busy + */ +static int mdc800_isBusy (char* ch) +{ + int i=0; + while (i<8) + { + if (ch [i] != (char)0x99) + return 0; + i++; + } + return 1; +} + + +/* + * Checks wether the Camera is ready + */ +static int mdc800_isReady (char *ch) +{ + int i=0; + while (i<8) + { + if (ch [i] != (char)0xbb) + return 0; + i++; + } + return 1; +} + + + +/* + * USB IRQ Handler for InputLine + */ +static void mdc800_usb_irq (struct urb *urb) +{ + int data_received=0, wake_up; + unsigned char* b=urb->transfer_buffer; + struct mdc800_data* mdc800=urb->context; + + if (urb->status >= 0) + { + + //dbg ("%i %i %i %i %i %i %i %i \n",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]); + + if (mdc800_isBusy (b)) + { + if (!mdc800->camera_busy) + { + mdc800->camera_busy=1; + dbg ("gets busy"); + } + } + else + { + if (mdc800->camera_busy && mdc800_isReady (b)) + { + mdc800->camera_busy=0; + dbg ("gets ready"); + } + } + if (!(mdc800_isBusy (b) || mdc800_isReady (b))) + { + /* Store Data in camera_answer field */ + dbg ("%i %i %i %i %i %i %i %i ",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]); + + memcpy (mdc800->camera_response,b,8); + data_received=1; + } + } + wake_up= ( mdc800->camera_request_ready > 0 ) + && + ( + ((mdc800->camera_request_ready == 1) && (!mdc800->camera_busy)) + || + ((mdc800->camera_request_ready == 2) && data_received) + || + ((mdc800->camera_request_ready == 3) && (mdc800->camera_busy)) + || + (urb->status < 0) + ); + + if (wake_up) + { + mdc800->camera_request_ready=0; + wake_up_interruptible (&mdc800->irq_wait); + } +} + + +/* + * Waits a while until the irq responds that camera is ready + * + * mode : 0: Wait for camera gets ready + * 1: Wait for receiving data + * 2: Wait for camera gets busy + * + * msec: Time to wait + */ +static int mdc800_usb_waitForIRQ (int mode, int msec) +{ + mdc800->camera_request_ready=1+mode; + + interruptible_sleep_on_timeout (&mdc800->irq_wait, msec*HZ/1000); + + if (mdc800->camera_request_ready>0) + { + mdc800->camera_request_ready=0; + err ("timeout waiting for camera."); + return -1; + } + + if (mdc800->state == NOT_CONNECTED) + { + warn ("Camera gets disconnected during waiting for irq."); + mdc800->camera_request_ready=0; + return -2; + } + + return 0; +} + + +/* + * The write_urb callback function + */ +static void mdc800_usb_write_notify (struct urb *urb) +{ + struct mdc800_data* mdc800=urb->context; + + if (urb->status != 0) + { + err ("writing command fails (status=%i)", urb->status); + } + else + { + mdc800->state=READY; + } + wake_up_interruptible (&mdc800->write_wait); +} + + +/* + * The download_urb callback function + */ +static void mdc800_usb_download_notify (struct urb *urb) +{ + struct mdc800_data* mdc800=urb->context; + + if (urb->status == 0) + { + /* Fill output buffer with these data */ + memcpy (mdc800->out, urb->transfer_buffer, 64); + mdc800->out_count=64; + mdc800->out_ptr=0; + mdc800->download_left-=64; + if (mdc800->download_left == 0) + { + mdc800->state=READY; + } + } + else + { + err ("request bytes fails (status:%i)", urb->status); + } + wake_up_interruptible (&mdc800->download_wait); +} + + +/*************************************************************************** + Probing for the Camera + ***************************************************************************/ + +static struct usb_driver mdc800_usb_driver; + +/* + * Callback to search the Mustek MDC800 on the USB Bus + */ +static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum ) +{ + int i,j; + struct usb_interface_descriptor *intf_desc; + int irq_interval=0; + + dbg ("(mdc800_usb_probe) called."); + + + if (dev->descriptor.idVendor != MDC800_VENDOR_ID) + return 0; + if (dev->descriptor.idProduct != MDC800_PRODUCT_ID) + return 0; + + if (mdc800->dev != 0) + { + warn ("only one Mustek MDC800 is supported."); + return 0; + } + + if (dev->descriptor.bNumConfigurations != 1) + { + err ("probe fails -> wrong Number of Configuration"); + return 0; + } + intf_desc=&dev->actconfig->interface[ifnum].altsetting[0]; + + if ( + ( intf_desc->bInterfaceClass != 0xff ) + || ( intf_desc->bInterfaceSubClass != 0 ) + || ( intf_desc->bInterfaceProtocol != 0 ) + || ( intf_desc->bNumEndpoints != 4) + ) + { + err ("probe fails -> wrong Interface"); + return 0; + } + + /* Check the Endpoints */ + for (i=0; i<4; i++) + { + mdc800->endpoint[i]=-1; + for (j=0; j<4; j++) + { + if (mdc800_endpoint_equals (&intf_desc->endpoint [j],&mdc800_ed [i])) + { + mdc800->endpoint[i]=intf_desc->endpoint [j].bEndpointAddress ; + if (i==1) + { + irq_interval=intf_desc->endpoint [j].bInterval; + } + + continue; + } + } + if (mdc800->endpoint[i] == -1) + { + err ("probe fails -> Wrong Endpoints."); + return 0; + } + } + + + usb_driver_claim_interface (&mdc800_usb_driver, &dev->actconfig->interface[ifnum], mdc800); + if (usb_set_interface (dev, ifnum, 0) < 0) + { + err ("MDC800 Configuration fails."); + return 0; + } + + info ("Found Mustek MDC800 on USB."); + + mdc800->dev=dev; + mdc800->state=READY; + mdc800->open=0; + mdc800->rw_lock=0; + + /* Setup URB Structs */ + FILL_INT_URB ( + mdc800->irq_urb, + mdc800->dev, + usb_rcvintpipe (mdc800->dev,mdc800->endpoint [1]), + mdc800->irq_urb_buffer, + 8, + mdc800_usb_irq, + mdc800, + irq_interval + ); + + FILL_BULK_URB ( + mdc800->write_urb, + mdc800->dev, + usb_sndbulkpipe (mdc800->dev, mdc800->endpoint[0]), + mdc800->write_urb_buffer, + 8, + mdc800_usb_write_notify, + mdc800 + ); + + FILL_BULK_URB ( + mdc800->download_urb, + mdc800->dev, + usb_rcvbulkpipe (mdc800->dev, mdc800->endpoint [3]), + mdc800->download_urb_buffer, + 64, + mdc800_usb_download_notify, + mdc800 + ); + + return mdc800; +} + + +/* + * Disconnect USB device (maybe the MDC800) + */ +static void mdc800_usb_disconnect (struct usb_device *dev,void* ptr) +{ + struct mdc800_data* mdc800=(struct mdc800_data*) ptr; + + dbg ("(mdc800_usb_disconnect) called"); + + if (mdc800->state == NOT_CONNECTED) + return; + + mdc800->state=NOT_CONNECTED; + + usb_unlink_urb (mdc800->irq_urb); + usb_unlink_urb (mdc800->write_urb); + usb_unlink_urb (mdc800->download_urb); + + usb_driver_release_interface (&mdc800_usb_driver, &dev->actconfig->interface[1]); + + mdc800->dev=0; + info ("Mustek MDC800 disconnected from USB."); +} + + +/*************************************************************************** + The Misc device Part (file_operations) +****************************************************************************/ + +/* + * This Function calc the Answersize for a command. + */ +static int mdc800_getAnswerSize (char command) +{ + switch ((unsigned char) command) + { + case 0x2a: + case 0x49: + case 0x51: + case 0x0d: + case 0x20: + case 0x07: + case 0x01: + case 0x25: + case 0x00: + return 8; + + case 0x05: + case 0x3e: + return mdc800->pic_len; + + case 0x09: + return 4096; + + default: + return 0; + } +} + + +/* + * Init the device: (1) alloc mem (2) Increase MOD Count .. + */ +static int mdc800_device_open (struct inode* inode, struct file *file) +{ + int retval=0; + + if (mdc800->state == NOT_CONNECTED) + { + return -EBUSY; + } + + if (mdc800->open) + { + return -EBUSY; + } + + mdc800->rw_lock=0; + mdc800->in_count=0; + mdc800->out_count=0; + mdc800->out_ptr=0; + mdc800->pic_index=0; + mdc800->pic_len=-1; + mdc800->download_left=0; + + mdc800->camera_busy=0; + mdc800->camera_request_ready=0; + + retval=0; + if (usb_submit_urb (mdc800->irq_urb)) + { + err ("request USB irq fails (submit_retval=%i urb_status=%i).",retval, mdc800->irq_urb->status); + return -EIO; + } + + mdc800->open=1; + + dbg ("Mustek MDC800 device opened."); + return 0; +} + + +/* + * Close the Camera and release Memory + */ +static int mdc800_device_release (struct inode* inode, struct file *file) +{ + int retval=0; + dbg ("Mustek MDC800 device closed."); + + if (mdc800->open && (mdc800->state != NOT_CONNECTED)) + { + mdc800->open=0; + usb_unlink_urb (mdc800->irq_urb); + usb_unlink_urb (mdc800->write_urb); + usb_unlink_urb (mdc800->download_urb); + } + else + { + retval=-EIO; + } + + return retval; +} + + +/* + * The Device read callback Function + */ +static ssize_t mdc800_device_read (struct file *file, char *buf, size_t len, loff_t *pos) +{ + int left=len, sts=len; /* single transfer size */ + char* ptr=buf; + + if (mdc800->state == NOT_CONNECTED) + return -EBUSY; + + if (mdc800->state == WORKING) + { + warn ("Illegal State \"working\" reached during read ?!"); + return -EBUSY; + } + + if (!mdc800->open || mdc800->rw_lock) + return -EBUSY; + mdc800->rw_lock=1; + + while (left) + { + if (signal_pending (current)) { + mdc800->rw_lock=0; + return -EINTR; + } + + sts=left > (mdc800->out_count-mdc800->out_ptr)?mdc800->out_count-mdc800->out_ptr:left; + + if (sts <= 0) + { + /* Too less Data in buffer */ + if (mdc800->state == DOWNLOAD) + { + mdc800->out_count=0; + mdc800->out_ptr=0; + + /* Download -> Request new bytes */ + if (usb_submit_urb (mdc800->download_urb)) + { + err ("Can't submit download urb (status=%i)",mdc800->download_urb->status); + mdc800->rw_lock=0; + return len-left; + } + interruptible_sleep_on_timeout (&mdc800->download_wait, TO_DOWNLOAD_GET_READY*HZ/1000); + if (mdc800->download_urb->status != 0) + { + err ("request download-bytes fails (status=%i)",mdc800->download_urb->status); + mdc800->rw_lock=0; + return len-left; + } + } + else + { + /* No more bytes -> that's an error*/ + mdc800->rw_lock=0; + return -EIO; + } + } + else + { + /* memcpy Bytes */ + memcpy (ptr, &mdc800->out [mdc800->out_ptr], sts); + ptr+=sts; + left-=sts; + mdc800->out_ptr+=sts; + } + } + + mdc800->rw_lock=0; + return len-left; +} + + +/* + * The Device write callback Function + * If a 8Byte Command is received, it will be send to the camera. + * After this the driver initiates the request for the answer or + * just waits until the camera becomes ready. + */ +static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t len, loff_t *pos) +{ + int i=0; + + if (mdc800->state != READY) + return -EBUSY; + + if (!mdc800->open || mdc800->rw_lock) + return -EBUSY; + mdc800->rw_lock=1; + + while (irw_lock=0; + return -EINTR; + } + + /* check for command start */ + if (buf [i] == (char) 0x55) + { + mdc800->in_count=0; + mdc800->out_count=0; + mdc800->out_ptr=0; + mdc800->download_left=0; + } + + /* save command byte */ + if (mdc800->in_count < 8) + { + mdc800->in[mdc800->in_count]=buf[i]; + mdc800->in_count++; + } + else + { + err ("Command is to long !\n"); + mdc800->rw_lock=0; + return -EIO; + } + + /* Command Buffer full ? -> send it to camera */ + if (mdc800->in_count == 8) + { + int answersize; + + if (mdc800_usb_waitForIRQ (0,TO_GET_READY)) + { + err ("Camera didn't get ready.\n"); + mdc800->rw_lock=0; + return -EIO; + } + + answersize=mdc800_getAnswerSize (mdc800->in[1]); + + mdc800->state=WORKING; + memcpy (mdc800->write_urb->transfer_buffer, mdc800->in,8); + if (usb_submit_urb (mdc800->write_urb)) + { + err ("submitting write urb fails (status=%i)", mdc800->write_urb->status); + mdc800->rw_lock=0; + return -EIO; + } + interruptible_sleep_on_timeout (&mdc800->write_wait, TO_WRITE_GET_READY*HZ/1000); + if (mdc800->state == WORKING) + { + usb_unlink_urb (mdc800->write_urb); + mdc800->rw_lock=0; + return -EIO; + } + + switch ((unsigned char) mdc800->in[1]) + { + case 0x05: /* Download Image */ + case 0x3e: /* Take shot in Fine Mode (WCam Mode) */ + if (mdc800->pic_len < 0) + { + err ("call 0x07 before 0x05,0x3e"); + mdc800->state=READY; + mdc800->rw_lock=0; + return -EIO; + } + mdc800->pic_len=-1; + + case 0x09: /* Download Thumbnail */ + mdc800->download_left=answersize+64; + mdc800->state=DOWNLOAD; + mdc800_usb_waitForIRQ (0,TO_DOWNLOAD_GET_BUSY); + break; + + + default: + if (answersize) + { + + if (mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ)) + { + err ("requesting answer from irq fails"); + mdc800->rw_lock=0; + return -EIO; + } + + /* Write dummy data, (this is ugly but part of the USB Protokoll */ + /* if you use endpoint 1 as bulk and not as irq */ + memcpy (mdc800->out, mdc800->camera_response,8); + + /* This is the interpreted answer */ + memcpy (&mdc800->out[8], mdc800->camera_response,8); + + mdc800->out_ptr=0; + mdc800->out_count=16; + + /* Cache the Imagesize, if command was getImageSize */ + if (mdc800->in [1] == (char) 0x07) + { + mdc800->pic_len=(int) 65536*(unsigned char) mdc800->camera_response[0]+256*(unsigned char) mdc800->camera_response[1]+(unsigned char) mdc800->camera_response[2]; + + dbg ("cached imagesize = %i",mdc800->pic_len); + } + + } + else + { + if (mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND)) + { + err ("Command Timeout."); + mdc800->rw_lock=0; + return -EIO; + } + } + mdc800->state=READY; + break; + } + } + i++; + } + mdc800->rw_lock=0; + return i; +} + + +/*************************************************************************** + Init and Cleanup this driver (Structs and types) +****************************************************************************/ + +/* File Operations of this drivers */ +static struct file_operations mdc800_device_ops = +{ + read: mdc800_device_read, + write: mdc800_device_write, + open: mdc800_device_open, + release: mdc800_device_release, +}; + + + +/* + * USB Driver Struct for this device + */ +static struct usb_driver mdc800_usb_driver = +{ + "mdc800", + mdc800_usb_probe, + mdc800_usb_disconnect, + { 0,0 }, + &mdc800_device_ops, + MDC800_DEVICE_MINOR_BASE +}; + + + +/************************************************************************ + Init and Cleanup this driver (Main Functions) +*************************************************************************/ + +#define try(A) if ((A) == 0) goto cleanup_on_fail; +#define try_free_mem(A) if (A != 0) { kfree (A); A=0; } +#define try_free_urb(A) if (A != 0) { usb_free_urb (A); A=0; } + +int __init usb_mdc800_init (void) +{ + /* Allocate Memory */ + try (mdc800=kmalloc (sizeof (struct mdc800_data), GFP_KERNEL)); + + mdc800->dev=0; + mdc800->open=0; + mdc800->state=NOT_CONNECTED; + memset(mdc800, 0, sizeof(struct mdc800_data)); + + init_waitqueue_head (&mdc800->irq_wait); + init_waitqueue_head (&mdc800->write_wait); + init_waitqueue_head (&mdc800->download_wait); + + try (mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL)); + try (mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL)); + try (mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL)); + + try (mdc800->irq_urb=usb_alloc_urb (0)); + try (mdc800->download_urb=usb_alloc_urb (0)); + try (mdc800->write_urb=usb_alloc_urb (0)); + + /* Register the driver */ + if (usb_register (&mdc800_usb_driver) < 0) + goto cleanup_on_fail; + + info ("Mustek Digital Camera Driver " VERSION " (MDC800)"); + info (RELEASE_DATE " Henning Zabel "); + + return 0; + + /* Clean driver up, when something fails */ + +cleanup_on_fail: + + if (mdc800 != 0) + { + err ("can't alloc memory!"); + + try_free_mem (mdc800->download_urb_buffer); + try_free_mem (mdc800->write_urb_buffer); + try_free_mem (mdc800->irq_urb_buffer); + + try_free_urb (mdc800->write_urb); + try_free_urb (mdc800->download_urb); + try_free_urb (mdc800->irq_urb); + + kfree (mdc800); + } + mdc800=0; + return -1; +} + + +void __exit usb_mdc800_cleanup (void) +{ + usb_deregister (&mdc800_usb_driver); + + usb_free_urb (mdc800->irq_urb); + usb_free_urb (mdc800->download_urb); + usb_free_urb (mdc800->write_urb); + + kfree (mdc800->irq_urb_buffer); + kfree (mdc800->write_urb_buffer); + kfree (mdc800->download_urb_buffer); + + kfree (mdc800); + mdc800=0; +} + + +MODULE_AUTHOR ("Henning Zabel "); +MODULE_DESCRIPTION ("USB Driver for Mustek MDC800 Digital Camera"); + +module_init (usb_mdc800_init); +module_exit (usb_mdc800_cleanup); diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/microtek.c linux-2.2.17/drivers/usb/microtek.c --- linux-2.2.17-orig/drivers/usb/microtek.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/microtek.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,1055 @@ +/* Driver for Microtek Scanmaker X6 USB scanner, and possibly others. + * + * (C) Copyright 2000 John Fremlin + * (C) Copyright 2000 Oliver Neukum + * + * Parts shamelessly stolen from usb-storage and copyright by their + * authors. Thanks to Matt Dharm for giving us permission! + * + * This driver implements a SCSI host controller driver and a USB + * device driver. To avoid confusion, all the USB related stuff is + * prefixed by mts_usb_ and all the SCSI stuff by mts_scsi_. + * + * Microtek (www.microtek.com) did not release the specifications for + * their USB protocol to us, so we had to reverse engineer them. We + * don't know for which models they are valid. + * + * The X6 USB has three bulk endpoints, one output (0x1) down which + * commands and outgoing data are sent, and two input: 0x82 from which + * normal data is read from the scanner (in packets of maximum 32 + * bytes) and from which the status byte is read, and 0x83 from which + * the results of a scan (or preview) are read in up to 64 * 1024 byte + * chunks by the Windows driver. We don't know how much it is possible + * to read at a time from 0x83. + * + * It seems possible to read (with URB transfers) everything from 0x82 + * in one go, without bothering to read in 32 byte chunks. + * + * There seems to be an optimisation of a further READ implicit if + * you simply read from 0x83. + * + * Guessed protocol: + * + * Send raw SCSI command to EP 0x1 + * + * If there is data to receive: + * If the command was READ datatype=image: + * Read a lot of data from EP 0x83 + * Else: + * Read data from EP 0x82 + * Else: + * If there is data to transmit: + * Write it to EP 0x1 + * + * Read status byte from EP 0x82 + * + * References: + * + * The SCSI command set for the scanner is available from + * ftp://ftp.microtek.com/microtek/devpack/ + * + * Microtek NV sent us a more up to date version of the document. If + * you want it, just send mail. + * + * Status: + * + * This driver does not work properly yet. + * Untested with multiple scanners. + * Untested on SMP. + * Untested on UHCI. + * + * History: + * + * 20000417 starting history + * 20000417 fixed load oops + * 20000417 fixed unload oops + * 20000419 fixed READ IMAGE detection + * 20000424 started conversion to use URBs + * 20000502 handled short transfers as errors + * 20000513 rename and organisation of functions (john) + * 20000513 added IDs for all products supported by Windows driver (john) + * 20000514 Rewrote mts_scsi_queuecommand to use URBs (john) + * 20000514 Version 0.0.8j + * 20000514 Fix reporting of non-existant devices to SCSI layer (john) + * 20000514 Added MTS_DEBUG_INT (john) + * 20000514 Changed "usb-microtek" to "microtek" for consistency (john) + * 20000514 Stupid bug fixes (john) + * 20000514 Version 0.0.9j + * 20000515 Put transfer context and URB in mts_desc (john) + * 20000515 Added prelim turn off debugging support (john) + * 20000515 Version 0.0.10j + * 20000515 Fixed up URB allocation (clear URB on alloc) (john) + * 20000515 Version 0.0.11j + * 20000516 Removed unnecessary spinlock in mts_transfer_context (john) + * 20000516 Removed unnecessary up on instance lock in mts_remove_nolock (john) + * 20000516 Implemented (badly) scsi_abort (john) + * 20000516 Version 0.0.12j + * 20000517 Hopefully removed mts_remove_nolock quasideadlock (john) + * 20000517 Added mts_debug_dump to print ll USB info (john) + * 20000518 Tweaks and documentation updates (john) + * 20000518 Version 0.0.13j + * 20000518 Cleaned up abort handling (john) + * 20000523 Removed scsi_command and various scsi_..._resets (john) + * 20000523 Added unlink URB on scsi_abort, now OHCI supports it (john) + * 20000523 Fixed last tiresome compile warning (john) + * 20000523 Version 0.0.14j (though version 0.1 has come out?) + * 20000602 Added primitive reset + * 20000602 Version 0.2.0 + * 20000603 various cosmetic changes + * 20000603 Version 0.2.1 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../scsi/scsi.h" +#include "../scsi/hosts.h" +#include "../scsi/sd.h" + +#include "microtek.h" + +/* Constants */ + +#define MTS_ABORT_TIMEOUT HZ /*jiffies*/ + + +/* Should we do debugging? */ + +#define MTS_DO_DEBUG + + +/* USB layer driver interface */ + +static void *mts_usb_probe(struct usb_device *dev, unsigned int interface); +static void mts_usb_disconnect(struct usb_device *dev, void *ptr); + +static struct usb_driver mts_usb_driver = { + "microtek", + mts_usb_probe, + mts_usb_disconnect, + { NULL, NULL } /* we need no fops. let gcc fill it */ +}; + + +/* Internal driver stuff */ + +#define MTS_VERSION "0.2.1" +#define MTS_NAME "microtek usb (rev " MTS_VERSION "): " + +#define MTS_WARNING(x...) \ + printk( KERN_WARNING MTS_NAME ## x ) +#define MTS_ERROR(x...) \ + printk( KERN_ERR MTS_NAME ## x ) +#define MTS_INT_ERROR(x...) \ + MTS_ERROR( ## x ) +#define MTS_MESSAGE(x...) \ + printk( KERN_INFO MTS_NAME ## x ) + +#if defined MTS_DO_DEBUG + +#define MTS_DEBUG(x...) \ + printk( KERN_DEBUG MTS_NAME ## x ) + +#define MTS_DEBUG_GOT_HERE() \ + MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __PRETTY_FUNCTION__ ) +#define MTS_DEBUG_INT() \ + do { MTS_DEBUG_GOT_HERE(); \ + MTS_DEBUG("transfer = %x context = %x\n",(int)transfer,(int)context ); \ + MTS_DEBUG("status = %x data-length = %x sent = %x\n",(int)transfer->status,(int)context->data_length, (int)transfer->actual_length ); \ + mts_debug_dump(context->instance);\ + } while(0) +#else + +#define MTS_NUL_STATEMENT do { } while(0) + +#define MTS_DEBUG(x...) MTS_NUL_STATEMENT +#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT +#define MTS_DEBUG_INT() MTS_NUL_STATEMENT + +#endif + + + +#define MTS_INT_INIT()\ + do {\ + context = (struct mts_transfer_context*)transfer->context; \ + if (atomic_read(&context->do_abort)) {\ + mts_transfer_cleanup(transfer);\ + return;\ + }\ + MTS_DEBUG_INT();\ + } while (0) + +static inline void mts_debug_dump(struct mts_desc* desc) { + MTS_DEBUG("desc at 0x%x: halted = %x%x, toggle = %x%x\n", + (int)desc,(int)desc->usb_dev->halted[1],(int)desc->usb_dev->halted[0], + (int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0] + ); + MTS_DEBUG("ep_out=%x ep_response=%x ep_image=%x\n", + usb_sndbulkpipe(desc->usb_dev,desc->ep_out), + usb_rcvbulkpipe(desc->usb_dev,desc->ep_response), + usb_rcvbulkpipe(desc->usb_dev,desc->ep_image) + ); +} + + +static inline void mts_show_command(Scsi_Cmnd *srb) +{ + char *what = NULL; + + switch (srb->cmnd[0]) { + case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break; + case REZERO_UNIT: what = "REZERO_UNIT"; break; + case REQUEST_SENSE: what = "REQUEST_SENSE"; break; + case FORMAT_UNIT: what = "FORMAT_UNIT"; break; + case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break; + case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break; + case READ_6: what = "READ_6"; break; + case WRITE_6: what = "WRITE_6"; break; + case SEEK_6: what = "SEEK_6"; break; + case READ_REVERSE: what = "READ_REVERSE"; break; + case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break; + case SPACE: what = "SPACE"; break; + case INQUIRY: what = "INQUIRY"; break; + case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break; + case MODE_SELECT: what = "MODE_SELECT"; break; + case RESERVE: what = "RESERVE"; break; + case RELEASE: what = "RELEASE"; break; + case COPY: what = "COPY"; break; + case ERASE: what = "ERASE"; break; + case MODE_SENSE: what = "MODE_SENSE"; break; + case START_STOP: what = "START_STOP"; break; + case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break; + case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break; + case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break; + case SET_WINDOW: what = "SET_WINDOW"; break; + case READ_CAPACITY: what = "READ_CAPACITY"; break; + case READ_10: what = "READ_10"; break; + case WRITE_10: what = "WRITE_10"; break; + case SEEK_10: what = "SEEK_10"; break; + case WRITE_VERIFY: what = "WRITE_VERIFY"; break; + case VERIFY: what = "VERIFY"; break; + case SEARCH_HIGH: what = "SEARCH_HIGH"; break; + case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break; + case SEARCH_LOW: what = "SEARCH_LOW"; break; + case SET_LIMITS: what = "SET_LIMITS"; break; + case READ_POSITION: what = "READ_POSITION"; break; + case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break; + case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break; + case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break; + case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break; + case COMPARE: what = "COMPARE"; break; + case COPY_VERIFY: what = "COPY_VERIFY"; break; + case WRITE_BUFFER: what = "WRITE_BUFFER"; break; + case READ_BUFFER: what = "READ_BUFFER"; break; + case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break; + case READ_LONG: what = "READ_LONG"; break; + case WRITE_LONG: what = "WRITE_LONG"; break; + case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break; + case WRITE_SAME: what = "WRITE_SAME"; break; + case READ_TOC: what = "READ_TOC"; break; + case LOG_SELECT: what = "LOG_SELECT"; break; + case LOG_SENSE: what = "LOG_SENSE"; break; + case MODE_SELECT_10: what = "MODE_SELECT_10"; break; + case MODE_SENSE_10: what = "MODE_SENSE_10"; break; + case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break; + case READ_12: what = "READ_12"; break; + case WRITE_12: what = "WRITE_12"; break; + case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break; + case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break; + case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break; + case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break; + case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break; + case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break; + case WRITE_LONG_2: what = "WRITE_LONG_2"; break; + default: + MTS_DEBUG("can't decode command\n"); + goto out; + break; + } + MTS_DEBUG( "Command %s (%d bytes)\n", what, srb->cmd_len); + + out: + MTS_DEBUG( " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5], + srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]); +} + +static inline int mts_is_aborting(struct mts_desc* desc) { + return (atomic_read(&desc->context.do_abort)); +} + +static inline void mts_request_abort(struct mts_desc* desc) { + MTS_DEBUG_GOT_HERE(); + mts_debug_dump(desc); + atomic_set(&desc->context.do_abort,1); +} + +static inline void mts_urb_abort(struct mts_desc* desc) { + MTS_DEBUG_GOT_HERE(); + mts_debug_dump(desc); + if ( desc->urb.status == USB_ST_URB_PENDING ) { + usb_unlink_urb( &desc->urb ); + } +} + +static inline void mts_wait_abort(struct mts_desc* desc) +{ + mts_request_abort(desc); + + while( !atomic_read(&desc->lock.count) ) { +/* Is there a function to check if the semaphore is locked? */ + schedule_timeout( MTS_ABORT_TIMEOUT ); + MTS_DEBUG_GOT_HERE(); + mts_urb_abort(desc); + } + +} + + +static struct mts_desc * mts_list; /* list of active scanners */ +struct semaphore mts_list_semaphore; + +/* Internal list operations */ + +static +void mts_remove_nolock( struct mts_desc* to_remove ) +{ + MTS_DEBUG( "removing 0x%x from list\n", + (int)to_remove ); + + mts_wait_abort(to_remove); + + down( &to_remove->lock ); + + MTS_DEBUG_GOT_HERE(); + + if ( to_remove != mts_list ) { + MTS_DEBUG_GOT_HERE(); + if (to_remove->prev && to_remove->next) + to_remove->prev->next = to_remove->next; + } else { + MTS_DEBUG_GOT_HERE(); + mts_list = to_remove->next; + if (mts_list) { + MTS_DEBUG_GOT_HERE(); + mts_list->prev = 0; + } + } + + if ( to_remove->next ) { + MTS_DEBUG_GOT_HERE(); + to_remove->next->prev = to_remove->prev; + } + + MTS_DEBUG_GOT_HERE(); + scsi_unregister_module(MODULE_SCSI_HA, &(to_remove->ctempl)); + + kfree( to_remove ); +} + +static +void mts_add_nolock( struct mts_desc* to_add ) +{ + MTS_DEBUG( "adding 0x%x to list\n", (int)to_add ); + + to_add->prev = 0; + to_add->next = mts_list; + if ( mts_list ) { + mts_list->prev = to_add; + } + + mts_list = to_add; +} + + + + +/* SCSI driver interface */ + +/* scsi related functions - dummies for now mostly */ + +static int mts_scsi_release(struct Scsi_Host *psh) +{ + MTS_DEBUG_GOT_HERE(); + + return 0; +} + +static int mts_scsi_abort (Scsi_Cmnd *srb) +/* interrupt context (!) */ +{ + struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]); + + MTS_DEBUG_GOT_HERE(); + + mts_request_abort(desc); + mts_urb_abort(desc); + + return SCSI_ABORT_PENDING; +} + +static int mts_scsi_host_reset (Scsi_Cmnd *srb) +{ + struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]); + + MTS_DEBUG_GOT_HERE(); + mts_debug_dump(desc); + + usb_reset_device(desc->usb_dev); + return 0; /* RANT why here 0 and not SUCCESS */ +} + +/* the core of the scsi part */ + +/* faking a detection - which can't fail :-) */ + +static int mts_scsi_detect (struct SHT * sht) +{ + /* Whole function stolen from usb-storage */ + + struct mts_desc * desc = (struct mts_desc *)sht->proc_dir; + /* What a hideous hack! */ + + char local_name[48]; + + MTS_DEBUG_GOT_HERE(); + + /* set up the name of our subdirectory under /proc/scsi/ */ + sprintf(local_name, "microtek-%d", desc->host_number); + sht->name = kmalloc (strlen(local_name) + 1, GFP_KERNEL); + /* FIXME: where is this freed ? */ + + if (!sht->name) { + MTS_ERROR( "unable to allocate memory for proc interface!!\n" ); + return 0; + } + + strcpy((char *)sht->name, local_name); + + sht->proc_dir = NULL; + + /* In host->hostdata we store a pointer to desc */ + desc->host = scsi_register(sht, sizeof(desc)); + desc->host->hostdata[0] = (unsigned long)desc; +/* FIXME: what if sizeof(void*) != sizeof(unsigned long)? */ + + return 1; +} + + + +/* Main entrypoint: SCSI commands are dispatched to here */ + + + +static +int mts_scsi_queuecommand (Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback ); + +static void mts_transfer_cleanup( struct urb *transfer ); + + +inline static +void mts_int_submit_urb (struct urb* transfer, + int pipe, + void* data, + unsigned length, + mts_usb_urb_callback callback ) +/* Interrupt context! */ + +/* Holding transfer->context->lock! */ +{ + int res; + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + FILL_BULK_URB(transfer, + context->instance->usb_dev, + pipe, + data, + length, + callback, + context + ); + +/* transfer->transfer_flags = USB_DISABLE_SPD;*/ + transfer->transfer_flags = USB_ASYNC_UNLINK; + transfer->status = 0; + transfer->timeout = 100; + + res = usb_submit_urb( transfer ); + if ( res ) { + MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res ); + context->srb->result = DID_ERROR << 16; + context->state = mts_con_error; + mts_transfer_cleanup(transfer); + } + return; +} + + +static void mts_transfer_cleanup( struct urb *transfer ) +/* Interrupt context! */ +{ + struct mts_transfer_context* context = (struct mts_transfer_context*)transfer->context; + + up( &context->instance->lock ); + if ( context->final_callback ) + context->final_callback(context->srb); + +} + +static void mts_transfer_done( struct urb *transfer ) +{ + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + context->srb->result &= MTS_MAX_CHUNK_MASK; + context->srb->result |= (unsigned)context->status<<1; + + mts_transfer_cleanup(transfer); + + return; +} + + +static void mts_get_status( struct urb *transfer ) +/* Interrupt context! */ +{ + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + context->state = mts_con_status; + + mts_int_submit_urb(transfer, + usb_rcvbulkpipe(context->instance->usb_dev, + context->instance->ep_response), + &context->status, + 1, + mts_transfer_done ); + + + return; +} + +static void mts_data_done( struct urb* transfer ) +/* Interrupt context! */ +{ + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + if ( context->data_length != transfer->actual_length ) { +#if 0 + context->srb->resid = context->data_length - transfer->actual_length; +#else + printk(KERN_WARNING "microtek.c: Don't know how to handle resid on 2.2 kernels\n"); +#endif + } else if ( transfer->status ) { + context->srb->result = DID_ERROR<<16; + } + + mts_get_status(transfer); + + return; +} + + +static void mts_command_done( struct urb *transfer ) +/* Interrupt context! */ +{ + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + if ( transfer->status ) { + context->srb->result = DID_ERROR<<16; + mts_transfer_cleanup(transfer); + + return; + } + + if ( context->data ) { + context->state = mts_con_data; + mts_int_submit_urb(transfer, + context->data_pipe, + context->data, + context->data_length, + mts_data_done); + } else mts_get_status(transfer); + + return; +} + + + + static const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 }; + static const u8 mts_read_image_sig_len = 4; + static const unsigned char mts_direction[256/8] = { + 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, + 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + +#define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1) + +static void +mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc ) +{ + + int pipe; + + + MTS_DEBUG_GOT_HERE(); + + desc->context.instance = desc; + desc->context.srb = srb; + desc->context.state = mts_con_command; + atomic_set(&desc->context.do_abort,0); + + if ( !srb->bufflen ){ + desc->context.data = 0; + desc->context.data_length = 0; + return; + } else { + desc->context.data = srb->buffer; + desc->context.data_length = srb->bufflen; + } + + /* can't rely on srb->sc_data_direction */ + + /* Brutally ripped from usb-storage */ + + if ( !memcmp( srb->cmnd, mts_read_image_sig, mts_read_image_sig_len ) +) { pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_image); + MTS_DEBUG( "transfering from desc->ep_image == %d\n", + (int)desc->ep_image ); + } else if ( MTS_DIRECTION_IS_IN(srb->cmnd[0]) ) { + pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_response); + MTS_DEBUG( "transfering from desc->ep_response == %d\n", + (int)desc->ep_response); + } else { + MTS_DEBUG("transfering to desc->ep_out == %d\n", + (int)desc->ep_out); + pipe = usb_sndbulkpipe(desc->usb_dev,desc->ep_out); + } + desc->context.data_pipe = pipe; +} + + +static +int mts_scsi_queuecommand( Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback ) +{ + struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]); + int err = 0; + int res; + + MTS_DEBUG_GOT_HERE(); + mts_show_command(srb); + mts_debug_dump(desc); + + if ( srb->device->lun || srb->device->id || srb->device->channel ) { + + MTS_DEBUG("Command to LUN=%d ID=%d CHANNEL=%d from SCSI layer\n",(int)srb->device->lun,(int)srb->device->id, (int)srb->device->channel ); + + MTS_DEBUG("this device doesn't exist\n"); + + srb->result = DID_BAD_TARGET << 16; + + if(callback) + callback(srb); + + goto out; + } + + down(&desc->lock); + + MTS_DEBUG_GOT_HERE(); + mts_show_command(srb); + + + FILL_BULK_URB(&desc->urb, + desc->usb_dev, + usb_sndbulkpipe(desc->usb_dev,desc->ep_out), + srb->cmnd, + srb->cmd_len, + mts_command_done, + &desc->context + ); + + + mts_build_transfer_context( srb, desc ); + desc->context.final_callback = callback; + desc->urb.timeout = 100; + desc->urb.transfer_flags = USB_ASYNC_UNLINK; + +/* desc->urb.transfer_flags = USB_DISABLE_SPD;*/ + + res=usb_submit_urb(&desc->urb); + + if(res){ + MTS_ERROR("error %d submitting URB\n",(int)res); + srb->result = DID_ERROR << 16; + + if(callback) + callback(srb); + + goto out; + } + + MTS_DEBUG_GOT_HERE(); + + out: + return err; +} +/* + * this defines our 'host' + */ + +/* NOTE: This is taken from usb-storage, should be right. */ + + +static Scsi_Host_Template mts_scsi_host_template = { + name: "microtek", + detect: mts_scsi_detect, + release: mts_scsi_release, + command: 0, + queuecommand: mts_scsi_queuecommand, + + eh_abort_handler: mts_scsi_abort, + eh_device_reset_handler:0, + eh_bus_reset_handler: 0, + eh_host_reset_handler: mts_scsi_host_reset, + + can_queue: 1, + this_id: -1, + cmd_per_lun: 1, + present: 0, + unchecked_isa_dma: FALSE, + use_clustering: FALSE, + use_new_eh_code: TRUE, + emulated: TRUE +}; + + +/* USB layer driver interface implementation */ + +static void mts_usb_disconnect (struct usb_device *dev, void *ptr) +{ + struct mts_desc* to_remove = (struct mts_desc*)ptr; + + MTS_DEBUG_GOT_HERE(); + + /* leave the list - lock it */ + down(&mts_list_semaphore); + + mts_remove_nolock(to_remove); + + up(&mts_list_semaphore); +} + +struct vendor_product +{ + u16 idVendor; + u16 idProduct; + char* name; + enum + { + mts_sup_unknown=0, + mts_sup_alpha, + mts_sup_full + } + support_status; +} ; + + +/* These are taken from the msmUSB.inf file on the Windows driver CD */ +const static struct vendor_product mts_supported_products[] = +{ + { + 0x4ce, 0x300,"Phantom 336CX",mts_sup_unknown + }, + { + 0x5da, 0x94,"Phantom 336CX",mts_sup_unknown + }, + { + 0x5da, 0x99,"Scanmaker X6",mts_sup_alpha + }, + { + 0x5da, 0x9a,"Phantom C6",mts_sup_unknown + }, + { + 0x5da, 0xa0,"Phantom 336CX",mts_sup_unknown + }, + { + 0x5da, 0xa3,"ScanMaker V6USL",mts_sup_unknown + }, + { + 0x5da, 0x80a3,"ScanMaker V6USL",mts_sup_unknown + }, + { + 0x5da, 0x80ac,"Scanmaker V6UL",mts_sup_unknown + } +} +; +const static struct vendor_product* mts_last_product = &mts_supported_products[ sizeof(mts_supported_products) / sizeof(struct vendor_product) ]; + /* Must never be derefed, points to one after last entry */ + + +static void * mts_usb_probe (struct usb_device *dev, unsigned int interface) +{ + int i; + int result; + int ep_out = -1; + int ep_in_set[3]; /* this will break if we have more than three endpoints + which is why we check */ + int *ep_in_current = ep_in_set; + + struct mts_desc * new_desc; + struct vendor_product const* p; + + /* the altsettting 0 on the interface we're probing */ + struct usb_interface_descriptor *altsetting; + + MTS_DEBUG_GOT_HERE(); + MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev ); + + MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n", + (int)dev->descriptor.idProduct, + (int)dev->descriptor.idVendor ); + + MTS_DEBUG_GOT_HERE(); + + /* checking IDs */ + for( p = mts_supported_products; p != mts_last_product; p++ ) + if ( dev->descriptor.idVendor == p->idVendor && + dev->descriptor.idProduct == p->idProduct ) + goto is_supported; + else + MTS_DEBUG( "doesn't appear to be model %s\n", p->name ); + + MTS_DEBUG( "returning NULL: unsupported\n" ); + + return NULL; + + is_supported: + + MTS_DEBUG_GOT_HERE(); + + MTS_DEBUG( "found model %s\n", p->name ); + if ( p->support_status != mts_sup_full ) + MTS_MESSAGE( "model %s is not known to be fully supported, reports welcome!\n", + p->name ); + + /* the altsettting 0 on the interface we're probing */ + altsetting = + &(dev->actconfig->interface[interface].altsetting[0]); + + + /* Check if the config is sane */ + + if ( altsetting->bNumEndpoints != MTS_EP_TOTAL ) { + MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n", + (int)MTS_EP_TOTAL, (int)altsetting->bNumEndpoints ); + return NULL; + } + + for( i = 0; i < altsetting->bNumEndpoints; i++ ) { + if ((altsetting->endpoint[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) { + + MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n", + (int)altsetting->endpoint[i].bEndpointAddress ); + } else { + if (altsetting->endpoint[i].bEndpointAddress & + USB_DIR_IN) + *ep_in_current++ + = altsetting->endpoint[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + else { + if ( ep_out != -1 ) { + MTS_WARNING( "can only deal with one output endpoints. Bailing out." ); + return NULL; + } + + ep_out = altsetting->endpoint[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + } + } + + } + + + if ( ep_out == -1 ) { + MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" ); + return NULL; + } + + + /* I don't understand the following fully (it's from usb-storage) -- John */ + + /* set the interface -- STALL is an acceptable response here */ + result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0); + + MTS_DEBUG("usb_set_interface returned %d.\n",result); + switch( result ) + { + case 0: /* no error */ + break; + + case -EPIPE: + usb_clear_halt(dev, usb_sndctrlpipe(dev, 0)); + MTS_DEBUG( "clearing clearing stall on control interface\n" ); + break; + + default: + MTS_DEBUG( "unknown error %d from usb_set_interface\n", + (int)result ); + return NULL; + } + + + /* allocating a new descriptor */ + new_desc = (struct mts_desc *)kmalloc(sizeof(struct mts_desc), GFP_KERNEL); + if (new_desc == NULL) + { + MTS_ERROR("couldn't allocate scanner desc, bailing out!\n"); + return NULL; + } + + /* As done by usb_alloc_urb */ + memset( new_desc, 0, sizeof(*new_desc) ); + spin_lock_init(&new_desc->urb.lock); + + + /* initialising that descriptor */ + new_desc->usb_dev = dev; + new_desc->interface = interface; + + init_MUTEX(&new_desc->lock); + + if(mts_list){ + new_desc->host_number = mts_list->host_number+1; + } else { + new_desc->host_number = 0; + } + + /* endpoints */ + + new_desc->ep_out = ep_out; + new_desc->ep_response = ep_in_set[0]; + new_desc->ep_image = ep_in_set[1]; + + + if ( new_desc->ep_out != MTS_EP_OUT ) + MTS_WARNING( "will this work? Command EP is not usually %d\n", + (int)new_desc->ep_out ); + + if ( new_desc->ep_response != MTS_EP_RESPONSE ) + MTS_WARNING( "will this work? Response EP is not usually %d\n", + (int)new_desc->ep_response ); + + if ( new_desc->ep_image != MTS_EP_IMAGE ) + MTS_WARNING( "will this work? Image data EP is not usually %d\n", + (int)new_desc->ep_image ); + + + /* Initialize the host template based on the default one */ + memcpy(&(new_desc->ctempl), &mts_scsi_host_template, sizeof(mts_scsi_host_template)); + /* HACK from usb-storage - this is needed for scsi detection */ + (struct mts_desc *)new_desc->ctempl.proc_dir = new_desc; /* FIXME */ + + MTS_DEBUG("registering SCSI module\n"); + + new_desc->ctempl.module = THIS_MODULE; + result = scsi_register_module(MODULE_SCSI_HA, &(new_desc->ctempl)); + /* Will get hit back in microtek_detect by this func */ + if ( result ) + { + MTS_ERROR( "error %d from scsi_register_module! Help!\n", + (int)result ); + + /* FIXME: need more cleanup? */ + kfree( new_desc ); + return NULL; + } + MTS_DEBUG_GOT_HERE(); + + /* FIXME: the bomb is armed, must the host be registered under lock ? */ + /* join the list - lock it */ + down(&mts_list_semaphore); + + mts_add_nolock( new_desc ); + + up(&mts_list_semaphore); + + + MTS_DEBUG("completed probe and exiting happily\n"); + + return (void *)new_desc; +} + + + +/* get us noticed by the rest of the kernel */ + +int __init microtek_drv_init(void) +{ + int result; + + MTS_DEBUG_GOT_HERE(); + init_MUTEX(&mts_list_semaphore); + + if ((result = usb_register(&mts_usb_driver)) < 0) { + MTS_DEBUG("usb_register returned %d\n", result ); + return -1; + } else { + MTS_DEBUG("driver registered.\n"); + } + + return 0; +} + +void __exit microtek_drv_exit(void) +{ + struct mts_desc* next; + + MTS_DEBUG_GOT_HERE(); + + usb_deregister(&mts_usb_driver); + + down(&mts_list_semaphore); + + while (mts_list) { + /* keep track of where the next one is */ + next = mts_list->next; + + mts_remove_nolock( mts_list ); + + /* advance the list pointer */ + mts_list = next; + } + + up(&mts_list_semaphore); +} + +module_init(microtek_drv_init); +module_exit(microtek_drv_exit); diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/microtek.h linux-2.2.17/drivers/usb/microtek.h --- linux-2.2.17-orig/drivers/usb/microtek.h Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/microtek.h Sun Sep 24 04:26:23 2000 @@ -0,0 +1,73 @@ + /* + * Driver for Microtek Scanmaker X6 USB scanner and possibly others. + * + * (C) Copyright 2000 John Fremlin + * (C) Copyright 2000 Oliver Neukum + * + * See microtek.c for history + * + */ + +typedef void (*mts_scsi_cmnd_callback)(Scsi_Cmnd *); +typedef void (*mts_usb_urb_callback) (struct urb *); + + +struct mts_transfer_context +{ + struct mts_desc* instance; + mts_scsi_cmnd_callback final_callback; + Scsi_Cmnd *srb; + + void* data; + unsigned data_length; + int data_pipe; + + enum { + mts_con_none, + mts_con_command, + mts_con_data, + mts_con_status, + mts_con_error, + mts_con_done + } + state; + + atomic_t do_abort; /* when != 0 URB completion routines will + return straightaway */ + + u8 status; /* status returned from ep_response after command completion */ +}; + + +struct mts_desc { + struct mts_desc *next; + struct mts_desc *prev; + + struct usb_device *usb_dev; + + int interface; + + /* Endpoint addresses */ + u8 ep_out; + u8 ep_response; + u8 ep_image; + + struct Scsi_Host * host; + Scsi_Host_Template ctempl; + int host_number; + + struct semaphore lock; + + struct urb urb; + struct mts_transfer_context context; +}; + + +#define MTS_EP_OUT 0x1 +#define MTS_EP_RESPONSE 0x2 +#define MTS_EP_IMAGE 0x3 +#define MTS_EP_TOTAL 0x3 + +#define MTS_MAX_CHUNK_MASK ~0x3fu +/*maximum amount the scanner will transmit at once */ + diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/mkmap linux-2.2.17/drivers/usb/mkmap --- linux-2.2.17-orig/drivers/usb/mkmap Mon Apr 19 11:53:27 1999 +++ linux-2.2.17/drivers/usb/mkmap Wed Dec 31 16:00:00 1969 @@ -1,83 +0,0 @@ -#!/usr/bin/perl - -($ME = $0) =~ s|.*/||; - -$file = "maps/serial.map"; -$line = 1; -open(PC, $file) || die("$!"); -while() -{ - if(/^\s*keycode\s+(\d+)\s*=\s*(\S+)/) - { - my($idx) = int($1); - my($sym) = $2; - if(defined($map{uc($sym)})) - { - # print STDERR "$file:$line: warning: `$sym' redefined\n"; - } - $map{uc($sym)} = $idx; - } - $line++; -} -close(PC); - -$file = "maps/fixup.map"; -$line = 1; -open(FIXUP, $file) || die("$!"); -while() -{ - if(/^\s*keycode\s+(\d+)\s*=\s*/) - { - my($idx) = int($1); - for $sym (split(/\s+/, $')) - { - $map{uc($sym)} = $idx; - } - } - $line++; -} -close(FIXUP); - -$file = "maps/usb.map"; -$line = 1; -open(USB, $file) || die("$!"); -while() -{ - if(/^\s*keycode\s+(\d+)\s*=\s*/) - { - my($idx) = int($1); - for $sym (split(/\s+/, $')) - { - my($val) = $map{uc($sym)}; - $map[$idx] = $val; - if(!defined($val)) - { - print STDERR "$file:$line: warning: `$sym' undefined\n"; - } - else - { - last; - } - } - } - $line++; -} -close(USB); - -print "unsigned char usb_kbd_map[256] = \n{\n"; -for($x = 0; $x < 32; $x++) -{ - if($x && !($x % 2)) - { - print "\n"; - } - print " "; - for($y = 0; $y < 8; $y++) - { - my($idx) = $x * 8 + $y; - print sprintf(" 0x%02x,", - int(defined($map[$idx]) ? $map[$idx]:0)); - } - print "\n"; -} -print "};\n"; diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/mouse.c linux-2.2.17/drivers/usb/mouse.c --- linux-2.2.17-orig/drivers/usb/mouse.c Fri Apr 30 08:20:49 1999 +++ linux-2.2.17/drivers/usb/mouse.c Wed Dec 31 16:00:00 1969 @@ -1,293 +0,0 @@ -/* - * USB HID boot protocol mouse support based on MS BusMouse driver, psaux - * driver, and Linus's skeleton USB mouse driver. Fixed up a lot by Linus. - * - * Brad Keryan 4/3/1999 - * - * version 0.20: Linus rewrote read_mouse() to do PS/2 and do it - * correctly. Events are added together, not queued, to keep the rodent sober. - * - * version 0.02: Hmm, the mouse seems drunk because I'm queueing the events. - * This is wrong: when an application (like X or gpm) reads the mouse device, - * it wants to find out the mouse's current position, not its recent history. - * The button thing turned out to be UHCI not flipping data toggle, so half the - * packets were thrown out. - * - * version 0.01: Switched over to busmouse protocol, and changed the minor - * number to 32 (same as uusbd's hidbp driver). Buttons work more sanely now, - * but it still doesn't generate button events unless you move the mouse. - * - * version 0.0: Driver emulates a PS/2 mouse, stealing /dev/psaux (sorry, I - * know that's not very nice). Moving in the X and Y axes works. Buttons don't - * work right yet: X sees a lot of MotionNotify/ButtonPress/ButtonRelease - * combos when you hold down a button and drag the mouse around. Probably has - * some additional bugs on an SMP machine. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "usb.h" - -#define USB_MOUSE_MINOR 32 - -struct mouse_state { - unsigned char buttons; /* current button state */ - long dx; /* dx, dy, dz are change since last read */ - long dy; - long dz; - int present; /* this mouse is plugged in */ - int active; /* someone is has this mouse's device open */ - int ready; /* the mouse has changed state since the last read */ - struct wait_queue *wait; /* for polling */ - struct fasync_struct *fasync; - /* later, add a list here to support multiple mice */ - /* but we will also need a list of file pointers to identify it */ -}; - -static struct mouse_state static_mouse_state; - -spinlock_t usb_mouse_lock = SPIN_LOCK_UNLOCKED; - -static int mouse_irq(int state, void *__buffer, void *dev_id) -{ - signed char *data = __buffer; - /* finding the mouse is easy when there's only one */ - struct mouse_state *mouse = &static_mouse_state; - - /* if a mouse moves with no one listening, do we care? no */ - if(!mouse->active) - return 1; - - /* if the USB mouse sends an interrupt, then something noteworthy - must have happened */ - mouse->buttons = data[0] & 0x07; - mouse->dx += data[1]; /* data[] is signed, so this works */ - mouse->dy -= data[2]; /* y-axis is reversed */ - mouse->dz += data[3]; - mouse->ready = 1; - - add_mouse_randomness((mouse->buttons << 24) + (mouse->dz << 16 ) + - (mouse->dy << 8) + mouse->dx); - - wake_up_interruptible(&mouse->wait); - if (mouse->fasync) - kill_fasync(mouse->fasync, SIGIO); - - return 1; -} - -static int fasync_mouse(int fd, struct file *filp, int on) -{ - int retval; - struct mouse_state *mouse = &static_mouse_state; - - retval = fasync_helper(fd, filp, on, &mouse->fasync); - if (retval < 0) - return retval; - return 0; -} - -static int release_mouse(struct inode * inode, struct file * file) -{ - struct mouse_state *mouse = &static_mouse_state; - - fasync_mouse(-1, file, 0); - if (--mouse->active) - return 0; - return 0; -} - -static int open_mouse(struct inode * inode, struct file * file) -{ - struct mouse_state *mouse = &static_mouse_state; - - if (!mouse->present) - return -EINVAL; - if (mouse->active++) - return 0; - /* flush state */ - mouse->buttons = mouse->dx = mouse->dy = mouse->dz = 0; - return 0; -} - -static ssize_t write_mouse(struct file * file, - const char * buffer, size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -/* - * Look like a PS/2 mouse, please.. - * - * The PS/2 protocol is fairly strange, but - * oh, well, it's at least common.. - */ -static ssize_t read_mouse(struct file * file, char * buffer, size_t count, loff_t *ppos) -{ - int retval = 0; - static int state = 0; - struct mouse_state *mouse = &static_mouse_state; - - if (count) { - mouse->ready = 0; - switch (state) { - case 0: { /* buttons and sign */ - int buttons = mouse->buttons; - mouse->buttons = 0; - if (mouse->dx < 0) - buttons |= 0x10; - if (mouse->dy < 0) - buttons |= 0x20; - put_user(buttons, buffer); - buffer++; - retval++; - state = 1; - if (!--count) - break; - } - case 1: { /* dx */ - int dx = mouse->dx; - mouse->dx = 0; - put_user(dx, buffer); - buffer++; - retval++; - state = 2; - if (!--count) - break; - } - case 2: { /* dy */ - int dy = mouse->dy; - mouse->dy = 0; - put_user(dy, buffer); - buffer++; - retval++; - state = 0; - } - break; - } - } - return retval; -} - -static unsigned int mouse_poll(struct file *file, poll_table * wait) -{ - struct mouse_state *mouse = &static_mouse_state; - - poll_wait(file, &mouse->wait, wait); - if (mouse->ready) - return POLLIN | POLLRDNORM; - return 0; -} - -struct file_operations usb_mouse_fops = { - NULL, /* mouse_seek */ - read_mouse, - write_mouse, - NULL, /* mouse_readdir */ - mouse_poll, /* mouse_poll */ - NULL, /* mouse_ioctl */ - NULL, /* mouse_mmap */ - open_mouse, - NULL, /* flush */ - release_mouse, - NULL, - fasync_mouse, -}; - -static struct miscdevice usb_mouse = { - USB_MOUSE_MINOR, "USB mouse", &usb_mouse_fops -}; - -static int mouse_probe(struct usb_device *dev) -{ - struct usb_interface_descriptor *interface; - struct usb_endpoint_descriptor *endpoint; - struct mouse_state *mouse = &static_mouse_state; - - /* We don't handle multi-config mice */ - if (dev->descriptor.bNumConfigurations != 1) - return -1; - - /* We don't handle multi-interface mice */ - if (dev->config[0].bNumInterfaces != 1) - return -1; - - /* Is it a mouse interface? */ - interface = &dev->config[0].interface[0]; - if (interface->bInterfaceClass != 3) - return -1; - if (interface->bInterfaceSubClass != 1) - return -1; - if (interface->bInterfaceProtocol != 2) - return -1; - - /* Multiple endpoints? What kind of mutant ninja-mouse is this? */ - if (interface->bNumEndpoints != 1) - return -1; - - endpoint = &interface->endpoint[0]; - - /* Output endpoint? Curiousier and curiousier.. */ - if (!(endpoint->bEndpointAddress & 0x80)) - return -1; - - /* If it's not an interrupt endpoint, we'd better punt! */ - if ((endpoint->bmAttributes & 3) != 3) - return -1; - - printk("USB mouse found\n"); - - usb_set_configuration(dev, dev->config[0].bConfigurationValue); - - usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), mouse_irq, endpoint->bInterval, NULL); - - mouse->present = 1; - return 0; -} - -static void mouse_disconnect(struct usb_device *dev) -{ - struct mouse_state *mouse = &static_mouse_state; - - /* this might need work */ - mouse->present = 0; -} - -static struct usb_driver mouse_driver = { - "mouse", - mouse_probe, - mouse_disconnect, - { NULL, NULL } -}; - -int usb_mouse_init(void) -{ - struct mouse_state *mouse = &static_mouse_state; - - misc_register(&usb_mouse); - - mouse->present = mouse->active = 0; - mouse->wait = NULL; - mouse->fasync = NULL; - - usb_register(&mouse_driver); - printk(KERN_INFO "USB HID boot protocol mouse registered.\n"); - return 0; -} - -void usb_mouse_cleanup(void) -{ - /* this, too, probably needs work */ - usb_deregister(&mouse_driver); - misc_deregister(&usb_mouse); -} diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/mousedev.c linux-2.2.17/drivers/usb/mousedev.c --- linux-2.2.17-orig/drivers/usb/mousedev.c Wed Dec 31 16:00:00 1969 +++ linux-2.2.17/drivers/usb/mousedev.c Sun Sep 24 04:26:23 2000 @@ -0,0 +1,468 @@ +/* + * $Id: mousedev.c,v 1.8 2000/05/28 17:31:36 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Input driver to PS/2 or ImPS/2 device driver module. + * + * Sponsored by SuSE + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#define MOUSEDEV_MINOR_BASE 32 +#define MOUSEDEV_MINORS 32 +#define MOUSEDEV_MIX 31 + +#include +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X +#define CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024 +#endif +#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y +#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768 +#endif + +struct mousedev { + int used; + int open; + int minor; + wait_queue_head_t wait; + struct mousedev_list *list; + struct input_handle handle; + devfs_handle_t devfs; +}; + +struct mousedev_list { + struct fasync_struct *fasync; + struct mousedev *mousedev; + struct mousedev_list *next; + int dx, dy, dz, oldx, oldy; + char ps2[6]; + unsigned long buttons; + unsigned char ready, buffer, bufsiz; + unsigned char mode, genseq, impseq; +}; + +#define MOUSEDEV_GENIUS_LEN 5 +#define MOUSEDEV_IMPS_LEN 6 + +static unsigned char mousedev_genius_seq[] = { 0xe8, 3, 0xe6, 0xe6, 0xe6 }; +static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 }; + +static struct input_handler mousedev_handler; + +static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; +static struct mousedev mousedev_mix; + +static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) +{ + struct mousedev *mousedevs[3] = { handle->private, &mousedev_mix, NULL }; + struct mousedev **mousedev = mousedevs; + struct mousedev_list *list; + int index, size; + + while (*mousedev) { + list = (*mousedev)->list; + while (list) { + switch (type) { + case EV_ABS: + switch (code) { + case ABS_X: + size = handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X]; + list->dx += (value * CONFIG_INPUT_MOUSEDEV_SCREEN_X - list->oldx) / size; + list->oldx += list->dx * size; + break; + case ABS_Y: + size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y]; + list->dy -= (value * CONFIG_INPUT_MOUSEDEV_SCREEN_Y - list->oldy) / size; + list->oldy -= list->dy * size; + break; + } + break; + case EV_REL: + switch (code) { + case REL_X: list->dx += value; break; + case REL_Y: list->dy -= value; break; + case REL_WHEEL: if (list->mode) list->dz -= value; break; + } + break; + + case EV_KEY: + switch (code) { + case BTN_0: + case BTN_TOUCH: + case BTN_LEFT: index = 0; break; + case BTN_4: + case BTN_EXTRA: if (list->mode > 1) { index = 4; break; } + case BTN_STYLUS: + case BTN_1: + case BTN_RIGHT: index = 1; break; + case BTN_3: + case BTN_SIDE: if (list->mode > 1) { index = 3; break; } + case BTN_2: + case BTN_STYLUS2: + case BTN_MIDDLE: index = 2; break; + default: return; + } + switch (value) { + case 0: clear_bit(index, &list->buttons); break; + case 1: set_bit(index, &list->buttons); break; + case 2: return; + } + break; + } + + list->ready = 1; + + kill_fasync(list->fasync, SIGIO); + + list = list->next; + } + + wake_up_interruptible(&((*mousedev)->wait)); + mousedev++; + } +} + +static int mousedev_fasync(int fd, struct file *file, int on) +{ + int retval; + struct mousedev_list *list = file->private_data; + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; +} + +static int mousedev_release(struct inode * inode, struct file * file) +{ + struct mousedev_list *list = file->private_data; + struct mousedev_list **listptr = &list->mousedev->list; + + mousedev_fasync(-1, file, 0); + + while (*listptr && (*listptr != list)) + listptr = &((*listptr)->next); + *listptr = (*listptr)->next; + + if (!--list->mousedev->open) { + if (list->mousedev->minor == MOUSEDEV_MIX) { + struct input_handle *handle = mousedev_handler.handle; + while (handle) { + struct mousedev *mousedev = handle->private; + if (!mousedev->open) + input_close_device(handle); + handle = handle->hnext; + } + } else { + if (!mousedev_mix.open) + input_close_device(&list->mousedev->handle); + } + } + + if (!--list->mousedev->used) { + input_unregister_minor(list->mousedev->devfs); + mousedev_table[list->mousedev->minor] = NULL; + kfree(list->mousedev); + } + + kfree(list); + + return 0; +} + +static int mousedev_open(struct inode * inode, struct file * file) +{ + struct mousedev_list *list; + int i = MINOR(inode->i_rdev) - MOUSEDEV_MINOR_BASE; + + if (i > MOUSEDEV_MINORS || !mousedev_table[i]) + return -ENODEV; + + if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL))) + return -ENOMEM; + memset(list, 0, sizeof(struct mousedev_list)); + + list->mousedev = mousedev_table[i]; + list->next = mousedev_table[i]->list; + mousedev_table[i]->list = list; + file->private_data = list; + + list->mousedev->used++; + + if (!list->mousedev->open++) { + if (list->mousedev->minor == MOUSEDEV_MIX) { + struct input_handle *handle = mousedev_handler.handle; + while (handle) { + struct mousedev *mousedev = handle->private; + if (!mousedev->open) + input_open_device(handle); + handle = handle->hnext; + } + } else { + if (!mousedev_mix.open) + input_open_device(&list->mousedev->handle); + } + } + + return 0; +} + +static void mousedev_packet(struct mousedev_list *list, unsigned char off) +{ + list->ps2[off] = 0x08 | ((list->dx < 0) << 4) | ((list->dy < 0) << 5) | (list->buttons & 0x07); + list->ps2[off + 1] = (list->dx > 127 ? 127 : (list->dx < -127 ? -127 : list->dx)); + list->ps2[off + 2] = (list->dy > 127 ? 127 : (list->dy < -127 ? -127 : list->dy)); + list->dx -= list->ps2[off + 1]; + list->dy -= list->ps2[off + 2]; + list->bufsiz = off + 3; + + if (list->mode > 1) + list->ps2[off] |= ((list->buttons & 0x30) << 2); + + if (list->mode) { + list->ps2[off + 3] = (list->dz > 127 ? 127 : (list->dz < -127 ? -127 : list->dz)); + list->bufsiz++; + list->dz -= list->ps2[off + 3]; + } + if (!list->dx && !list->dy && (!list->mode || !list->dz)) list->ready = 0; + list->buffer = list->bufsiz; +} + + +static ssize_t mousedev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) +{ + struct mousedev_list *list = file->private_data; + unsigned char c; + int i; + + for (i = 0; i < count; i++) { + + c = buffer[i]; + + if (c == mousedev_genius_seq[list->genseq]) { + if (++list->genseq == MOUSEDEV_GENIUS_LEN) { + list->genseq = 0; + list->ready = 1; + list->mode = 2; + } + } else list->genseq = 0; + + if (c == mousedev_imps_seq[list->impseq]) { + if (++list->impseq == MOUSEDEV_IMPS_LEN) { + list->impseq = 0; + list->ready = 1; + list->mode = 1; + } + } else list->impseq = 0; + + list->ps2[0] = 0xfa; + list->bufsiz = 1; + + switch (c) { + + case 0xeb: /* Poll */ + mousedev_packet(list, 1); + break; + + case 0xf2: /* Get ID */ + list->ps2[1] = (list->mode == 1) ? 3 : 0; + list->bufsiz = 2; + break; + + case 0xe9: /* Get info */ + if (list->mode == 2) { + list->ps2[1] = 0x00; list->ps2[2] = 0x33; list->ps2[3] = 0x55; + } else { + list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200; + } + list->bufsiz = 4; + break; + } + + list->buffer = list->bufsiz; + } + + kill_fasync(list->fasync, SIGIO); + + wake_up_interruptible(&list->mousedev->wait); + + return count; +} + +static ssize_t mousedev_read(struct file * file, char * buffer, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct mousedev_list *list = file->private_data; + int retval = 0; + + if (!list->ready && !list->buffer) { + + add_wait_queue(&list->mousedev->wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + while (!list->ready) { + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + schedule(); + } + + current->state = TASK_RUNNING; + remove_wait_queue(&list->mousedev->wait, &wait); + } + + if (retval) + return retval; + + if (!list->buffer) + mousedev_packet(list, 0); + + if (count > list->buffer) + count = list->buffer; + + if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer, count)) + return -EFAULT; + + list->buffer -= count; + + return count; +} + +/* No kernel lock - fine */ +static unsigned int mousedev_poll(struct file *file, poll_table *wait) +{ + struct mousedev_list *list = file->private_data; + poll_wait(file, &list->mousedev->wait, wait); + if (list->ready || list->buffer) + return POLLIN | POLLRDNORM; + return 0; +} + +struct file_operations mousedev_fops = { + read: mousedev_read, + write: mousedev_write, + poll: mousedev_poll, + open: mousedev_open, + release: mousedev_release, + fasync: mousedev_fasync, +}; + +static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev) +{ + struct mousedev *mousedev; + int minor = 0; + + if (!test_bit(EV_KEY, dev->evbit) || + (!test_bit(BTN_LEFT, dev->keybit) && !test_bit(BTN_TOUCH, dev->keybit))) + return NULL; + + if ((!test_bit(EV_REL, dev->evbit) || !test_bit(REL_X, dev->relbit)) && + (!test_bit(EV_ABS, dev->evbit) || !test_bit(ABS_X, dev->absbit))) + return NULL; + + for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++); + if (mousedev_table[minor]) { + printk(KERN_ERR "mousedev: no more free mousedev devices\n"); + return NULL; + } + + if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL))) + return NULL; + memset(mousedev, 0, sizeof(struct mousedev)); + init_waitqueue_head(&mousedev->wait); + + mousedev->used = 1; + mousedev->minor = minor; + mousedev_table[minor] = mousedev; + + mousedev->handle.dev = dev; + mousedev->handle.handler = handler; + mousedev->handle.private = mousedev; + + mousedev->devfs = input_register_minor("mouse%d", minor, MOUSEDEV_MINOR_BASE); + + if (mousedev_mix.open) + input_open_device(&mousedev->handle); + + printk(KERN_INFO "mouse%d: PS/2 mouse device for input%d\n", minor, dev->number); + + return &mousedev->handle; +} + +static void mousedev_disconnect(struct input_handle *handle) +{ + struct mousedev *mousedev = handle->private; + + if (mousedev->open || mousedev_mix.open) + input_close_device(handle); + + if (!--mousedev->used) { + input_unregister_minor(mousedev->devfs); + mousedev_table[mousedev->minor] = NULL; + kfree(mousedev); + } +} + +static struct input_handler mousedev_handler = { + event: mousedev_event, + connect: mousedev_connect, + disconnect: mousedev_disconnect, + fops: &mousedev_fops, + minor: MOUSEDEV_MINOR_BASE, +}; + +static int __init mousedev_init(void) +{ + input_register_handler(&mousedev_handler); + + memset(&mousedev_mix, 0, sizeof(struct mousedev)); + init_waitqueue_head(&mousedev_mix.wait); + mousedev_table[MOUSEDEV_MIX] = &mousedev_mix; + mousedev_mix.used = 1; + mousedev_mix.minor = MOUSEDEV_MIX; + mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE); + + printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n"); + + return 0; +} + +static void __exit mousedev_exit(void) +{ + input_unregister_minor(mousedev_mix.devfs); + input_unregister_handler(&mousedev_handler); +} + +module_init(mousedev_init); +module_exit(mousedev_exit); diff -burN --exclude .* --exclude *.ver linux-2.2.17-orig/drivers/usb/ohci-debug.c linux-2.2.17/drivers/usb/ohci-debug.c --- linux-2.2.17-orig/drivers/usb/ohci-debug.c Tue May 11 10:04:03 1999 +++ linux-2.2.17/drivers/usb/ohci-debug.c Wed Dec 31 16:00:00 1969 @@ -1,178 +0,0 @@ -/* - * OHCI debugging code. It's gross. - * - * (C) Copyright 1999 Gregory P. Smith - */ - -#include -#include - -#include "ohci.h" - -void show_ohci_status(struct ohci *ohci) -{ - struct ohci_regs regs; - int i; - - regs.revision = readl(&ohci->regs->revision); - regs.control = readl(&ohci->regs->control); - regs.cmdstatus = readl(&ohci->regs->cmdstatus); - regs.intrstatus = readl(&ohci->regs->intrstatus); - regs.intrenable = readl(&ohci->regs->intrenable); - regs.hcca = readl(&ohci->regs->hcca); - regs.ed_periodcurrent = readl(&ohci->regs->ed_periodcurrent); - regs.ed_controlhead = readl(&ohci->regs->ed_controlhead); - regs.ed_controlcurrent = readl(&ohci->regs->ed_controlcurrent); - regs.ed_bulkhead = readl(&ohci->regs->ed_bulkhead); - regs.ed_bulkcurrent = readl(&ohci->regs->ed_bulkcurrent); - regs.current_donehead = readl(&ohci->regs->current_donehead); - regs.fminterval = readl(&ohci->regs->fminterval); - regs.fmremaining = readl(&ohci->regs->fmremaining); - regs.fmnumber = readl(&ohci->regs->fmnumber); - regs.periodicstart = readl(&ohci->regs->periodicstart); - regs.lsthresh = readl(&ohci->regs->lsthresh); - regs.roothub.a = readl(&ohci->regs->roothub.a); - regs.roothub.b = readl(&ohci->regs->roothub.b); - regs.roothub.status = readl(&ohci->regs->roothub.status); - for (i=0; iregs->roothub.portstatus[i]); - - printk(KERN_DEBUG " ohci revision = %x\n", regs.revision); - printk(KERN_DEBUG " ohci control = %x\n", regs.control); - printk(KERN_DEBUG " ohci cmdstatus = %x\n", regs.cmdstatus); - printk(KERN_DEBUG " ohci intrstatus = %x\n", regs.intrstatus); - printk(KERN_DEBUG " ohci intrenable = %x\n", regs.intrenable); - - printk(KERN_DEBUG " ohci hcca = %x\n", regs.hcca); - printk(KERN_DEBUG " ohci ed_pdcur = %x\n", regs.ed_periodcurrent); - printk(KERN_DEBUG " ohci ed_ctrlhead = %x\n", regs.ed_controlhead); - printk(KERN_DEBUG " ohci ed_ctrlcur = %x\n", regs.ed_controlcurrent); - printk(KERN_DEBUG " ohci ed_bulkhead = %x\n", regs.ed_bulkhead); - printk(KERN_DEBUG " ohci ed_bulkcur = %x\n", regs.ed_bulkcurrent); - printk(KERN_DEBUG " ohci curdonehead = %x\n", regs.current_donehead); - - printk(KERN_DEBUG " ohci fminterval = %x\n", regs.fminterval); - printk(KERN_DEBUG " ohci fmremaining = %x\n", regs.fmremaining); - printk(KERN_DEBUG " ohci fmnumber = %x\n", regs.fmnumber); - printk(KERN_DEBUG " ohci pdstart = %x\n", regs.periodicstart); - printk(KERN_DEBUG " ohci lsthresh = %x\n", regs.lsthresh); - - printk(KERN_DEBUG " ohci roothub.a = %x\n", regs.roothub.a); - printk(KERN_DEBUG " ohci roothub.b = %x\n", regs.roothub.b); - printk(KERN_DEBUG " ohci root status = %x\n", regs.roothub.status); - printk(KERN_DEBUG " roothub.port0 = %x\n", regs.roothub.portstatus[0]); - printk(KERN_DEBUG " roothub.port1 = %x\n", regs.roothub.portstatus[1]); -} /* show_ohci_status() */ - - -void show_ohci_ed(struct ohci_ed *ed) -{ - int stat = ed->status; - int skip = (stat & OHCI_ED_SKIP); - int mps = (stat & OHCI_ED_MPS) >> 16; - int isoc = (stat & OHCI_ED_F_ISOC); - int low_speed = (stat & OHCI_ED_S_LOW); - int dir = (stat & OHCI_ED_D); - int endnum = (stat & OHCI_ED_EN) >> 7; - int funcaddr = (stat & OHCI_ED_FA); - int halted = (ed->_head_td & 1); - int toggle = (ed->_head_td & 2) >> 1; - - printk(KERN_DEBUG " ohci ED:\n"); - printk(KERN_DEBUG " status = 0x%x\n", stat); - printk(KERN_DEBUG " %sMPS %d%s%s%s%s tc%d e%d fa%d\n", - skip ? "Skip " : "", - mps, - isoc ? "Isoc. " : "", - low_speed ? " LowSpd" : "", - (dir == OHCI_ED_D_IN) ? " Input" : - (dir == OHCI_ED_D_OUT) ? " Output" : "", - halted ? " Halted" : "", - toggle, - endnum, - funcaddr); - printk(KERN_DEBUG " tail_td = 0x%x\n", ed->tail_td); - printk(KERN_DEBUG " head_td = 0x%x\n", ed_head_td(ed)); - printk(KERN_DEBUG " next_ed = 0x%x\n", ed->next_ed); -} /* show_ohci_ed() */ - - -void show_ohci_td(struct ohci_td *td) -{ - int td_round = td->info & OHCI_TD_ROUND; - int td_dir = td->info & OHCI_TD_D; - int td_int_delay = (td->info & OHCI_TD_IOC_DELAY) >> 21; - int td_toggle = (td->info & OHCI_TD_DT) >> 24; - int td_errcnt = td_errorcount(*td); - int td_cc = OHCI_TD_CC_GET(td->info); - - printk(KERN_DEBUG " ohci TD hardware fields:\n"); - printk(KERN_DEBUG " info = 0x%x\n", td->info); - printk(KERN_DEBUG " %s%s%s%d %s\n", - td_round ? "Rounding " : "", - (td_dir == OHCI_TD_D_IN) ? "Input " : - (td_dir == OHCI_TD_D_OUT) ? "Output " : - (td_dir == OHCI_TD_D_SETUP) ? "Setup " : "", - "IntDelay ", td_int_delay, - (td_toggle < 2) ? " " : - (td_toggle & 1) ? "Data1 " : "Data0 "); - printk(KERN_DEBUG " %s%d %s0x%x, %sAccessed, %sActive\n", - "ErrorCnt ", td_errcnt, - "ComplCode ", td_cc, - td_cc_accessed(*td) ? "" : "Not ", - td_active(*td) ? "" : "Not "); - - printk(KERN_DEBUG " cur_buf = 0x%x\n", td->cur_buf); - printk(KERN_DEBUG " next_td = 0x%x\n", td->next_td); - printk(KERN_DEBUG " buf_end = 0x%x\n", td->buf_end); - printk(KERN_DEBUG " ohci TD driver fields:\n"); - printk(KERN_DEBUG " data = %p\n", td->data); - printk(KERN_DEBUG " dev_id = %p\n", td->dev_id); - printk(KERN_DEBUG " ed = %p\n", td->ed); - if (td->data != NULL) { - unsigned char *d = td->data; - printk(KERN_DEBUG " DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n", - d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7] ); - } -} /* show_ohci_td() */ - - -void show_ohci_device(struct ohci_device *dev) -{ - int idx; - printk(KERN_DEBUG " ohci_device usb = %p\n", dev->usb); - printk(KERN_DEBUG " ohci_device ohci = %p\n", dev->ohci); - printk(KERN_DEBUG " ohci_device ohci_hcca