diff -urN linux/Documentation/Configure.help linux.usb/Documentation/Configure.help — linux/Documentation/Configure.help Tue Jan 25 00:09:54 2000 +++ linux.usb/Documentation/Configure.help Tue Jan 25 00:09:28 2000 @@ -4909,6 +4909,327 @@ This enables support for the Powertec SCSI card on Acorn systems. If you have one of these, say Y. If unsure, say N. ++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 PCs 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) and then choose + from among the drivers for USB peripherals. + + 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. + +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, say Y. 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. + + 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. + +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 (actual 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 a keyboard, mouse, joystick, + graphic tablet, UPS or any other HID based devices to your computer + via USB. + +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. + +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. + +Wacom Graphire tablet support +CONFIG_USB_GRAPHIRE + Say Y here if you want to use the USB version of the Wacom + Graphire tablet. Make sure you select Mouse and Event support, + don’t select HID support, because this driver collides with it. + Use HIDBP support for keyboards and mice instead if you need it. + +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. + +Keyboard support +CONFIG_INPUT_KEYBDEV + Say Y here if you want your USB HID keyboard to be able to serve as + a system keyboard. + +Mouse support +CONFIG_INPUT_MOUSEDEV + Say Y here if you want your USB HID mouse to be accessible as + misc devices 32+ under /dev/, as an emulated PS/2 mouse. + +Mix all mice into one device +CONFIG_INPUT_MOUSEDEV_MIX + Say Y here if you want input from all your USB HID mice to be mixed + into one misc device. If you say N, you’ll have a separate + device for each your USB mouse. + +Support for digitizers +CONFIG_INPUT_MOUSEDEV_DIGITIZER + Use this if you have a digitizer that doesn’t emulate a mouse + itself, and want to use it as a mouse. + +Horizontal screen resolution +CONFIG_INPUT_MOUSEDEV_SCREEN_X + For the mouse emulation to be correct, the mousedev driver needs + to know the screen resolution you are using (in X). + +Vertical screen resolution +CONFIG_INPUT_MOUSEDEV_SCREEN_Y + For the mouse emulation to be correct, the mousedev driver needs + to know the screen resolution you are using (in X). + +Joystick support +CONFIG_INPUT_JOYDEV + Say Y here if you want your USB HID joystick or gamepad to be + accessible as /dev/js device. You can’t use a normal joystick + if you select this. + +Event interface support +CONFIG_INPUT_EVDEV + Say Y here if you want your USB HID device events be accessible + under /dev/inputX (misc 64+) 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 hp_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 USB 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. + + 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 want to connect a Connect Tech WhiteHEAT + multi-port USB to serial converter; a Belkin, Peracom, or eTek + single port USB to serial converter; or a Handspring Visor. + 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 + Driver 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. + +USB Belkin Single Port Serial Driver +CONFIG_USB_SERIAL_BELKIN + Say Y here if you want to use a Belkin single port USB to serial + converter device. + +USB Peracom Single Port Serial Driver +CONFIG_USB_SERIAL_PERACOM + Say Y here if you want to use a Peracom single port USB to serial + converter device. + +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 CPiA Camera support +CONFIG_USB_CPIA + Say Y here if you want to connect this type of camera 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 cpia.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB IBM (Xirlink) C-It Camera support +CONFIG_USB_IBMCAM + Say Y here if you want to connect this type of camera 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 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 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 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 SCSI (mass storage) support +CONFIG_USB_SCSI + 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-scsi.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB SCSI verbose debug +CONFIG_USB_SCSI_DEBUG + Say Y here in order to have the USB SCSI 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 adapters 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 filesystem +CONFIG_USB_DEVICEFS + This file system implements a “devices” file, that lists + the currently connected to your USB busses, a “drivers” file + that lists the USB kernel client drivers currently loaded, + and for every connected device a file named “xxx/yyy”, where + xxx is the bus number and yyy the device number, that can be used + by userspace drivers to talk to the device. + + Most users want to say Y here. + +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. + + 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. + Network device support? CONFIG_NETDEVICES You can say N here if you don’t intend to connect your Linux box to diff -urN linux/Documentation/usb/CREDITS linux.usb/Documentation/usb/CREDITS — linux/Documentation/usb/CREDITS Wed Dec 31 17:00:00 1969 +++ linux.usb/Documentation/usb/CREDITS Tue Jan 25 00:08:43 2000 @@ -0,0 +1,167 @@ +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 + Paul Mackerras + David E. Nelson + Vojtech Pavlik + 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 -urN linux/Documentation/usb/URB.txt linux.usb/Documentation/usb/URB.txt — linux/Documentation/usb/URB.txt Wed Dec 31 17:00:00 1969 +++ linux.usb/Documentation/usb/URB.txt Tue Jan 25 00:08:43 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 -urN linux/Documentation/usb/acm.txt linux.usb/Documentation/usb/acm.txt — linux/Documentation/usb/acm.txt Wed Dec 31 17:00:00 1969 +++ linux.usb/Documentation/usb/acm.txt Tue Jan 25 00:08:43 2000 @@ -0,0 +1,94 @@ +The ACM driver works with modems and ISDN TAs that use the USB Abstract +Control Model standard. + +**************************** +Test it: +Watch out, the driver is not stable and tested. Sync often, make backups, +most importand: don’t blame me… + +Create device files: +mknod /dev/ttyACM0 c 166 0 +mknod /dev/ttyACM1 c 166 1 +mknod /dev/ttyACM2 c 166 2 +mknod /dev/ttyACM3 c 166 3 +Compile a kernel with support for your host controller (uhci only for now!) +and support for ACM. Boot this kernel. If you connect your device to the +USB bus you should see messages like the following: + +Jul 19 20:14:29 office kernel: USB new device connect, assigned device number 1 +Jul 19 20:14:29 office kernel: Found 02:09 +Jul 19 20:14:29 office kernel: Found 04:09 +Jul 19 20:14:29 office kernel: Found 05:07 +Jul 19 20:14:29 office last message repeated 2 times +Jul 19 20:14:29 office kernel: parsed = 39 len = 67 +Jul 19 20:14:29 office kernel: Expected descriptor 04/09, got 02/09 – skipping +Jul 19 20:14:29 office kernel: 0 09 +Jul 19 20:14:29 office kernel: 1 02 +Jul 19 20:14:29 office kernel: 2 43 +Jul 19 20:14:29 office kernel: 3 00 +Jul 19 20:14:29 office kernel: 4 02 +Jul 19 20:14:29 office kernel: 5 02 +Jul 19 20:14:29 office kernel: 6 04 +Jul 19 20:14:29 office kernel: 7 60 +Jul 19 20:14:29 office kernel: 8 00 +Jul 19 20:14:29 office kernel: Found 04:09 +Jul 19 20:14:29 office kernel: Found 02:09 +Jul 19 20:14:29 office kernel: Found 04:09 +Jul 19 20:14:29 office kernel: Found 05:07 +Jul 19 20:14:29 office kernel: Found 04:09 +Jul 19 20:14:29 office kernel: Found 05:07 +Jul 19 20:14:29 office kernel: Found 05:07 +Jul 19 20:14:29 office kernel: parsed = 67 len = 0 +Jul 19 20:14:29 office kernel: getstringtable +Jul 19 20:14:29 office kernel: acm_probe +Jul 19 20:14:29 office kernel: USB ACM found + +Watch out for the line: +Jul 19 20:14:29 office kernel: USB new device connect, assigned device number 1 +and the line: +Jul 19 20:14:29 office kernel: USB ACM found +These two lines show that the device was seen by the usb host controller and +then recognized by the acm driver as a valid device. + +If you use a terminal emulation software like minicom with /dev/ttyACM0 you +should be able to send AT commands to your device and get responses. I’ve +been able to do zmodem downloads to another pc. However downloads from one +ISDN TA to another ISDN TA connected to the same PC didn’t work. Don’t +know why. Flow control is not finised after all and i’d guess there might +be problems on heavily loades PCs. I also did some tests with ppp but i’m +not finised with this. There might be a chance to get it working. However +i’d like to know if your device is recognized as an ACM device. I’m also +interested if the thing is stable or if it crashes. +(should i say how it crases?) + +You should be able to add and remove devices from the bus. The driver will +always try to fill up unused ttys. This means if you hotplug devices their +order may have changed after reboot. This is not the behaviour Linus liked +to see but it’s ok for now. (I hope 😉 + +Please report your experiences to me: [email protected] + +*************************** +I’ve tested it with: +3Com ISDN Pro TA. + +It should work with (That means i know these devices conform to ACM): +3Com Office Connect Modem +3Com Sportster USB (I think that’s what it’s called) + +*************************** +Many thanks to 3Com which did not only support me with hardware but also +with technical support in USB questions. They also allowed me to do tests in +their lab. Great! + +*************************** +Known bugs: +Flow control not tested (likely not to work) +Some tty function calls not implemented (putchar, etc…) +Huge amounts of debug output (compile in [*] Magic SysRq key and press ALT+PRTSCR+0 ) +Not all mem is freed at close (need terminate irq in hcd) + +*************************** +Have fun, + Armin Fuerst diff -urN linux/Documentation/usb/dc2xx.txt linux.usb/Documentation/usb/dc2xx.txt — linux/Documentation/usb/dc2xx.txt Wed Dec 31 17:00:00 1969 +++ linux.usb/Documentation/usb/dc2xx.txt Tue Jan 25 00:08:43 2000 @@ -0,0 +1,113 @@ +19 January 2000 [email protected] + +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. At this writing the best +known support for these cameras is a Python script that supports image +downloading from those cameras. (See archives of the linux-usb mailing +list.) When it becomes available, the HP PhotoSmart C500 should also +work … it’s another Digita OS camera with USB support. + +It’s likely that other digital still cameras can also use this USB driver, +even if they’re not from Kodak and don’t use Digita. The reason is that +most currently known USB still camera protocols treat USB like a faster +packet-carrying connection than a serial line, which is exactly how this +driver looks to an application. + + +USB HARDWARE + +This has been shown to work on x86 OHCI and UHCI (Intel) chipsets. OHCI has +been trouble free; not so with UHCI, which was first seen to be happy with +2.3.24 kernels, and has not been as fast as OHCI. Users on the PowerMac +platform have had success, although the stock kernel doesn’t yet support +that 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. + + +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 0666 /dev/kodak00 c 180 80 + # mknod -m 0666 /dev/kodak01 c 180 81 + … + +The driver supports multiple device nodes. The USB framework supports +a maximum of sixteen device nodes (up to minor device number 96), though +by default fewer devices are available. + +When you plug in one camera, it will use the first device node (kodak00 +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. + +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, but the “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 + 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 + Serial Number: ? + 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/kodak00” (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 -urN linux/Documentation/usb/error-codes.txt linux.usb/Documentation/usb/error-codes.txt — linux/Documentation/usb/error-codes.txt Wed Dec 31 17:00:00 1969 +++ linux.usb/Documentation/usb/error-codes.txt Tue Jan 25 00:08:43 2000 @@ -0,0 +1,108 @@ +$Id: linux-2.2.14-usb.diff,v 1.1 2000/01/28 02:51:18 trini 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 (0900) + +-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 -urN linux/Documentation/usb/hid.txt linux.usb/Documentation/usb/hid.txt — linux/Documentation/usb/hid.txt Wed Dec 31 17:00:00 1969 +++ linux.usb/Documentation/usb/hid.txt Tue Jan 25 00:08:43 2000 @@ -0,0 +1,162 @@ + Linux HID driver v0.8 + (c) 1999 Vojtech Pavlik + (c) 1999 Andreas Gal + 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. Introduction +~~~~~~~~~~~~~~~ + This is a driver for USB devices conforming to the USB HID (Human Input +Device) standard. These devices include namely keyboards, mice and +joysticks. + + However many other devices (monitors, speakers, UPSs …) also communicate +through the same protocol, which makes its specification somewhat bloated. +This isn’t a problem, though, because the driver doesn’t need to know about +all the possible devices it can control, and can just parse the protocol and +leave the rest of the job (for example understanding what the UPS wants to +say) to the userland. + + Because of this, the USB HID driver has two interfaces. One is via the +proc filesystem, allowing userland applications send and read arbitrary +reports to and from a connected USB device. The other is via a very simple +yet generic input device driver, which dispatches input events (keystrokes, +mouse or joystick movements) to specific, backward compatible userland +interfaces. This way a PS/2 mouse, an AT keyboard or a Linux joystick driver +interface are emulated, and allow applications to immediately work with USB +mice, USB keyboards and USB joysticks without any changes. + + The input driver is aimed for a little more than USB device handling in +the future, though. It’s generic enough so that it can be used for any +mouse, keyboard or joystick (and more, of course). A PS/2 mouse driver, a +serial mouse, Sun mouse, and most of the busmouse drivers were rewritten to +use this as well as the AT keyboard and Sun keyboard drivers. This will +hopefully allow conversion of all Linux keyboard and mouse and joystick +drivers to this scheme. + + 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. +There is also a mailing list for this: + + [email protected] + +Send “subscribe linux-joystick Your Name” to subscribe to it. + +2. Usage +~~~~~~~~ + Since the driver comes with recent 2.3 kernels, all that’s needed to use +it is to enable it either as a module or compiled-in into the kernel. + + After that, after reboot (and possibly also inserting the USB and HID +modules) the following will happen: + +* If you selected keyboard support, all USB keystrokes will be also routed + to the Linux keyboard driver as if being input through the ordinary system + keyboard. + +* If you selected mouse support, there will be (one or more) simulated PS/2 + mouse devices on major 10, minor 32, 33 and more. These simulated mice can + in addition to a standard 3-button PS/2 mouse behave like MS Intellimice, + with a wheel. If you want to use the wheel, just specify ‘-t imps2’ to gpm + and ‘Protocol “ImPS/2″‘ to X, and it will work. A single emulated mouse + device can be open by any number of processes (unlike the /dev/psaux), and + for each of them the emulation is separate, each can use a different mode. + The mousedev driver, which emulates the mice, can also emulate a Genius + NewScroll 5 buttons-and-a-wheel mouse, if you set it to a Genius PS/2 + mode (‘-t netmouse’ ‘Protocol “NetMousePS/2″‘). However, not gpm, nor X + can decode the 5 buttons yet, so this isn’t very useful right now. + +* If you selected joystick support, the driver will take over major 15, the + joystick major number, and will emulate joysticks on it. This means the + normal joystick driver can’t be used together with it (now, after the + normal joystick drivers are converted to the input scheme, all will work + nicely together). Also, you’ll probably need to calibrate your joystick + manually (‘man jscal’) to be able to use it, because the USB + autocalibration is far from perfect yet. + +* If you selected event device support, there will be devices on major 10, + minors 64, 65 and more for each input device connected through this + driver. These devices output raw events the input driver dispatches. Each + has a timestamp. This hopefully will be THE way X will talk to keyboard + and mice, because it’s hardware independent, and not limited by existing + de-facto standards. + +3. 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/hidmouse (c, 10, 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). + +4. FAQ +~~~~~~ +Q: Why aren’t any questions here yet? +A: Because none were frequent enough yet. + +5. Event interface +~~~~~~~~~~~~~~~~~~ + Should you want to add event device support into any application (X, gpm, +svgalib …) I ([email protected]) 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/inputX 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. + +6. Proc interface +~~~~~~~~~~~~~~~~~ + For HID-specific devices there is also the /proc interface. It isn’t +present in this release yet, though, so it’s description will appear here +together with the code in the driver. diff -urN linux/Documentation/usb/ohci-hcd.txt linux.usb/Documentation/usb/ohci-hcd.txt — linux/Documentation/usb/ohci-hcd.txt Wed Dec 31 17:00:00 1969 +++ linux.usb/Documentation/usb/ohci-hcd.txt Tue Jan 25 00:08:43 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-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 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 -urN linux/Documentation/usb/ov511.txt linux.usb/Documentation/usb/ov511.txt — linux/Documentation/usb/ov511.txt Wed Dec 31 17:00:00 1969 +++ linux.usb/Documentation/usb/ov511.txt Tue Jan 25 00:08:43 2000 @@ -0,0 +1,110 @@ +——————————————————————————- +Readme for Linux device driver for the OmniVision OV511 USB to camera bridge IC +——————————————————————————- + +Author: Mark McClelland +Homepage: http://people.delphi.com/mmcclelland/linux/ + +INTRODUCTION: + +This is a preliminary version of my OV511 Linux device driver. Currently, it can +grab a frame in color (YUV420) at 640×480 or 320×240 using either vidcat or +xawtv. Other utilities may work but have not yet been tested. + +NOTE: 320×240 does not work reliably for me, and causes complete system crashes. + I recommend not using it until a later version, and if you do, run “sync” + first. + +SUPPORTED CAMERAS: +________________________________________________________ +Manufacturer | Model | Custom ID | Status +—————–+—————-+———–+——— +MediaForte | MV300 | 0 | Untested +D-Link | DSB-C300 | 3 | Working +Creative Labs | WebCam 3 | 21 | Working +Lifeview | RoboCam | 100 | Untested +AverMedia | InterCam Elite | 102 | Working +——————————————————– + +Any camera using the OV511 and the OV7610 CCD should work with this driver. The +driver only detects known cameras though, based on their custom id number. If +you have a currently unsupported camera, the ID number should be reported to you +in the kernel logs. If you have an unsupported camera, please send me the model, +manufacturer and ID number and I will add it to the detection code. In the +meantime, you can add to the code yourself in the function ov511_probe() + +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) + (see: http://www.exploits.org/v4l/ ) + +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 + +Now you are ready to run a video app! Both vidcat and xawtv work well for me +at 640×480. + +[Using vidcat:] + + vidcat -s 640×480 > 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. + +WORKING FEATURES: + o Color streaming/capture at 640×480 (reliably) and 320×240 (unreliably) + o YUV420 color + o Setting/getting of saturation, contrast and brightness (no color yet) + +WHAT NEEDS TO BE DONE: + +The rest of the work will involve implementing support for all the different +resolutions, color depths, etc. Also, while support for the OV511’s proprietary +lossy compression is apparently not necessary (the code currently disables it,) +it would be a nice addition as it improves performance 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. + +HOW TO CONTACT ME: + +You can email me at [email protected] . 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. diff -urN linux/Documentation/usb/proc_usb_info.txt linux.usb/Documentation/usb/proc_usb_info.txt — linux/Documentation/usb/proc_usb_info.txt Wed Dec 31 17:00:00 1969 +++ linux.usb/Documentation/usb/proc_usb_info.txt Tue Jan 25 00:08:43 2000 @@ -0,0 +1,237 @@ +/proc/bus/usb filesystem output +=============================== +(version 19991218) + + +The /proc filesystem for USB devices generates +/proc/bus/usb/drivers and /proc/bus/usb/devices. + +/proc/bus/usb/drivers just lists the registered drivers, +one per line. Not very interesting or pretty. + +In /proc/bus/usb/devices, each device’s output has multiple +lines (except for a root hub) 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 +D = Device descriptor info. +P = Product ID info. (from Device descriptor, but they won’t fit + together on one line) +S = String info +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. +|__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 TDPCIE] 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 +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 -urN linux/Documentation/usb/scanner-hp-sane.txt linux.usb/Documentation/usb/scanner-hp-sane.txt — linux/Documentation/usb/scanner-hp-sane.txt Wed Dec 31 17:00:00 1969 +++ linux.usb/Documentation/usb/scanner-hp-sane.txt Tue Jan 25 00:08:43 2000 @@ -0,0 +1,69 @@ +Oct. 19, 1999 + +CHANGES + +- Ammended for Linux-2.3.22+ + + +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 Scanner that I’m aware of that does not support SCL is the +4200C. All other HP scanners with USB interfaces should work (4100C, +5200C, 6200C, and 6300C). 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 README.scanner +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. + + +OK, I’VE INSTALLED SANE. SO WHAT DO I DO NOW? + +NOTE: $INSTALL_DIR is the location where SANE was 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 + +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 work. If you are dealing with a RedHat system, this means that +you’ll also need to install the gimp-devel rpm package. + +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 [email protected] +http://www.jump.net/~dnelson diff -urN linux/Documentation/usb/scanner.txt linux.usb/Documentation/usb/scanner.txt — linux/Documentation/usb/scanner.txt Wed Dec 31 17:00:00 1969 +++ linux.usb/Documentation/usb/scanner.txt Tue Jan 25 00:08:43 2000 @@ -0,0 +1,231 @@ +Oct 19, 1999 + +CHANGES + +- Ammended for linux-2.3.22+ +- Appended hp_scan.c to end of this README +- Removed most references to HP + + +OVERVIEW + +This README will address 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 README.scanner_hp_sane for guidance on how to configure +SANE to use a USB HP Scanner. + + +ADDITIONAL INFORMATION + +http://www.linux-usb.org/ +http://www.dynamine.net/linux-usb/HOWTO/ + + +REQUIREMENTS + +A host with a USB port. Ideally, either a UHCI (Intel) or OHCI +(Compaq and others) hardware port should work. However, I’ve only +been able to really use an OHCI controller. I did have access to a +system with a UHCI controller but some very limited testing did not +produce satisfactory results. Luke Ordelmans + has reported success using the UHCI host +controller with kernel 2.3.18 and a ChainTech motherboard. Here +lately I’ve been having better success with the ohci-hcd driver. But +since Linux USB support is still in a state of constant development +that may change at a later date. I am confident that eventually all +the host contollers will perform without incident. + +A Linux kernel with USB support (preferably linux-2.3.18+) + +A Linux kernel with USB Scanner support. + + +CONFIGURATION + +Using `make menuconfig` or your prefered method for configuring the +kernel, select ‘Support for USB’, ‘OHCI/OHCI-HCD/UHCI’ depending on +your hardware, ‘USB hub support’, and ‘USB Scanner support’. Compile +and install the modules (you may need to execute `depmod -a` to update +the module dependencies). Testing was performed only as modules, +YMMV. + +Add a device for the USB scanner: + linux-2.3.22 and above: `mknod /dev/usbscanner c 180 48` + linux-2.3.21 and below: `mknod /dev/usbscanner c 16 1` + +Set appropriate permissions for /dev/usbscanner (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 usb-ohci + modprobe scanner + + OHCI-HCD: + modprobe usb-ohci-hcd + modprobe hub + modprobe scanner + + UHCI: + modprobe usb-uhci + modprobe hub (don’t know if this is required or not) + 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. Its 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. + + +MODULE PARAMETERS + +If you have a device that 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 conf.modules/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` if USB /proc +support was selected during kernel configuration. + + +BUGS + +If you encounter any problems feel free to drop me an email. + +David /\/elson [email protected] +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) { + printf(“Read: %u\n”, cnt); + if(fwrite(data, sizeof(char), cnt, fpout) 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 -urN linux/Documentation/usb/usb-serial.txt linux.usb/Documentation/usb/usb-serial.txt — linux/Documentation/usb/usb-serial.txt Wed Dec 31 17:00:00 1969 +++ linux.usb/Documentation/usb/usb-serial.txt Tue Jan 25 00:08:43 2000 @@ -0,0 +1,115 @@ +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 16 different serial interfaces at + one time. Once more of the drivers become stable, this number will be + increased to the full 256. + + 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/ttyUSB4 c 188 4 + mknod /dev/ttyUSB5 c 188 5 + mknod /dev/ttyUSB6 c 188 6 + mknod /dev/ttyUSB7 c 188 7 + mknod /dev/ttyUSB8 c 188 8 + mknod /dev/ttyUSB9 c 188 9 + mknod /dev/ttyUSB10 c 188 10 + mknod /dev/ttyUSB11 c 188 11 + mknod /dev/ttyUSB12 c 188 12 + mknod /dev/ttyUSB13 c 188 13 + mknod /dev/ttyUSB14 c 188 14 + mknod /dev/ttyUSB15 c 188 15 + mknod /dev/ttyUSB16 c 188 16 + + +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, but the use of a + special Anchor Chips extension is currently giving me problems. + 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. + + I write _should_ because communication does not seem to work properly at + this time. I am in contact with the developers at HandSpring and am + working at getting this to work properly. + + There is a webpage for this portion of the driver at + http://milosch.net/visor/ and a project set up with mailing lists for + it at : + http://sourceforge.net/project/?group_id=1404 + + +Belkin single port serial converter +Peracom single port serial converter + +Current status: + The driver can handle enumerating the device, and sending and receiving + data from the converter. However, since I do not have a spec for the + Belkin, Peracom, and eTek devices, and the raw dumps from the Win98 + driver are confusing, and eTek refuses to provide documentation on their + protocol, no control signals are currently handled, and the data will + most likely come through on a baud rate that you are not expecting. So + if you have these devices, do not expect the correct data to show up at + either end. + + +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 [email protected] diff -urN linux/Makefile linux.usb/Makefile — linux/Makefile Tue Jan 25 11:21:03 2000 +++ linux.usb/Makefile Tue Jan 25 11:44:33 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) diff -urN linux/drivers/char/mem.c linux.usb/drivers/char/mem.c — linux/drivers/char/mem.c Tue Jan 25 00:09:54 2000 +++ linux.usb/drivers/char/mem.c Tue Jan 25 00:08:45 2000 @@ -56,14 +56,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, @@ -625,17 +619,8 @@ if (register_chrdev(MEM_MAJOR,”mem”,&memory_fops)) printk(“unable to get major %d for memory devs\n”, MEM_MAJOR); rand_initialize(); – raw_init(); #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 -urN linux/drivers/usb/CREDITS linux.usb/drivers/usb/CREDITS — linux/drivers/usb/CREDITS Tue May 11 10:04:03 1999 +++ linux.usb/drivers/usb/CREDITS Wed Dec 31 17: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 -urN linux/drivers/usb/Config.in linux.usb/drivers/usb/Config.in — linux/drivers/usb/Config.in Mon May 10 10:18:34 1999 +++ linux.usb/drivers/usb/Config.in Tue Jan 25 00:08:39 2000 @@ -1,30 +1,63 @@ # # 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 – fi +tristate ‘Support for USB’ CONFIG_USB +if [ ! “$CONFIG_USB” = “n” ]; then – bool ‘USB mouse support’ CONFIG_USB_MOUSE – bool ‘USB keyboard support’ CONFIG_USB_KBD – bool ‘USB audio parsing support’ CONFIG_USB_AUDIO – fi +comment ‘USB Controllers’ + dep_tristate ‘ UHCI (Intel PIIX4, VIA, …) support’ CONFIG_USB_UHCI $CONFIG_USB + dep_tristate ‘ OHCI (Compaq, iMacs, OPTi, SiS, ALi, …) support’ CONFIG_USB_OHCI $CONFIG_USB + +comment ‘Miscellaneous USB options’ + bool ‘ Preliminary USB device filesystem’ CONFIG_USB_DEVICEFS + +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 + 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 ConnectTech WhiteHEAT Serial Driver’ CONFIG_USB_SERIAL_WHITEHEAT + bool ‘ USB Handspring Visor Driver’ CONFIG_USB_SERIAL_VISOR + bool ‘ USB Belkin Single Port Serial Driver’ CONFIG_USB_SERIAL_BELKIN + bool ‘ USB Peracom Single Port Serial Driver’ CONFIG_USB_SERIAL_PERACOM + fi + dep_tristate ‘ USB CPiA Camera support’ CONFIG_USB_CPIA $CONFIG_USB + dep_tristate ‘ USB IBM (Xirlink) C-it Camera support’ CONFIG_USB_IBMCAM $CONFIG_USB + dep_tristate ‘ USB OV511 Camera support’ CONFIG_USB_OV511 $CONFIG_USB + dep_tristate ‘ USB Kodak DC-2xx Camera support’ CONFIG_USB_DC2XX $CONFIG_USB + dep_tristate ‘ USB SCSI (mass storage) support’ CONFIG_USB_SCSI $CONFIG_USB + if [ “$CONFIG_USB_SCSI” != “n” ]; then + bool ‘ USB SCSI verbose debug’ CONFIG_USB_SCSI_DEBUG + fi + dep_tristate ‘ USS720 parport driver’ CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT + dep_tristate ‘ DABUSB driver’ CONFIG_USB_DABUSB $CONFIG_USB + +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 Graphire tablet support’ CONFIG_USB_GRAPHIRE $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 + bool ‘ Mix all mice into one device’ CONFIG_INPUT_MOUSEDEV_MIX + bool ‘ Support for digitizers’ CONFIG_INPUT_MOUSEDEV_DIGITIZER + if [ “$CONFIG_INPUT_MOUSEDEV_DIGITIZER” != “n” ]; then + int ‘ Horizontal screen resolution’ CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024 + int ‘ Vertical screen resolution’ CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768 + fi + fi + dep_tristate ‘ Joystick support’ CONFIG_INPUT_JOYDEV $CONFIG_USB + dep_tristate ‘ Event interface support’ CONFIG_INPUT_EVDEV $CONFIG_USB fi endmenu diff -urN linux/drivers/usb/Makefile linux.usb/drivers/usb/Makefile — linux/drivers/usb/Makefile Mon May 10 10:18:34 1999 +++ linux.usb/drivers/usb/Makefile Tue Jan 25 00:08:39 2000 @@ -1,88 +1,109 @@ # -# 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. -SUB_DIRS := -MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) – -L_TARGET := usb.a -M_OBJS := -L_OBJS := -LX_OBJS := -USBX_OBJS := usb.o hub.o usb-debug.o +# Subdirs. -ifeq ($(CONFIG_USB_MOUSE),y) – USBX_OBJS += mouse.o -endif +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) -ifeq ($(CONFIG_USB_KBD),y) – USBX_OBJS += keyboard.o keymap.o -endif +# The target object and module list name. -ifeq ($(CONFIG_USB_AUDIO),y) – USBX_OBJS += audio.o -endif +O_TARGET := usbdrv.o +M_OBJS := +O_OBJS := +MOD_LIST_NAME := USB_MODULES -ifeq ($(CONFIG_USB), y) – L_OBJS += $(USBX_OBJS) -endif +# Objects that export symbols. -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 +export-objs := usb.o input.o -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 -endif +# Multipart objects. + +list-multi := usbcore.o +usbcore-objs := usb.o usb-debug.o usb-core.o hub.o +usb-scsi-objs := usb_scsi.o -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 – endif +# Optional parts of multipart objects. + +ifeq ($(CONFIG_USB_DEVICEFS),y) + usbcore-objs += devio.o inode.o drivers.o devices.o endif -include $(TOPDIR)/Rules.make +ifeq ($(CONFIG_USB_SCSI_DEBUG),y) + usb-scsi-objs += usb_scsi_debug.o +endif + +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := -keymap.o: keymap.c +# Each configuration option enables a list of files. -keymap.c: maps/serial.map maps/usb.map maps/fixup.map – ./mkmap > [email protected] +obj-$(CONFIG_USB) += usbcore.o +obj-$(CONFIG_USB_UHCI) += usb-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_GRAPHIRE) += graphire.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_SERIAL) += usb-serial.o +obj-$(CONFIG_USB_AUDIO) += audio.o +obj-$(CONFIG_USB_CPIA) += cpia.o +obj-$(CONFIG_USB_IBMCAM) += ibmcam.o +obj-$(CONFIG_USB_DC2XX) += dc2xx.o +obj-$(CONFIG_USB_SCSI) += usb-scsi.o +obj-$(CONFIG_USB_USS720) += uss720.o +obj-$(CONFIG_USB_DABUSB) += dabusb.o +obj-$(CONFIG_USB_OV511) += ov511.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. + +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-uhci.o: uhci.o uhci-debug.o $(USBX_OBJS) – $(LD) $(LD_RFLAG) -r -o [email protected] uhci.o uhci-debug.o $(USBX_OBJS) +# Link rules for multi-part drivers. -usb-ohci.o: ohci.o ohci-debug.o $(USBX_OBJS) – $(LD) $(LD_RFLAG) -r -o [email protected] ohci.o ohci-debug.o $(USBX_OBJS) +usbcore.o: $(usbcore-objs) + $(LD) -r -o [email protected] $(usbcore-objs) -usb-ohci-hcd.o: ohci-hcd.o ohci-root-hub.o $(USBX_OBJS) – $(LD) $(LD_RFLAG) -r -o [email protected] ohci-hcd.o ohci-root-hub.o $(USBX_OBJS) – +usb-scsi.o: $(usb-scsi-objs) + $(LD) -r -o [email protected] $(usb-scsi-objs) diff -urN linux/drivers/usb/README.kbd linux.usb/drivers/usb/README.kbd — linux/drivers/usb/README.kbd Wed Apr 21 10:29:09 1999 +++ linux.usb/drivers/usb/README.kbd Wed Dec 31 17: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 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 -urN linux/drivers/usb/README.ohci linux.usb/drivers/usb/README.ohci — linux/drivers/usb/README.ohci Tue May 11 10:04:03 1999 +++ linux.usb/drivers/usb/README.ohci Wed Dec 31 17: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… – — [email protected] – diff -urN linux/drivers/usb/README.ohci_hcd linux.usb/drivers/usb/README.ohci_hcd — linux/drivers/usb/README.ohci_hcd Mon May 10 10:18:34 1999 +++ linux.usb/drivers/usb/README.ohci_hcd Wed Dec 31 17: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 -urN linux/drivers/usb/acm.c linux.usb/drivers/usb/acm.c — linux/drivers/usb/acm.c Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/acm.c Tue Jan 25 00:08:39 2000 @@ -0,0 +1,663 @@ +/* + * acm.c Version 0.14 + * + * 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 + */ + +/* + * 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 “usb.h” + +/* + * 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) */ + 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 clocal; /* termios CLOCAL */ +}; + +static struct usb_driver acm_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 context; + devrequest *dr = urb->transfer_buffer; + unsigned char *data = (unsigned char *)(dr + 1); + int newctrl; + + if (!ACM_READY(acm)) return; + + if (urb->status 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 (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { + dbg(“calling hangup”); + tty_hangup(acm->tty); + } + + 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; + } + + 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; + + if (!ACM_READY(acm)) return; + + if (!urb->status) { + + for (i = 0; i actual_length; i++) + tty_insert_flip_char(tty, data[i], 0); + + tty_flip_buffer_push(tty); + + } else + dbg(“nonzero read bulk status received: %d”, urb->status); + + if (usb_submit_urb(urb)) + dbg(“failed resubmitting read urb”); + + return; +} + +static void acm_write_bulk(struct urb *urb) +{ + struct acm *acm = (struct acm *)urb->context; + struct tty_struct *tty = acm->tty; + + if (!ACM_READY(acm)) return; + + if (urb->status) + dbg(“nonzero write bulk status received: %d”, urb->status); + + if ((tty->flags & (1 ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + + wake_up_interruptible(&tty->write_wait); + + return; +} + +/* + * 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; + + MOD_DEC_USE_COUNT; + + if (–acm->used) return; + + 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); + return; + } + + acm_table[acm->minor] = NULL; + kfree(acm); +} + +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; + + 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; + usb_unlink_urb(&acm->readurb); +} + +static void acm_tty_unthrottle(struct tty_struct *tty) +{ + struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return; + if (usb_submit_urb(&acm->readurb)) + dbg(“usb_submit_urb(read bulk) in unthrottle() failed”); +} + +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; + + 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 (!memcmp(&acm->line, &newline, sizeof(struct acm_line))) + return; + + memcpy(&acm->line, &newline, sizeof(struct acm_line)); + + if (!newline.speed) { + if (acm->ctrlout) acm_set_control(acm, acm->ctrlout = 0); + return; + } + + acm_set_line(acm, &acm->line); + + dbg(“set line: %d %d %d %d”, newline.speed, newline.stopbits, newline.parity, newline.databits); +} + +/* + * 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 descriptor.bNumConfigurations; i++) { + + cfacm = dev->config + i; + dbg(“probing config %d”, cfacm->bConfigurationValue); + + ifcom = cfacm->interface[0].altsetting + 0; + if (ifcom->bInterfaceClass != 2 || ifcom->bInterfaceSubClass != 2 || + ifcom->bInterfaceProtocol != 1 || ifcom->bNumEndpoints != 1) + continue; + + epctrl = ifcom->endpoint + 0; + if ((epctrl->bEndpointAddress & 0x80) != 0x80 || (epctrl->bmAttributes & 3) != 3) + continue; + + ifdata = cfacm->interface[1].altsetting + 0; + if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2) + continue; + + if (usb_interface_claimed(cfacm->interface + 0) || + usb_interface_claimed(cfacm->interface + 1)) + continue; + + epread = ifdata->endpoint + 0; + epwrite = ifdata->endpoint + 1; + + if ((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 wMaxPacketSize; + readsize = epread->wMaxPacketSize; + acm->writesize = epwrite->wMaxPacketSize; + acm->iface = cfacm->interface; + acm->minor = minor; + acm->dev = dev; + + 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); + + 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) { + 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: “usb”, + name: “ttyACM”, + 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, + + 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 / cleanup. + */ + +#ifdef MODULE +void cleanup_module(void) +{ + usb_deregister(&acm_driver); + tty_unregister_driver(&acm_tty_driver); +} + +int init_module(void) +#else +int usb_acm_init(void) +#endif +{ + 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) but please Philips start with your + * own stuff!!!! + * 1999-11-02: 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: 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 + * + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include “usb.h” +#include “audio.h” -static int usb_audio_probe(struct usb_device *dev); -static void usb_audio_disconnect(struct usb_device *dev); -static LIST_HEAD(usb_audio_list); +#define AUDIO_DEBUG 1 -struct usb_audio -{ – struct usb_device *dev; +#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= 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 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 usb_audio_probe(struct usb_device *dev) +static int dmabuf_init(struct dmabuf *db) { – struct usb_interface_descriptor *interface; – struct usb_endpoint_descriptor *endpoint; – struct usb_audio *aud; + unsigned int nr, bytepersec, bufs; + void *p; – int i; – int na=0; – – interface = &dev->config[0].interface[0]; + /* initialize some fields */ + db->rdptr = db->wrptr = db->total_bytes = db->count = db->error = 0; + /* calculate required buffer size */ + bytepersec = db->srate format); + bufs = 1U ossfragshift) { + if ((1000 ossfragshift) fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag fragshift > 3) { + db->fragshift–; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags numfrag) + db->numfrag = db->ossmaxfrags; + db->dmasize = db->numfrag fragshift; + for(nr = 0; nr 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 = db->dmasize) + break; + } + db->bufsize = nr 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\n”, + bytepersec, bufs, db->ossfragshift, db->ossmaxfrags, db->fragshift, db->fragsize, + db->numfrag, db->dmasize, db->bufsize); + return 0; +} + +static int dmabuf_mmap(struct dmabuf *db, unsigned long start, unsigned long size, pgprot_t prot) +{ + unsigned int nr; + + if (!db->ready || db->mapped || (start | size) & (PAGE_SIZE-1) || size > db->bufsize) + return -EINVAL; + size >>= PAGE_SHIFT; + for(nr = 0; nr sgbuf[nr]) + return -EINVAL; + db->mapped = 1; + for(nr = 0; nr sgbuf[nr]), PAGE_SIZE, prot)) + return -EAGAIN; + start += PAGE_SIZE; + } + return 0; +} + +static void dmabuf_copyin(struct dmabuf *db, const void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; + + db->total_bytes += size; + for (;;) { + if (size 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; + } +} + +static void dmabuf_copyout(struct dmabuf *db, void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; + + db->total_bytes += size; + for (;;) { + if (size 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 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; + } +} + +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 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); +} + +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; – for(i=0;iconfig[0].bNumInterfaces;i++) – { – int x; + cnt = scnt; + if (AFMT_ISSTEREO(ifmt)) + cnt endpoint[i]; + case AFMT_U16_LE: + for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i > 1; + } + cnt = scnt; + if (AFMT_ISSTEREO(ofmt)) + cnt > 8) ^ 0x80; + break; + + case AFMT_S8: + for (i = 0; i > 8; + break; + + case AFMT_U16_LE: + for (i = 0; i > 8) ^ 0x80; + } + break; + + case AFMT_U16_BE: + for (i = 0; i > 8) ^ 0x80; + } + break; – if(interface->bInterfaceClass != 1) – continue; + case AFMT_S16_LE: + for (i = 0; i > 8; + } + break; – printk(KERN_INFO “USB audio device detected.\n”); + case AFMT_S16_BE: + for (i = 0; i > 8; + } + break; + } – switch(interface->bInterfaceSubClass) – { – case 0x01: – printk(KERN_INFO “audio: Control device.\n”); – break; – case 0x02: – printk(KERN_INFO “audio: streaming.\n”); – break; – case 0x03: – printk(KERN_INFO “audio: nonstreaming.\n”); – 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 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 iso_frame_desc[i].length = maxsize; + urb->iso_frame_desc[i].offset = offs; + } + return 0; +} + +/* + * 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; + + ufmtsh = AFMT_BYTESSHIFT(u->format); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + for (i = 0; i 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 dma.mapped) { + dmafree = u->dma.dmasize – u->dma.count; + if (cnt > dmafree) { + scnt = dmafree >> dfmtsh; + cnt = scnt 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; +} + +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 (as->remove_pending) + return; + 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); +} + +/* + * 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; – if(na==0) – return -1; + for (i = offs = 0; i 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; +} + +/* + * 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 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_audiodev *as = (struct usb_audiodev *)urb->context; + struct usbin *u = &as->usbin; + unsigned long flags; + unsigned int mask; + int suret = USB_ST_NOERROR; – aud = kmalloc(sizeof(struct usb_audio), GFP_KERNEL); – if(aud) – { – memset(aud, 0, sizeof(*aud)); – aud->dev = dev; – dev->private = aud; +#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 (as->remove_pending) + return; – endpoint = &interface->endpoint[0]; + 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); + } + spin_unlock_irqrestore(&as->lock, flags); +} -// 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_audio_irq, – endpoint->bInterval, – aud); +static int usbin_start(struct usb_audiodev *as) +{ + struct usb_device *dev = as->state->usbdev; + struct usbin *u = &as->usbin; + purb_t urb; + unsigned long flags; + unsigned int maxsze, bufsz; – list_add(&aud->list, &usb_audio_list); +#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 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); + } + 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 usb_audio_disconnect(struct usb_device *dev) +static void usbout_stop(struct usb_audiodev *as) { – struct usb_audio *aud = (struct usb_audio*) dev->private; – if(aud) – { – dev->private = NULL; – list_del(&aud->list); – kfree(aud); + 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; + } } – printk(KERN_INFO “USB audio driver removed.\n”); + 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; } -int usb_audio_init(void) +static inline void usbout_release(struct usb_audiodev *as) { – usb_register(&usb_audio_driver); + 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 dma.format, buffer, u->format, tmp.b, scnt); + buffer += scnt transfer_buffer; + + ufmtsh = AFMT_BYTESSHIFT(u->format); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + for (i = offs = 0; i 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 dma.mapped) { + if (cnt > u->dma.count) { + scnt = u->dma.count >> dfmtsh; + cnt = scnt 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 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 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 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 */ – -void usb_audio_interface(struct usb_interface_descriptor *interface, u8 *data) +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 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 iso_frame_desc[i].actual_length); + continue; + } + f = cp[0] | (cp[1] 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 (as->remove_pending) + return; + 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 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); + } + 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 find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt) +{ + unsigned int i; + + /* first find an exact match */ + for (i = 0; i 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 fmtnr, ep; + unsigned char data[3]; + int ret; + + if (u->interface interface >= config->bNumInterfaces) + return 0; + iface = &config->interface[u->interface]; + fmtnr = find_format(as->fmtin, as->numfmtin, d->format); + 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 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 sratelo) + d->srate = fmt->sratelo; + if (d->srate > fmt->sratehi) + d->srate = fmt->sratehi; + if (usb_set_interface(dev, alts->bInterfaceNumber, fmt->altsetting) 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 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 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 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] srate = data[0] | (data[1] 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 fmtnr, ep; + unsigned char data[3]; + int ret; + + if (u->interface interface >= config->bNumInterfaces) + return 0; + iface = &config->interface[u->interface]; + fmtnr = find_format(as->fmtout, as->numfmtout, d->format); + 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) { + + 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); + + if (alts->bNumEndpoints 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; + } + + printk(KERN_DEBUG “datapipe 0x%x syncpipe 0x%x\n”, u->datapipe, u->syncpipe); + + + if (d->srate sratelo) + d->srate = fmt->sratelo; + if (d->srate > fmt->sratehi) + d->srate = fmt->sratehi; + if (usb_set_interface(dev, u->interface, fmt->altsetting) 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 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 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 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] srate = data[0] | (data[1] 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 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 iface | (ch->unitid 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)) flags & MIXFLG_STEREOOUT)), + ms->iface | (ch->unitid > 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector chnum, ms->iface | (ch->unitid chnum == 0) + 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 chnum + 1), ms->iface | (ch->unitid > 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector chnum, ms->iface | (ch->unitid chnum == 0) + 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 chnum + 1), ms->iface | (ch->unitid devnum, ms->iface, ch->unitid, ch->chnum, ch->selector); + return -1; +} + +/* ——————————————————————— */ + +/* + * 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++; + + MOD_INC_USE_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); + MOD_DEC_USE_COUNT; + 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 */ + /* don’t know how to handle this yet */ + return put_user(0, (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i numch; i++) + val |= 1 ch[i].osschannel; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + /* don’t know how to handle this yet */ + return put_user(0, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i numch; i++) + if (ms->ch[i].flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) + val |= 1 ch[i].osschannel; + return put_user(val, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(0, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + for (j = 0; j 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); + /* set recording source: val */ + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + for (j = 0; j 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 = { + &usb_audio_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &usb_audio_ioctl_mixdev, + NULL, /* mmap */ + &usb_audio_open_mixdev, + NULL, /* flush */ + &usb_audio_release_mixdev, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* lock */ +}; + +/* ——————————————————————— */ + +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; + 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 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 lock, flags); + if (cnt > count) + cnt = count; + if (cnt 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; + 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; + 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%x\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 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 lock, flags); + if (cnt > count) + cnt = count; + if (cnt 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 (usbout_start(as)) { + if (!ret) + ret = -ENODEV; + break; + } + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&as->usbout.dma.wait, &wait); + return ret; +} + +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; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,22) + if (vma->vm_pgoff != 0) + return -EINVAL; +#endif + 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 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 usbin.dma.ossfragshift = 4; + if (as->usbin.dma.ossfragshift > 15) + as->usbin.dma.ossfragshift = 15; + if (as->usbin.dma.ossmaxfrags 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 usbout.dma.ossfragshift = 4; + if (as->usbout.dma.ossfragshift > 15) + as->usbout.dma.ossfragshift = 15; + if (as->usbout.dma.ossmaxfrags 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++; + as->remove_pending=0; + MOD_INC_USE_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); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations usb_audio_fops = { + &usb_audio_llseek, + &usb_audio_read, + &usb_audio_write, + NULL, /* readdir */ + &usb_audio_poll, + &usb_audio_ioctl, + &usb_audio_mmap, + &usb_audio_open, + NULL, /* flush */ + &usb_audio_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* lock */ +}; + +/* ——————————————————————— */ + +/* + * TO DO in order to get to the point of building an OSS interface + * structure, let alone playing music.. + * + * Use kmalloc/kfree for the descriptors we build + * Write the descriptor->OSS convertor code + * Figure how we deal with mixers + * Check alternate configurations. For now assume we will find one + * zero bandwidth (idle) config and one or more live one pers interface. + */ + +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, + /*{ NULL, NULL }, */ LIST_HEAD_INIT(usb_audio_driver.driver_list), + NULL, + 0 +}; + + +#if 0 +static int usb_audio_irq(int state, void *buffer, int len, void *dev_id) +{ +#if 0 + struct usb_audio_device *aud = (struct usb_audio_device *)dev_id; + + printk(KERN_DEBUG “irq on %p\n”, aud); +#endif + + return 1; +} +#endif + +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) + return NULL; + if (p[1] == USB_DT_INTERFACE) { + /* minimum length of interface descriptor */ + if (p[0] 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] 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 num_altsetting; i++) { + alts = &iface->altsetting[i]; + if (alts->bInterfaceClass != USB_CLASS_AUDIO || alts->bInterfaceSubClass != 2) + continue; + if (alts->bNumEndpoints 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] 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] devnum, asifin, i); + continue; + } + if (fmt[4] 2 || 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] 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] 0; j–) { + k = fmt[8+3*j] | (fmt[9+3*j] fp->sratehi) + fp->sratehi = k; + if (k 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 num_altsetting; i++) { + alts = &iface->altsetting[i]; + if (alts->bInterfaceClass != USB_CLASS_AUDIO || alts->bInterfaceSubClass != 2) + continue; + if (alts->bNumEndpoints 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] 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] devnum, asifout, i); + continue; + } + if (fmt[4] 2 || 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] 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] 0; j–) { + k = fmt[8+3*j] | (fmt[9+3*j] fp->sratehi) + fp->sratehi = k; + if (k 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)) 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 mixch[state->nrmixch++]; + c->osschannel = nr; + state->mixchmask &= ~(1 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) == 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 mixchmask & SOUND_MASK_RADIO)) + return SOUND_MIXER_RADIO; + if (state->termtype >= 0x709 && state->termtype 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 ctrlif | (ch->unitid minval = buf[0] | (buf[1] chnum ctrlif | (ch->unitid maxval = buf[0] | (buf[1] 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 ctrlif | (ch->unitid 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)) flags & MIXFLG_STEREOOUT)), + state->ctrlif | (ch->unitid minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + } + ch->value |= v3 selector chnum, state->ctrlif | (ch->unitid minval = buf[0] | (buf[1] selector chnum, state->ctrlif | (ch->unitid maxval = buf[0] | (buf[1] selector chnum, state->ctrlif | (ch->unitid 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->chnum != 0) { + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector chnum + 1), state->ctrlif | (ch->unitid minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + } + ch->value |= v3 selector chnum, state->ctrlif | (ch->unitid minval = buf[0] selector chnum, state->ctrlif | (ch->unitid maxval = buf[0] selector chnum, state->ctrlif | (ch->unitid 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->chnum != 0) { + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector chnum + 1), state->ctrlif | (ch->unitid minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + } + ch->value |= v3 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 nrchannels; + termt[i] = state->termtype; + } + state->termtype = 0; + state->chconfig = mixer[6+mixer[4]] | (mixer[7+mixer[4]] > 3; + bmap += bmapsize – 1; + if (mixer[0] 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 void usb_audio_selectorunit(struct consmixstate *state, unsigned char *selector) +{ + unsigned int chnum, i; + + if (!selector[4]) { + printk(KERN_ERR “usbaudio: unit %u invalid SELECTOR_UNIT descriptor\n”, selector[3]); + return; + } + usb_audio_recurseunit(state, selector[5]); + chnum = state->nrchannels; + for (i = 1; i 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; + } + } + 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 nrchannels = proc[7+proc[6]]; + state->termtype = 0; + state->chconfig = proc[8+proc[6]] | (proc[9+proc[6]] s->usbdev; + struct mixerchannel *ch; + unsigned short chftr, mchftr; + unsigned char data[1]; + + 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 (ftr[0] 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 = MIXFLG_STEREOIN | MIXFLG_STEREOOUT; + 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 = MIXFLG_STEREOIN | MIXFLG_STEREOOUT; + 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 = MIXFLG_STEREOIN | MIXFLG_STEREOOUT; + 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 ctrlif | (ftr[3] 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_ERR “usbaudio: mixer path recursion detected, 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] nrchannels = p1[7]; + state->termtype = p1[4] | (p1[5] chconfig = p1[8] | (p1[9] termtype; + else if (j != state->termtype) + j = 0; + } + state->nrchannels = p1[7+p1[6]]; + state->chconfig = p1[8+p1[6]] | (p1[9+p1[6]] 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] 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)) 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, 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] 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 = 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 altsetting[0].bNumEndpoints > 0) { + printk(KERN_ERR “usbaudio: device %d audiocontrol interface %u altsetting 0 not zero bandwidth\n”, + dev->devnum, ctrlif); + continue; + } + if (iface->altsetting[1].bNumEndpoints 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 devnum, ctrlif, numifin, numifout); + for (i = 0; i = 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) { + printk(KERN_DEBUG “usbaudio: vendor id 0x%04x, product id 0x%04x contains no AudioControl interface\n”, + dev->descriptor.idVendor, dev->descriptor.idProduct); + return NULL; + } + /* + * audiocontrol interface found + * find which configuration number is active + */ + for (i = 0; i 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) bConfigurationValue); + return NULL; + } + ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buf, 8); + if (retdevnum); + return NULL; + } + if (buf[1] != USB_DT_CONFIG || buf[0] devnum); + return NULL; + } + buflen = buf[2] | (buf[3] 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; + } +#if 0 + if(aud->irq_handle) + usb_release_irq(dev, aud->irq_handle, aud->irqpipe); + aud->irq_handle = NULL; +#endif + release(s); + wake_up(&open_wait); +} + +int usb_audio_init(void) +{ + usb_register(&usb_audio_driver); + return 0; +} + +#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 -urN linux/drivers/usb/audio.h linux.usb/drivers/usb/audio.h — linux/drivers/usb/audio.h Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/audio.h Tue Jan 25 00:08:39 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 8 + +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 -urN linux/drivers/usb/cpia.c linux.usb/drivers/usb/cpia.c — linux/drivers/usb/cpia.c Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/cpia.c Tue Jan 25 00:08:39 2000 @@ -0,0 +1,1376 @@ +/* + * USB CPiA Video Camera driver + * + * Supports CPiA based Video Cameras. Many manufacturers use this chipset. + * There’s a good chance, if you have a USB video camera, it’s a CPiA based + * one. + * + * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Randy Dunlap + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include “usb.h” +#include “cpia.h” + +static int debug = 0; +MODULE_PARM(debug, “i”); + +/* 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 MAX_FRAME_SIZE (384 * 288 * 3) + +/*******************************/ +/* Memory management functions */ +/*******************************/ + +#define MDEBUG(x) do { } while(0) /* Debug memory management */ + +static struct usb_driver cpia_driver; + +/* 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(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); +} + +static int usb_cpia_get_version(struct usb_device *dev, void *buf) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_CPIA_GET_VERSION, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, buf, 4, HZ); +} + +#ifdef NOTUSED +static int usb_cpia_get_pnp_id(struct usb_device *dev, void *buf) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_CPIA_GET_PNP_ID, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, buf, 6, HZ); +} +#endif + +#ifdef NOTUSED +static int usb_cpia_get_camera_status(struct usb_device *dev, void *buf) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_CPIA_GET_CAMERA_STATUS, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, buf, 8, HZ); +} +#endif + +static int usb_cpia_goto_hi_power(struct usb_device *dev) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_GOTO_HI_POWER, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, NULL, 0, HZ); +} + +static int usb_cpia_get_vp_version(struct usb_device *dev, void *buf) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_CPIA_GET_VP_VERSION, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, buf, 4, HZ); +} + +static int usb_cpia_set_sensor_fps(struct usb_device *dev, int sensorbaserate, int sensorclkdivisor) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_SET_SENSOR_FPS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (sensorbaserate scratchlen – (int)((char *)x – (char *)cpia->scratch)) + +static void cpia_parse_data(struct usb_cpia *cpia) +{ + struct cpia_frame *frame, *pframe; + unsigned char *data = cpia->scratch; + unsigned long left; + long copylen = 0; + + /* Grab the current frame and the previous frame */ + frame = &cpia->frame[cpia->curframe]; + pframe = &cpia->frame[(cpia->curframe – 1 + CPIA_NUMFRAMES) % CPIA_NUMFRAMES]; + + while (1) { + if (!scratch_left(data)) + goto out; + + switch (frame->scanstate) { + case STATE_SCANNING: + { + struct cpia_frame_header *header; + + /* We need at least 2 bytes for the magic value */ + if (scratch_left(data) magic) == CPIA_MAGIC) { + frame->scanstate = STATE_HEADER; + break; + } + + /* Woops, lost the header, find the end of the frame */ + if (scratch_left(data) = 4) { + if (*((__u32 *)data) == 0xFFFFFFFF) { + data += 4; + if (debug >= 1) + printk(KERN_INFO “cpia: EOF while scanning for magic\n”); + goto error; + } + data++; + } + break; + } + case STATE_HEADER: + /* We need at least 64 bytes for the header */ + if (scratch_left(data) header, data, + sizeof(struct cpia_frame_header)); + + /* Skip over the header */ + data += sizeof(struct cpia_frame_header); + + frame->hdrwidth = (frame->header.col_end – + frame->header.col_start) * 8; + frame->hdrheight = (frame->header.row_end – + frame->header.row_start) * 4; + if (debug >= 2) { + printk(KERN_DEBUG “cpia: frame size %dx%d\n”, + frame->hdrwidth, frame->hdrheight); + printk(KERN_DEBUG “cpia: frame %scompressed\n”, + frame->header.comp_enable ? “” : “not “); + } + + frame->scanstate = STATE_LINES; + frame->curline = 0; + break; + + case STATE_LINES: + { + unsigned char *f, *end; + unsigned int len; + int i; + int y, u, y1, v, r, g, b; + + /* We want at least 2 bytes for the length */ + if (scratch_left(data) (frame->hdrwidth * 2) + 1) { + if (debug >= 1) + printk(KERN_DEBUG “cpia: bad length, resynching (expected %d, got %d)\n”, (frame->hdrwidth * 2) + 1, len); + goto error; + } + + /* Make sure there’s enough data for the entire line */ + if (scratch_left(data + 2) = 1) + printk(KERN_DEBUG “cpia: lost synch\n”); + goto error; + } + + /* Start at the beginning */ + end = data + len – 1; + + f = frame->data + (frame->width * 3 * frame->curline); + + if (frame->header.comp_enable) { + unsigned char *fp; + + /* We use the previous frame as a reference */ + fp = pframe->data + + (frame->width * 3 * frame->curline); + + while (data > 1; + memcpy(f, fp, i * 3); + copylen += (i * 3); + f += (i * 3); + fp += (i * 3); + data++; + } else { + /* Raw data */ + +#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)>16) + +y = *data++ – 16; +u = *data++ – 128; +y1 = *data++ – 16; +v = *data++ – 128; +r = 104635 * v; +g = -25690 * u + -53294 * v; +b = 132278 * u; +y *= 76310; +y1 *= 76310; +*f++ = LIMIT(b + y); *f++ = LIMIT(g + y); *f++ = LIMIT(r + y); +*f++ = LIMIT(b + y1); *f++ = LIMIT(g + y1); *f++ = LIMIT(r + y1); + fp += 6; + copylen += 6; + } + } + } else { + /* Raw data */ + while (data curline >= frame->hdrheight) + goto nextframe; + + break; + } /* end case STATE_LINES */ + } /* end switch (scanstate) */ + } /* end while (1) */ + +nextframe: + if (debug >= 1) + printk(KERN_DEBUG “cpia: marking as success\n”); + + if (scratch_left(data) >= 4 && *((__u32 *)data) == 0xFFFFFFFF) + data += 4; + + frame->grabstate = FRAME_DONE; + + goto wakeup; + +error: + if (debug >= 1) + printk(KERN_DEBUG “cpia: marking as error\n”); + + frame->grabstate = FRAME_ERROR; + + /* Get a fresh frame since this frame may have been important */ + cpia->compress = 0; + + copylen = 0; + +wakeup: + + cpia->curframe = -1; + + /* This will cause the process to request another frame. */ + if (waitqueue_active(&frame->wq)) + wake_up_interruptible(&frame->wq); + +out: + /* Grab the remaining */ + left = scratch_left(data); + memmove(cpia->scratch, data, left); + cpia->scratchlen = left; + + /* Update the frame’s uncompressed length. */ + frame->scanlength += copylen; +} + +/* + * Make all of the blocks of data contiguous + */ +static int cpia_compress_isochronous(struct usb_cpia *cpia, urb_t *urb) +{ + unsigned char *cdata, *data; + int i, totlen = 0; + + data = cpia->scratch + cpia->scratchlen; + for (i = 0; i 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 && debug >= 1) + printk(KERN_DEBUG “cpia data error: [%d] len=%d, status=%X\n”, + i, n, st); + + if ((cpia->scratchlen + n) > SCRATCH_BUF_SIZE) { + printk(KERN_DEBUG “cpia: scratch buf overflow!scr_len: %d, n: %d\n”,cpia->scratchlen, n ); + return totlen; + } + + if (n) { + memmove(data, cdata, n); + data += n; + totlen += n; + cpia->scratchlen += n; + } + } + + return totlen; +} + +static void cpia_isoc_irq(struct urb *urb) +{ + int len; + struct usb_cpia *cpia = urb->context; + struct cpia_sbuf *sbuf; + int i; + +#if 0 +printk(“cpia_isoc_irq: %p status %d, errcount = %d, length = %d\n”, urb, urb->status, urb->error_count, urb->actual_length); +#endif + + if (!cpia->streaming) { + if (debug >= 1) + printk(KERN_DEBUG “cpia: oops, not streaming, but interrupt\n”); + return; + } + + sbuf = &cpia->sbuf[cpia->cursbuf]; + // usb_kill_isoc(sbuf->isodesc); + + /* Copy the data received into our scratch buffer */ + len = cpia_compress_isochronous(cpia, urb); + + /* If we don’t have a frame we’re current working on, complain */ + if (cpia->scratchlen) { + if (cpia->curframe = 1) + printk(KERN_DEBUG “cpia: received data, but no frame available\n”); + } else + cpia_parse_data(cpia); + } + + for (i = 0; i urb->iso_frame_desc[i].status = 0; + sbuf->urb->iso_frame_desc[i].actual_length = 0; + } + /* Move to the next sbuf */ + cpia->cursbuf = (cpia->cursbuf + 1) % CPIA_NUMSBUF; + + /* Reschedule this block of Isochronous desc */ + // usb_run_isoc(sbuf->isodesc, cpia->sbuf[cpia->cursbuf].isodesc); + + return; +} + +static int cpia_init_isoc(struct usb_cpia *cpia) +{ + struct usb_device *dev = cpia->dev; + urb_t *urb; + int fx, err; + + cpia->compress = 0; + cpia->curframe = -1; + cpia->cursbuf = 0; + cpia->scratchlen = 0; + + /* Alternate interface 3 is is the biggest frame size */ + if (usb_set_interface(cpia->dev, cpia->iface, 3) sbuf[0].isodesc); + urb = usb_alloc_urb(FRAMES_PER_DESC); + + if (!urb) { + printk(KERN_ERR “cpia_init_isoc: usb_init_isoc ret %d\n”, + 0); + return -ENOMEM; + } + cpia->sbuf[0].urb = urb; + urb->dev = dev; + urb->context = cpia; + urb->pipe = usb_rcvisocpipe(dev, 1); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = cpia->sbuf[0].data; + urb->complete = cpia_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx 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_init_isoc ret %d\n”, + 0); + return -ENOMEM; + } + cpia->sbuf[1].urb = urb; + urb->dev = dev; + urb->context = cpia; + urb->pipe = usb_rcvisocpipe(dev, 1); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = cpia->sbuf[1].data; + urb->complete = cpia_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + + cpia->sbuf[1].urb->next = cpia->sbuf[0].urb; + cpia->sbuf[0].urb->next = cpia->sbuf[1].urb; + + err = usb_submit_urb(cpia->sbuf[0].urb); + if (err) + printk(KERN_ERR “cpia_init_isoc: usb_run_isoc(0) ret %d\n”, + err); + err = usb_submit_urb(cpia->sbuf[1].urb); + if (err) + printk(KERN_ERR “cpia_init_isoc: usb_run_isoc(1) ret %d\n”, + err); + + cpia->streaming = 1; + + return 0; +} + +static void cpia_stop_isoc(struct usb_cpia *cpia) +{ + if (!cpia->streaming) + return; + + /* Turn off continuous grab */ + if (usb_cpia_set_grab_mode(cpia->dev, 0) dev, cpia->iface, 0) sbuf[1].urb); + usb_unlink_urb(cpia->sbuf[0].urb); + + cpia->streaming = 0; + + /* Delete them all */ + usb_free_urb(cpia->sbuf[1].urb); + usb_free_urb(cpia->sbuf[0].urb); +} + +static int cpia_new_frame(struct usb_cpia *cpia, int framenum) +{ + struct cpia_frame *frame; + int 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 (cpia->curframe == -1) { + if (cpia->frame[(framenum – 1 + CPIA_NUMFRAMES) % CPIA_NUMFRAMES].grabstate == FRAME_READY) + framenum = (framenum – 1 + CPIA_NUMFRAMES) % CPIA_NUMFRAMES; + } else + return 0; + + frame = &cpia->frame[framenum]; + width = frame->width; + height = frame->height; + + frame->grabstate = FRAME_GRABBING; + frame->scanstate = STATE_SCANNING; + frame->scanlength = 0; /* accumulated in cpia_parse_data() */ + + cpia->curframe = framenum; + + /* Make sure it’s not too big */ + if (width > 352) + width = 352; + width = (width / 8) * 8; /* Multiple of 8 */ + + if (height > 288) + height = 288; + height = (height / 4) * 4; /* Multiple of 4 */ + + /* Set the ROI they want */ + if (usb_cpia_set_roi(cpia->dev, 0, width / 8, 0, height / 4) dev, cpia->compress ? + COMP_AUTO : COMP_DISABLED, DONT_DECIMATE) compress = (cpia->compress + 1) % 30; + + /* Grab the frame */ + if (usb_cpia_upload_frame(cpia->dev, WAIT_FOR_NEXT_FRAME) lock); + if (cpia->user) + goto out_unlock; + + cpia->frame[0].grabstate = FRAME_UNUSED; + cpia->frame[1].grabstate = FRAME_UNUSED; + + err = -ENOMEM; + + /* Allocate memory for the frame buffers */ + cpia->fbuf = rvmalloc(2 * MAX_FRAME_SIZE); + if (!cpia->fbuf) + goto open_err_ret; + + cpia->frame[0].data = cpia->fbuf; + cpia->frame[1].data = cpia->fbuf + MAX_FRAME_SIZE; + + cpia->sbuf[0].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!cpia->sbuf[0].data) + goto open_err_on0; + + cpia->sbuf[1].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!cpia->sbuf[1].data) + goto open_err_on1; + + /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used + * (using read() instead). */ + cpia->frame[0].width = 352; + cpia->frame[0].height = 288; + cpia->frame[0].bytes_read = 0; + cpia->frame[1].width = 352; + cpia->frame[1].height = 288; + cpia->frame[1].bytes_read = 0; + + err = cpia_init_isoc(cpia); + if (err) + goto open_err_on2; + + cpia->user++; + up(&cpia->lock); + + MOD_INC_USE_COUNT; + + return 0; + +open_err_on2: + kfree (cpia->sbuf[1].data); +open_err_on1: + kfree (cpia->sbuf[0].data); +open_err_on0: + rvfree(cpia->fbuf, 2 * MAX_FRAME_SIZE); +open_err_ret: + return err; + +out_unlock: + up(&cpia->lock); + return err; +} + +static void cpia_close(struct video_device *dev) +{ + struct usb_cpia *cpia = (struct usb_cpia *)dev; + + down(&cpia->lock); + cpia->user–; + + MOD_DEC_USE_COUNT; + + cpia_stop_isoc(cpia); + + rvfree(cpia->fbuf, 2 * MAX_FRAME_SIZE); + + kfree(cpia->sbuf[1].data); + kfree(cpia->sbuf[0].data); + + up(&cpia->lock); +} + +static int cpia_init_done(struct video_device *dev) +{ + return 0; +} + +static long cpia_write(struct video_device *dev, const char *buf, unsigned long count, int noblock) +{ + return -EINVAL; +} + +static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct usb_cpia *cpia = (struct usb_cpia *)dev; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability b; + + strcpy(b.name, “CPiA USB Camera”); + b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; + b.channels = 1; + b.audios = 0; + b.maxwidth = 352; /* CIF */ + b.maxheight = 288; /* ” */ + b.minwidth = 8; + b.minheight = 4; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v.channel != 0) + return -EINVAL; + + v.flags = 0; + v.tuners = 0; + v.type = VIDEO_TYPE_CAMERA; + strcpy(v.name, “Camera”); + + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + + return 0; + } + case VIDIOCSCHAN: + { + int v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + if (v != 0) + return -EINVAL; + + return 0; + } + case VIDIOCGPICT: + { + struct video_picture p; + + p.colour = 0x8000; /* Damn British people 🙂 */ + p.hue = 0x8000; + p.brightness = 180 compress = 0; + + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + + vw.x = 0; + vw.y = 0; + vw.width = 352; + vw.height = 288; + vw.chromakey = 0; + vw.flags = 30; /* 30 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 (cpia->frame[vm.frame].grabstate == FRAME_GRABBING) + return -EBUSY; + + /* Don’t compress if the size changed */ + if ((cpia->frame[vm.frame].width != vm.width) || + (cpia->frame[vm.frame].height != vm.height)) + cpia->compress = 0; + + cpia->frame[vm.frame].width = vm.width; + cpia->frame[vm.frame].height = vm.height; + + /* Mark it as ready */ + cpia->frame[vm.frame].grabstate = FRAME_READY; + + return cpia_new_frame(cpia, vm.frame); + } + case VIDIOCSYNC: + { + int frame; + + if (copy_from_user((void *)&frame, arg, sizeof(int))) + return -EFAULT; + + if (debug >= 1) + printk(KERN_DEBUG “cpia: syncing to frame %d\n”, frame); + + switch (cpia->frame[frame].grabstate) { + case FRAME_UNUSED: + return -EINVAL; + case FRAME_READY: + case FRAME_GRABBING: + case FRAME_ERROR: +redo: + do { +#if 0 + init_waitqueue_head(&cpia->frame[frame].wq); +#endif + interruptible_sleep_on(&cpia->frame[frame].wq); + if (signal_pending(current)) + return -EINTR; + } while (cpia->frame[frame].grabstate == FRAME_GRABBING); + + if (cpia->frame[frame].grabstate == FRAME_ERROR) { + int ret; + + if ((ret = cpia_new_frame(cpia, frame)) frame[frame].grabstate = FRAME_UNUSED; + break; + } + + cpia->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: + return -EINVAL; + case VIDIOCGTUNER: + case VIDIOCSTUNER: + return -EINVAL; + case VIDIOCGFREQ: + case VIDIOCSFREQ: + return -EINVAL; + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static long cpia_read(struct video_device *dev, char *buf, unsigned long count, int noblock) +{ + struct usb_cpia *cpia = (struct usb_cpia *)dev; + int frmx = -1; + volatile struct cpia_frame *frame; + + if (debug >= 1) + printk(KERN_DEBUG “cpia_read: %ld bytes, noblock=%d\n”, count, noblock); + + if (!dev || !buf) + return -EFAULT; + + /* See if a frame is completed, then use it. */ + if (cpia->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */ + frmx = 0; + else if (cpia->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 (cpia->frame[0].grabstate == FRAME_GRABBING) + frmx = 0; + else if (cpia->frame[1].grabstate == FRAME_GRABBING) + frmx = 1; + } + + /* If no frame is active, start one. */ + if (frmx == -1) + cpia_new_frame(cpia, frmx = 0); + + frame = &cpia->frame[frmx]; + +restart: + while (frame->grabstate == FRAME_GRABBING) { + interruptible_sleep_on(&frame->wq); + if (signal_pending(current)) + return -EINTR; + } + + if (frame->grabstate == FRAME_ERROR) { + frame->bytes_read = 0; +printk(“cpia_read: errored frame %d\n”, cpia->curframe); + if (cpia_new_frame(cpia, frmx)) + printk(KERN_ERR “cpia_read: cpia_new_frame error\n”); + goto restart; + } + + if (debug >= 1) + printk(KERN_DEBUG “cpia_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 “cpia_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. */ + cpia->frame[frmx].grabstate = FRAME_UNUSED; + if (cpia_new_frame(cpia, frmx ? 0 : 1)) + printk(KERN_ERR “cpia_read: cpia_new_frame returned error\n”); + } + + return count; +} + +static int cpia_mmap(struct video_device *dev, const char *adr, unsigned long size) +{ + struct usb_cpia *cpia = (struct usb_cpia *)dev; + unsigned long start = (unsigned long)adr; + unsigned long page, pos; + + if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE – 1) & ~(PAGE_SIZE – 1))) + return -EINVAL; + + pos = (unsigned long)cpia->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 cpia_template = { + “CPiA USB Camera”, + VID_TYPE_CAPTURE, + VID_HARDWARE_CPIA, + cpia_open, + cpia_close, + cpia_read, + cpia_write, + NULL, + cpia_ioctl, + cpia_mmap, + cpia_init_done, + NULL, + 0, + 0 +}; + +static int usb_cpia_configure(struct usb_cpia *cpia) +{ + struct usb_device *dev = cpia->dev; + unsigned char version[4]; + + /* Set altsetting 0 */ + if (usb_set_interface(dev, cpia->iface, 0) = 1) + printk(KERN_DEBUG “cpia: Firmware v%d.%d, VC Hardware v%d.%d\n”, + version[0], version[1], version[2], version[3]); + + memcpy(&cpia->vdev, &cpia_template, sizeof(cpia_template)); + + init_waitqueue_head(&cpia->frame[0].wq); + init_waitqueue_head(&cpia->frame[1].wq); + + if (video_register_device(&cpia->vdev, VFL_TYPE_GRABBER) == -1) { + printk(KERN_ERR “video_register_device failed\n”); + return -EBUSY; + } + + if (usb_cpia_goto_hi_power(dev) = 1) { + printk(KERN_DEBUG “cpia: VP v%d rev %d\n”, version[0], version[1]); + printk(KERN_DEBUG “cpia: Camera Head ID %04X\n”, (version[3] compress = 0; + + return 0; + +error: + video_unregister_device(&cpia->vdev); + usb_driver_release_interface(&cpia_driver, + &dev->actconfig->interface[0]); + + kfree(cpia); + + return -EBUSY; +} + +static void * cpia_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_interface_descriptor *interface; + struct usb_cpia *cpia; + + /* We don’t handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return NULL; + + interface = &dev->actconfig->interface[ifnum].altsetting[0]; + + /* Is it a CPiA? */ + if (dev->descriptor.idVendor != 0x0553) + return NULL; + if (dev->descriptor.idProduct != 0x0002) + return NULL; + + /* Checking vendor/product should be enough, but what the hell */ + if (interface->bInterfaceClass != 0xFF) + return NULL; + if (interface->bInterfaceSubClass != 0x00) + return NULL; + + /* We found a CPiA */ + printk(KERN_INFO “USB CPiA camera found\n”); + + if ((cpia = kmalloc(sizeof(*cpia), GFP_KERNEL)) == NULL) { + printk(KERN_ERR “couldn’t kmalloc cpia struct\n”); + return NULL; + } + + memset(cpia, 0, sizeof(*cpia)); + + cpia->dev = dev; + cpia->iface = interface->bInterfaceNumber; + + if (!usb_cpia_configure(cpia)) { + cpia->user=0; + init_MUTEX(&cpia->lock); /* to 1 == available */ + return cpia; + } else return NULL; +} + +static void cpia_disconnect(struct usb_device *dev, void *ptr) +{ + struct usb_cpia *cpia = (struct usb_cpia *) ptr; + + video_unregister_device(&cpia->vdev); + + usb_driver_release_interface(&cpia_driver, + &cpia->dev->actconfig->interface[0]); + + /* Free the memory */ + kfree(cpia); +} + +static struct usb_driver cpia_driver = { + “cpia”, + cpia_probe, + cpia_disconnect, + { NULL, NULL } +}; + +int usb_cpia_init(void) +{ + return usb_register(&cpia_driver); +} + +void usb_cpia_cleanup(void) +{ + usb_deregister(&cpia_driver); +} + +#ifdef MODULE +int init_module(void) +{ + return usb_cpia_init(); +} + +void cleanup_module(void) +{ + usb_cpia_cleanup(); +} +#endif diff -urN linux/drivers/usb/cpia.h linux.usb/drivers/usb/cpia.h — linux/drivers/usb/cpia.h Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/cpia.h Tue Jan 25 00:08:39 2000 @@ -0,0 +1,205 @@ +#ifndef __LINUX_CPIA_H +#define __LINUX_CPIA_H + +#include + +#define USB_REQ_CPIA_GET_VERSION 0x01 +#define USB_REQ_CPIA_GET_PNP_ID 0x02 +#define USB_REQ_CPIA_GET_CAMERA_STATUS 0x03 +#define USB_REQ_CPIA_GOTO_HI_POWER 0x04 +#define USB_REQ_CPIA_GOTO_LO_POWER 0x05 +/* No 0x06 */ +#define USB_REQ_CPIA_GOTO_SUSPEND 0x07 +#define USB_REQ_CPIA_GOTO_PASS_THROUGH 0x08 +/* No 0x09 */ +#define USB_REQ_CPIA_MODIFY_CAMERA_STATUS 0x0A + +#define USB_REQ_CPIA_READ_VC_REGS 0x21 +#define USB_REQ_CPIA_WRITE_BC_REG 0x22 +#define USB_REQ_CPIA_READ_MC_PORTS 0x23 +#define USB_REQ_CPIA_WRITE_MC_PORT 0x24 +#define USB_REQ_CPIA_SET_BAUD_RATE 0x25 +#define USB_REQ_CPIA_SET_ECP_TIMING 0x26 +#define USB_REQ_CPIA_READ_IDATA 0x27 +#define USB_REQ_CPIA_WRITE_IDATA 0x28 +#define USB_REQ_CPIA_GENERIC_CALL 0x29 +#define USB_REQ_CPIA_I2CSTART 0x2A +#define USB_REQ_CPIA_I2CSTOP 0x2B +#define USB_REQ_CPIA_I2CWRITE 0x2C +#define USB_REQ_CPIA_I2CREAD 0x2D + +#define USB_REQ_CPIA_GET_VP_VERSION 0xA1 +#define USB_REQ_CPIA_SET_COLOUR_PARAMS 0xA3 +#define USB_REQ_CPIA_SET_EXPOSURE 0xA4 +/* No 0xA5 */ +#define USB_REQ_CPIA_SET_COLOUR_BALANCE 0xA6 +#define USB_REQ_CPIA_SET_SENSOR_FPS 0xA7 +#define USB_REQ_CPIA_SET_VP_DEFAULTS 0xA8 +#define USB_REQ_CPIA_SET_APCOR 0xA9 +#define USB_REQ_CPIA_SET_FLICKER_CTRL 0xAA +#define USB_REQ_CPIA_SET_VL_OFFSET 0xAB + +#define USB_REQ_CPIA_GET_COLOUR_PARAMETERS 0xB0 +#define USB_REQ_CPIA_GET_COLOUR_BALANCE 0xB1 +#define USB_REQ_CPIA_GET_EXPOSURE 0xB2 +#define USB_REQ_CPIA_SET_SENSOR_MATRIX 0xB3 + +#define USB_REQ_CPIA_COLOUR_BARS 0xBD +#define USB_REQ_CPIA_READ_VP_REGS 0xBE +#define USB_REQ_CPIA_WRITE_VP_REGS 0xBF + +#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 USB_REQ_CPIA_SET_GRAB_MODE 0xC3 +#define USB_REQ_CPIA_INIT_STREAM_CAP 0xC4 +#define USB_REQ_CPIA_FINI_STREAM_CAP 0xC5 +#define USB_REQ_CPIA_START_STREAM_CAP 0xC6 +#define USB_REQ_CPIA_END_STREAM_CAP 0xC7 +#define USB_REQ_CPIA_SET_FORMAT 0xC8 +#define FORMAT_QCIF 0 +#define FORMAT_CIF 1 +#define FORMAT_YUYV 0 +#define FORMAT_UYVY 1 +#define FORMAT_420 0 +#define FORMAT_422 1 +#define USB_REQ_CPIA_SET_ROI 0xC9 +#define USB_REQ_CPIA_SET_COMPRESSION 0xCA +#define COMP_DISABLED 0 +#define COMP_AUTO 1 +#define COMP_MANUAL 2 +#define DONT_DECIMATE 0 +#define DECIMATE 1 +#define USB_REQ_CPIA_SET_COMPRESSION_TARGET 0xCB +#define TARGET_QUALITY 0 +#define TARGET_FRAMERATE 1 +#define USB_REQ_CPIA_SET_YUV_THRESH 0xCC +#define USB_REQ_CPIA_SET_COMPRESSION_PARAMS 0xCD +#define USB_REQ_CPIA_DISCARD_FRAME 0xCE + +#define USB_REQ_CPIA_OUTPUT_RS232 0xE1 +#define USB_REQ_CPIA_ABORT_PROCESS 0xE4 +#define USB_REQ_CPIA_SET_DRAM_PAGE 0xE5 +#define USB_REQ_CPIA_START_DRAM_UPLOAD 0xE6 +#define USB_REQ_CPIA_START_DUMMY_STREAM 0xE8 +#define USB_REQ_CPIA_ABORT_STREAM 0xE9 +#define USB_REQ_CPIA_DOWNLOAD_DRAM 0xEA +/* #define USB_REQ_CPIA_NULL_CMD 0x?? */ + +#define STREAM_BUF_SIZE (PAGE_SIZE * 4) +/* #define STREAM_BUF_SIZE (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC) */ + +#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) + +#define FRAMES_PER_DESC 10 +#define FRAME_SIZE_PER_DESC 960 /* Shouldn’t be hardcoded */ + +enum { + STATE_SCANNING, /* Scanning for start */ + STATE_HEADER, /* Parsing header */ + STATE_LINES, /* Parsing lines */ +}; + +#define CPIA_MAGIC 0x1968 +struct cpia_frame_header { + __u16 magic; /* 0 – 1 */ + __u16 timestamp; /* 2 – 3 */ + __u16 unused; /* 4 – 5 */ + __u16 timestamp1; /* 6 – 7 */ + __u8 unused1[8]; /* 8 – 15 */ + __u8 video_size; /* 16 0 = QCIF, 1 = CIF */ + __u8 sub_sample; /* 17 0 = 4:2:0, 1 = 4:2:2 */ + __u8 yuv_order; /* 18 0 = YUYV, 1 = UYVY */ + __u8 unused2[5]; /* 19 – 23 */ + __u8 col_start; /* 24 */ + __u8 col_end; /* 25 */ + __u8 row_start; /* 26 */ + __u8 row_end; /* 27 */ + __u8 comp_enable; /* 28 0 = non compressed, 1 = compressed */ + __u8 decimation; /* 29 0 = no decimation, 1 = decimation */ + __u8 y_thresh; /* 30 */ + __u8 uv_thresh; /* 31 */ + __u8 system_state; /* 32 */ + __u8 grab_state; /* 33 */ + __u8 stream_state; /* 34 */ + __u8 fatal_error; /* 35 */ + __u8 cmd_error; /* 36 */ + __u8 debug_flags; /* 37 */ + __u8 camera_state_7; /* 38 */ + __u8 camera_state_8; /* 39 */ + __u8 cr_achieved; /* 40 */ + __u8 fr_achieved; /* 41 */ + __u8 unused3[22]; /* 42 – 63 */ +}; + +struct usb_device; + +struct cpia_sbuf { + char *data; + urb_t *urb; +}; + +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 cpia_frame { + char *data; /* Frame buffer */ + + struct cpia_frame_header header; /* Header from stream */ + + int width; /* Width application is expecting */ + int height; /* Height */ + + int hdrwidth; /* Width the frame actually is */ + int hdrheight; /* 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 CPIA_NUMFRAMES 2 +#define CPIA_NUMSBUF 2 + +struct usb_cpia { + struct video_device vdev; + + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; + + struct semaphore lock; + int user; /* user count for exclusive use */ + + int streaming; /* Are we streaming Isochronous? */ + int grabbing; /* Are we grabbing? */ + + int compress; /* Should the next frame be compressed? */ + + char *fbuf; /* Videodev buffer area */ + + int curframe; + struct cpia_frame frame[CPIA_NUMFRAMES]; /* Double buffering */ + + int cursbuf; /* Current receiving sbuf */ + struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ + + /* Scratch space from the Isochronous pipe */ + unsigned char scratch[SCRATCH_BUF_SIZE]; + int scratchlen; +}; + +#endif diff -urN linux/drivers/usb/dabfirmware.h linux.usb/drivers/usb/dabfirmware.h — linux/drivers/usb/dabfirmware.h Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/dabfirmware.h Tue Jan 25 00:08:39 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 -urN linux/drivers/usb/dabusb.c linux.usb/drivers/usb/dabusb.c — linux/drivers/usb/dabusb.c Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/dabusb.c Tue Jan 25 00:08:39 2000 @@ -0,0 +1,900 @@ +/*****************************************************************************/ + +/* + * dabusb.c — dab usb driver. + * + * Copyright (C) 1999 Deti Fliegl ([email protected]) + * + * 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: linux-2.2.14-usb.diff,v 1.1 2000/01/28 02:51:18 trini Exp $ + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG +#undef DEBUG_ALL + +#include “usb.h” + +#include “dabusb.h” +#include “dabfirmware.h” +/* ——————————————————————— */ + +#define NRDABUSB 4 + +#if LINUX_VERSION_CODE 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); + if (b->purb) + kfree (b->purb); + tmp = p->next; + list_del (p); + kfree (b); + p = tmp; + } + return 0; +} +/*——————————————————————-*/ +static int dabusb_free_buffers (pdabusb_t s) +{ + dbg(“dabusb_free_buffers”); + dabusb_free_queue (&s->free_buff_list); + dabusb_free_queue (&s->rec_buff_list); + 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; + +#ifdef DEBUG_ALL + dbg(“dabusb_iso_complete”); +#endif + if (purb->status != USB_ST_URB_KILLED) { + unsigned int pipe = usb_rcvisocpipe (purb->dev, _DABUSB_ISOPIPE); + int pipesize = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe)); + for (i = 0; i number_of_packets; i++) + if (purb->iso_frame_desc[i].status == USB_ST_NOERROR) { + len = purb->iso_frame_desc[i].actual_length; + if (len iso_frame_desc[i].offset, len); + dst += len; + } + else + err(“dabusb_iso_complete: invalid len %d”, len); + } + 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; + int len = sizeof (urb_t) + packets * sizeof (iso_packet_descriptor_t); + + dbg(“dabusb_alloc_buffers len:%d pipesize:%d packets:%d transfer_buffer_len:%d”, + len, pipesize, packets, transfer_buffer_length); + + while (buffers total_buffer_size s = s; + b->purb = (purb_t) kmalloc (len, GFP_KERNEL); + if (!b->purb) { + err(“kmalloc(sizeof(urb_t)+packets*sizeof(iso_packet_descriptor_t))==NULL”); + kfree (b); + goto err; + } + memset (b->purb, 0, len); + 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 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_reset_pipe (struct usb_device *usbdev, unsigned int ep) +{ + dbg(“dabusb_reset_pipe”); + if ((ep & ~0x80) >= 16) + return -EINVAL; + + usb_settoggle (usbdev, ep & 0xf, !(ep & 0x80), 0); + + return 0; +} +/* ——————————————————————— */ +static int dabusb_submit_urb (pdabusb_t s, purb_t purb) +{ + int ret; + bulk_completion_context_t context; + + init_waitqueue_head (&context.wait); + purb->context = &context; + +#ifdef DEBUG_ALL + dump_urb(purb); +#endif + + ret = usb_submit_urb (purb); + if (ret status == USB_ST_URB_PENDING) { + err(“dabusb_usb_submit_urb: %p timed out”, purb); + usb_unlink_urb (purb); + dabusb_reset_pipe(purb->dev, purb->pipe); + return -ETIMEDOUT; + } + return purb->status; +} +/* ——————————————————————— */ +static void dabusb_bulk_complete (purb_t purb) +{ + pbulk_completion_context_t context = purb->context; + +#ifdef DEBUG_ALL + dbg(“dabusb_bulk_complete”); + dump_urb(purb); +#endif + wake_up (&context->wait); +} + +/* ——————————————————————— */ +static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb) +{ + int ret; + urb_t urb; + unsigned int pipe; + +#ifdef DEBUG_ALL + dbg(“dabusb_bulk”); +#endif + + if (!pb->pipe) + pipe = usb_rcvbulkpipe (s->usbdev, 2); + else + pipe = usb_sndbulkpipe (s->usbdev, 2); + + memset (&urb, 0, sizeof (urb_t)); + FILL_BULK_URB ((&urb), s->usbdev, pipe, pb->data, pb->size, dabusb_bulk_complete, NULL); + + ret = dabusb_submit_urb (s, &urb); + pb->size = urb.actual_length; + return ret; +} +/* ——————————————————————— */ +static int dabusb_writemem (pdabusb_t s, int pos, unsigned char *data, int len) +{ + int ret; + urb_t urb; + unsigned int pipe; + unsigned char *setup = kmalloc (8, GFP_KERNEL); + unsigned char *transfer_buffer; + + if (!setup) { + err(“dabusb_writemem: kmalloc(8) failed.”); + return -ENOMEM; + } + transfer_buffer = kmalloc (len, GFP_KERNEL); + if (!transfer_buffer) { + err(“dabusb_writemem: kmalloc(%d) failed.”, len); + kfree (setup); + return -ENOMEM; + } + setup[0] = 0x40; + setup[1] = 0xa0; + setup[2] = pos & 0xff; + setup[3] = pos >> 8; + setup[4] = 0; + setup[5] = 0; + setup[6] = len & 0xff; + setup[7] = len >> 8; + + memcpy (transfer_buffer, data, len); + + pipe = usb_sndctrlpipe (s->usbdev, 0); + + memset (&urb, 0, sizeof (urb_t)); + FILL_CONTROL_URB ((&urb), s->usbdev, pipe, setup, transfer_buffer, len, dabusb_bulk_complete, NULL); + + ret = dabusb_submit_urb (s, &urb); + kfree (setup); + kfree (transfer_buffer); + if (ret Type == 0) { +#ifdef DEBUG_ALL + err(“dabusb_writemem: %04X %p %d)”, ptr->Address, ptr->Data, ptr->Length); +#endif + ret = dabusb_writemem (s, ptr->Address, ptr->Data, ptr->Length); + if (ret 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] data[0] = 0x2b; + b->data[1] = 0; + b->data[2] = 0; + b->data[3] = 60; + + for (n = 0; n size = 64; + memcpy (b->data + 4, buf + 74 + n, 60); + ret = dabusb_bulk (s, b); + if (ret 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) usbdev, _DABUSB_ISOPIPE); + 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)) { +#ifdef DEBUG_ALL + dbg(“submitting: end:%p s->rec_buff_list:%p”, s->rec_buff_list.prev, &s->rec_buff_list); +#endif + 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”); + } + else + atomic_inc (&s->pending_io); + } +#ifdef DEBUG_ALL + dbg(“pending_io: %d”,s->pending_io.counter); +#endif + } + 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 ret = 0; + int rem; + int cnt; + pbuff_t b; + purb_t purb = NULL; + +#ifdef DEBUG_ALL + dbg(“dabusb_read”); +#endif + + if (*ppos) + return -ESPIPE; + + if (s->remove_pending) + return -EIO; + + + if (!s->usbdev) + return -EIO; + + while (count > 0) { + dabusb_startrek (s); + if (list_empty (&s->rec_buff_list)) { + err(“error: rec_buf_list is empty”); + goto err; + } + b = list_entry (s->rec_buff_list.next, buff_t, buff_list); + purb = b->purb; + + if (purb->status == USB_ST_URB_PENDING) { + 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; + } + if (list_empty (&s->rec_buff_list)) { + err(“error: still no buffer available.”); + goto err; + } + 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; + +#ifdef DEBUG_ALL + dbg(“copy_to_user:%p %p %d”,buf, purb->transfer_buffer + s->readptr, cnt); +#endif + + 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 + NRDABUSB)) + return -EIO; + + MOD_INC_USE_COUNT; + 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) { + MOD_DEC_USE_COUNT; + return -EBUSY; + } + schedule_timeout (HZ / 2); + + if (signal_pending (current)) { + MOD_DEC_USE_COUNT; + return -EAGAIN; + } + down (&s->mutex); + } + s->opened = 1; + up (&s->mutex); + + if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) 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) remove_ok); + + MOD_DEC_USE_COUNT; + 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; + DECLARE_WAITQUEUE (wait, current); + +// 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 = +{ + dabusb_llseek, + dabusb_read, + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + dabusb_ioctl, + NULL, /* mmap */ + dabusb_open, + NULL, /* flush */ + dabusb_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL /* lock */ +}; + +static int dabusb_find_struct (void) +{ + int u; + + for (u = 0; u 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) descriptor.idProduct == 0x2131) + dabusb_loadmem (s, NULL); + else { + dabusb_fpga_download (s, NULL); + + if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) 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; + + /* initialize struct */ + for (u = 0; u 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); + } + + /* register misc device */ + usb_register (&dabusb_driver); + + 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, [email protected]”); +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 -urN linux/drivers/usb/dabusb.h linux.usb/drivers/usb/dabusb.h — linux/drivers/usb/dabusb.h Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/dabusb.h Tue Jan 25 00:08:39 2000 @@ -0,0 +1,85 @@ +#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 64 +#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; + int in_use; +} 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 0x89 +#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 -urN linux/drivers/usb/dc2xx.c linux.usb/drivers/usb/dc2xx.c — linux/drivers/usb/dc2xx.c Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/dc2xx.c Tue Jan 25 00:08:39 2000 @@ -0,0 +1,477 @@ +/* + * 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. + */ + +/* + * 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 “usb.h” + + + +/* 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 { + short idVendor; + 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 +// { 0x03f0, 0xffff }, // HP PhotoSmart C500 + + /* Other USB devices may well work here too, so long as they + * just stick to half duplex bulk packet exchanges. + */ +}; + + +struct camera_state { + struct usb_device *dev; /* USB device handle */ + char inEP; /* read endpoint */ + char 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 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 %ld”, 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; + unsigned long 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 = MAX_CAMERAS + || !(camera = minor_data [subminor])) { + return -ENODEV; + } + + if (!(camera->buf = (char *) kmalloc (MAX_PACKET_SIZE, GFP_KERNEL))) { + return -ENOMEM; + } + + dbg (“open”); + + /* Keep driver from being unloaded while it’s in use */ + MOD_INC_USE_COUNT; + + 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); + } + + MOD_DEC_USE_COUNT; + + 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 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) { + 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->dev = dev; + 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”); + camera->dev = NULL; + return NULL; + } + + + if (usb_set_configuration (dev, dev->config[0].bConfigurationValue)) { + err (“Failed usb_set_configuration”); + camera->dev = NULL; + return NULL; + } + + camera->info = camera_info; + return camera; +} + +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) . (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: linux-2.2.14-usb.diff,v 1.1 2000/01/28 02:51:18 trini Exp $ + */ + +#include +#include +#include +#include +#include + +#include “usb.h” +#include “usbdevice_fs.h” + +#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->bEndpointAddress & USB_DIR_IN) ? ‘I’ : ‘O’, + desc->bmAttributes, EndpointType[desc->bmAttributes & 3], + 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 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 bNumInterfaces; i++) { + interface = config->interface + i; + if (!interface) + break; + for (j = 0; j 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, const 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 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) + return start; + start += sprintf(start, “Interface:”); + if (index 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, const struct usb_device *usbdev, + int 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, 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 + */ + /* do not dump descriptors for root hub */ + if (usbdev->devnum >= 0) + 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 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 next) { + /* print bandwidth allocation */ + bus = list_entry(buslist, struct usb_bus, bus_list); + len = sprintf(page, 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); + end = usb_device_dump(page + len, page + (2*PAGE_SIZE – 256), bus->root_hub, bus->busnum, 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; +} + +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; + + 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; + 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 long long usb_device_lseek(struct file * file, long long 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 = { + usb_device_lseek, /* lseek */ + usb_device_read, /* read */ + NULL, /* write */ + NULL, /* readdir */ + usb_device_poll, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + usb_device_open, /* open */ + NULL, /* flush */ + usb_device_release, /* release */ + NULL /* fsync */ +}; diff -urN linux/drivers/usb/devio.c linux.usb/drivers/usb/devio.c — linux/drivers/usb/devio.c Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/devio.c Tue Jan 25 00:08:39 2000 @@ -0,0 +1,1021 @@ +/*****************************************************************************/ + +/* + * devio.c — User space communication with USB devices. + * + * Copyright (C) 1999-2000 Thomas Sailer ([email protected]) + * + * 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: linux-2.2.14-usb.diff,v 1.1 2000/01/28 02:51:18 trini 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 “usb.h” +#include “usbdevice_fs.h” + +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 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 long long usbdev_lseek(struct file *file, long long 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; + + pos = *ppos; + down_read(&ps->devsem); + if (!ps->dev) + ret = -ENODEV; + else if (pos nbytes) + len = nbytes; + if (copy_to_user(buf, ((char *)&ps->dev->descriptor) + pos, len)) + ret = -EFAULT; + else { + *ppos += len; + buf += len; + nbytes -= len; + ret += len; + } + } + 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 0 + 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); + if (!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 = { + “usbdevfs”, + driver_probe, + driver_disconnect, + LIST_HEAD_INIT(usbdevfs_driver.driver_list), + NULL, + 0 +}; + +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 actconfig->bNumInterfaces; i++) { + iface = &dev->actconfig->interface[i]; + for (j = 0; j num_altsetting; j++) { + alts = &iface->altsetting[j]; + for (e = 0; e 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 actconfig->bNumInterfaces; i++) { + iface = &dev->actconfig->interface[i]; + for (j = 0; j num_altsetting; j++) { + alts = &iface->altsetting[j]; + if (alts->bInterfaceNumber == ifn) + return i; + } + } + return -ENOENT; +} + +/* + * 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 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)) dev, ctrl.index & 0xff)) 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 (idevnum, 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)) 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 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)) dev, ep & 0xf, !(ep & USB_DIR_IN), 0); + return 0; +} + +static int proc_setintf(struct dev_state *ps, void *arg) +{ + struct usbdevfs_setinterface setintf; + int ret; + + copy_from_user_ret(&setintf, arg, sizeof(setintf), -EFAULT); + if ((ret = findintfif(ps->dev, setintf.interface)) 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) SIGRTMAX)) + return -EINVAL; + if ((ret = findintfep(ps->dev, uurb.endpoint)) 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 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 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 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 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 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 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)) dev, intf)) 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_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; + + } + up_read(&ps->devsem); + if (ret >= 0) + inode->i_atime = CURRENT_TIME; + return ret; +} + +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; +} + +static struct file_operations usbdevfs_device_file_operations = { + usbdev_lseek, /* lseek */ + usbdev_read, /* read */ + NULL, /* write */ + NULL, /* readdir */ + usbdev_poll, /* poll */ + usbdev_ioctl, /* ioctl */ + NULL, /* mmap */ + usbdev_open, /* open */ + NULL, /* flush */ + usbdev_release, /* release */ + NULL /* fsync */ +}; + +struct inode_operations usbdevfs_device_inode_operations = { + &usbdevfs_device_file_operations, /* file-ops */ +}; diff -urN linux/drivers/usb/drivers.c linux.usb/drivers/usb/drivers.c — linux/drivers/usb/drivers.c Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/drivers.c Tue Jan 25 00:08:39 2000 @@ -0,0 +1,126 @@ +/* + * 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: linux-2.2.14-usb.diff,v 1.1 2000/01/28 02:51:18 trini Exp $ + */ + +#include +#include +#include + +#include “usb.h” +#include “usbdevice_fs.h” + + +/*****************************************************************/ + +/* + * 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 next) { + struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list); + start += sprintf (start, “%s\n”, 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 long long usb_driver_lseek(struct file * file, long long 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 = { + usb_driver_lseek, /* lseek */ + usb_driver_read, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* open */ + NULL, /* flush */ + NULL, /* release */ + NULL /* fsync */ +}; diff -urN linux/drivers/usb/evdev.c linux.usb/drivers/usb/evdev.c — linux/drivers/usb/evdev.c Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/evdev.c Tue Jan 25 00:08:39 2000 @@ -0,0 +1,275 @@ +/* + * evdev.c Version 0.1 + * + * Copyright (c) 1999 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_BUFFER_SIZE 64 + +#include +#include +#include +#include +#include +#include + +struct evdev { + char name[32]; + int used; + struct input_handle handle; + struct miscdevice misc; + wait_queue_head_t wait; + 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 unsigned long evdev_miscbits = 0; +static struct evdev *evdev_base[BITS_PER_LONG]; + +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); + + if (list->fasync) + kill_fasync(list->fasync, SIGIO, POLL_IN); + + 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 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->used) { + clear_bit(list->evdev->misc.minor – EVDEV_MINOR_BASE, &evdev_miscbits); + misc_deregister(&list->evdev->misc); + kfree(list->evdev); + } + + kfree(list); + + MOD_DEC_USE_COUNT; + 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 > BITS_PER_LONG || !test_bit(i, &evdev_miscbits)) + return -ENODEV; + + if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL))) + return -ENOMEM; + + memset(list, 0, sizeof(struct evdev_list)); + + list->evdev = evdev_base[i]; + list->next = evdev_base[i]->list; + evdev_base[i]->list = list; + + file->private_data = list; + + list->evdev->used++; + + MOD_INC_USE_COUNT; + 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) 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; +} + +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 struct file_operations evdev_fops = { + read: evdev_read, + write: evdev_write, + poll: evdev_poll, + open: evdev_open, + release: evdev_release, + fasync: evdev_fasync, +}; + +static int evdev_connect(struct input_handler *handler, struct input_dev *dev) +{ + struct evdev *evdev; + + if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL))) + return -1; + + memset(evdev, 0, sizeof(struct evdev)); + + init_waitqueue_head(&evdev->wait); + + evdev->misc.minor = ffz(evdev_miscbits); + set_bit(evdev->misc.minor, &evdev_miscbits); + evdev_base[evdev->misc.minor] = evdev; + + sprintf(evdev->name, “evdev%d”, evdev->misc.minor); + evdev->misc.name = evdev->name; + evdev->misc.minor += EVDEV_MINOR_BASE; + evdev->misc.fops = &evdev_fops; + + evdev->handle.dev = dev; + evdev->handle.handler = handler; + evdev->handle.private = evdev; + + evdev->used = 1; + + misc_register(&evdev->misc); + input_open_device(&evdev->handle); + + printk(“%s: Event device for input%d on misc%d – /dev/input%d\n”, + evdev->name, dev->number, evdev->misc.minor, evdev->misc.minor – EVDEV_MINOR_BASE); + + return 0; +} + +static void evdev_disconnect(struct input_handle *handle) +{ + struct evdev *evdev = handle->private; + + input_close_device(handle); + + if (!–evdev->used) { + clear_bit(evdev->misc.minor – EVDEV_MINOR_BASE, &evdev_miscbits); + misc_deregister(&evdev->misc); + kfree(evdev); + } +} + +static struct input_handler evdev_handler = { + event: evdev_event, + connect: evdev_connect, + disconnect: evdev_disconnect, +}; + +#ifdef MODULE +int init_module(void) +#else +int __init evdev_init(void) +#endif +{ + input_register_handler(&evdev_handler); + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + input_unregister_handler(&evdev_handler); +} +#endif diff -urN linux/drivers/usb/graphire.c linux.usb/drivers/usb/graphire.c — linux/drivers/usb/graphire.c Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/graphire.c Tue Jan 25 00:08:39 2000 @@ -0,0 +1,195 @@ +/* + * graphire.c Version 0.2 + * + * Copyright (c) 2000 Vojtech Pavlik + * Copyright (c) 2000 Andreas Bach Aaen + * + * USB Wacom Graphire tablet support + * + * Sponsored by SuSE + * + * ChangeLog: + * v0.1 (vp) – Initial release + * v0.2 (aba) – Support for all buttons / combinations + */ + +/* + * 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 “usb.h” + +MODULE_AUTHOR(“Vojtech Pavlik “); + +/* + * Thanks for the following information to: Andreas Bach Aaen + * + * The input report: + * + * byte 0: report ID (2) + * byte 1: bit7 mouse/pen/rubber near + * bit5-6 0 – pen, 1 – rubber, 2 – mouse + * bit4 1 ? + * bit3 0 ? + * bit2 mouse middle button / pen button2 + * bit1 mouse right button / pen button1 + * bit0 mouse left button / pen tip / rubber + * byte 2: X low bits + * byte 3: X high bits + * byte 4: Y low bits + * byte 5: Y high bits + * byte 6: pen pressure low bits / mouse wheel + * byte 7: pen presure high bits / mouse distance + * + * There are also two single-byte feature reports (2 and 3). + * + * Resolution: + * X: 0 – 10206 + * Y: 0 – 7422 + * + * (0,0) is upper left corner + */ + +#define USB_VENDOR_ID_WACOM 0x056a +#define USB_DEVICE_ID_WACOM_GRAPHIRE 0x0010 + +struct graphire { + signed char data[8]; + struct input_dev dev; + struct urb irq; +}; + +static void graphire_irq(struct urb *urb) +{ + struct graphire *graphire = urb->context; + unsigned char *data = graphire->data; + struct input_dev *dev = &graphire->dev; + + if (urb->status) return; + + if (data[0] != 2) + dbg(“received unknown report #%d”, data[0]); + + if ( data[1] & 0x80 ) { + input_report_abs(dev, ABS_X, data[2] | ((__u32)data[3] > 5) & 3) { + + case 0: /* Pen */ + input_report_key(dev, BTN_TOOL_PEN, !!(data[1] & 0x80)); + input_report_key(dev, BTN_TOUCH, !!(data[1] & 0x01)); + input_report_key(dev, BTN_STYLUS, !!(data[1] & 0x02)); + input_report_key(dev, BTN_STYLUS2, !!(data[1] & 0x04)); + input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] 24); + input_report_key(dev, BTN_LEFT, !!(data[1] & 0x01)); + input_report_key(dev, BTN_RIGHT, !!(data[1] & 0x02)); + input_report_key(dev, BTN_MIDDLE, !!(data[1] & 0x04)); + input_report_abs(dev, ABS_DISTANCE, data[7]); + input_report_rel(dev, REL_WHEEL, (signed char) data[6]); + break; + } +} + +static void *graphire_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_endpoint_descriptor *endpoint; + struct graphire *graphire; + + if (dev->descriptor.idVendor != USB_VENDOR_ID_WACOM || + dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_GRAPHIRE) + return NULL; + + endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; + + if (!(graphire = kmalloc(sizeof(struct graphire), GFP_KERNEL))) return NULL; + memset(graphire, 0, sizeof(struct graphire)); + + graphire->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); + graphire->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + graphire->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE); + graphire->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2); + graphire->dev.relbit[0] |= BIT(REL_WHEEL); + graphire->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE); + + graphire->dev.absmax[ABS_X] = 10206; + graphire->dev.absmax[ABS_Y] = 7422; + graphire->dev.absmax[ABS_PRESSURE] = 511; + graphire->dev.absmax[ABS_DISTANCE] = 32; + + FILL_INT_URB(&graphire->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), + graphire->data, 8, graphire_irq, graphire, endpoint->bInterval); + + if (usb_submit_urb(&graphire->irq)) { + kfree(graphire); + return NULL; + } + + input_register_device(&graphire->dev); + + printk(KERN_INFO “input%d: Wacom Graphire\n”, graphire->dev.number); + + return graphire; +} + +static void graphire_disconnect(struct usb_device *dev, void *ptr) +{ + struct graphire *graphire = ptr; + usb_unlink_urb(&graphire->irq); + input_unregister_device(&graphire->dev); + kfree(graphire); +} + +static struct usb_driver graphire_driver = { + name: “graphire”, + probe: graphire_probe, + disconnect: graphire_disconnect, +}; + +#ifdef MODULE +void cleanup_module(void) +{ + usb_deregister(&graphire_driver); +} + +int init_module(void) +#else +int graphire_init(void) +#endif +{ + usb_register(&graphire_driver); + return 0; +} diff -urN linux/drivers/usb/hid-debug.h linux.usb/drivers/usb/hid-debug.h — linux/drivers/usb/hid-debug.h Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/hid-debug.h Tue Jan 25 00:08:39 2000 @@ -0,0 +1,233 @@ +/* + * driver/usb/hid-debug.h + * + * (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, “System PowerDown”}, + {0, 0x82, “System Sleep”}, + {0, 0x83, “System WakeUp”}, + {0, 0x84, “System ContextMenu”}, + {0, 0x85, “System MainMenu”}, + {0, 0x86, “System AppMenu”}, + {0, 0x87, “System MenuHelp”}, + {0, 0x88, “System MenuExit”}, + {0, 0x89, “System MenuSelect”}, + {0, 0x8a, “System MenuRight”}, + {0, 0x8b, “System MenuLeft”}, + {0, 0x8c, “System MenuUp”}, + {0, 0x8d, “System MenuDown”}, + {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” }, + { 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, 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 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 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 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 -urN linux/drivers/usb/hid.c linux.usb/drivers/usb/hid.c — linux/drivers/usb/hid.c Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/hid.c Tue Jan 25 00:08:39 2000 @@ -0,0 +1,1340 @@ +/* + * hid.c Version 0.8 + * + * 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 “usb.h” +#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 + +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,192,138,192,192,128,129,131,137,133,135,136,113, + 115,114,192,192,192,192,192,124,192,192,192,192,192,192,192,192, + 192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, + 192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, + 192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, + 192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, + 192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, + 29, 42, 56,125, 97, 54,100,126 +}; + +static struct { + __s32 x; + __s32 y; +} hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +/* + * 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 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 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 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 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 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 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 local.delimiter_depth–; + } + return 1; + + case HID_LOCAL_ITEM_TAG_USAGE: + + if (parser->local.delimiter_branch size global.usage_page local.delimiter_branch size global.usage_page local.usage_minimum = data; + return 0; + } + dbg(“alternative usage ignored”); + return 0; + + case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: + + if (parser->local.delimiter_branch size global.usage_page local.usage_minimum; n 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 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 report_enum + i; + + for (j = 0; j 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( *((__u16*)start)++); + return start; + } + + case 3: + item->size++; + if ((end – start) >= 4) { + item->data.u32 = le32_to_cpu( *((__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 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); + if (a && a != -1) return value > 0 ? 1 > 5) > offset) & ((1 > 5) input; + int max; + unsigned long *bit; + + switch (usage->hid & HID_USAGE_PAGE) { + + case HID_UP_KEYBOARD: + + if ((usage->hid & HID_USAGE) code = hid_keyboard[usage->hid & HID_USAGE])) + return; + } else + usage->code = KEY_UNKNOWN; + + set_bit(EV_REP, input->evbit); + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + 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: + + usage->code = usage->hid & 0xf; + + 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 */ + + 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 0x33: /* Touch */ + case 0x42: /* TipSwitch */ + case 0x43: /* TipSwitch2 */ + + 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; + + default: + unknown: + + if (field->flags & HID_MAIN_ITEM_RELATIVE) { + usage->code = REL_MISC; + usage->type = EV_REL; bit = input->relbit; max = REL_MAX; + break; + } + + if (field->report_size == 1) { + usage->code = BTN_MISC; + usage->type = EV_KEY; bit = input->keybit; max = KEY_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 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 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, struct hid_usage *usage, __s32 value) +{ + hid_dump_input(usage, value); + + if (usage->hat) { + if (usage->hat == 2) value = value * 2 – 1; + 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; + } + + input_event(input, usage->type, usage->code, value); +} + +/* + * 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 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, &field->usage[n], value[n]); + + } else { + + if (field->value[n] >= min && field->value[n] usage[field->value[n] – min].hid /* nonzero usage */ + && search(value, field->value[n], count)) + hid_process_event(&dev->input, &field->usage[field->value[n] – min], 0); + + if (value[n] >= min && value[n] usage[value[n] – min].hid /* nonzero usage */ + && search(field->value, value[n], count)) + hid_process_event(&dev->input, &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 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 size – 1) >> 3) + 1) { + dbg(“report %d is too short, (%d id, len, ((report->size – 1) >> 3) + 1); + return; + } + + for (n = 0; n 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 report_count; + unsigned offset = field->report_offset; + unsigned size = field->report_size; + unsigned n; + + for (n = 0; n logical_minimum 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 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 field->logical_maximum) + || (value 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 maxfield; i++) { + *field = report->field[i]; + for (j = 0; j 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; +} + +/* + * 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; + + for (k = HID_INPUT_REPORT; k 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 maxfield; i++) + for (j = 0; j 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, report->id, 0); + hid_read_report(hid, report); + } + } + } +} + +static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum) +{ + struct usb_interface_descriptor *interface = dev->actconfig->interface[ifnum].altsetting + 0; + struct hid_descriptor *hdesc; + struct hid_device *hid; + unsigned rsize = 0; + int n; + + 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 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)) 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); + + if (usb_submit_urb(&hid->urb)) { + dbg(“submitting interrupt URB failed”); + continue; + } + + 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; + + 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) +{ + char *hid_name[] = {“Device”, “Pointer”, “Mouse”, “Device”, “Joystick”, + “Gamepad”, “Keyboard”, “Keypad”, “Multi-Axis Controller”}; + struct hid_device *hid; + + dbg(“HID probe called for ifnum %d”, ifnum); + + if (!(hid = usb_hid_configure(dev, ifnum))) + return NULL; + + hid_dump_device(hid); + + hid_init_input(hid); + input_register_device(&hid->input); + + printk(KERN_INFO “input%d: USB HID v%d.%d %s\n”, + hid->input.number, hid->version >> 8, hid->version & 0xff, + (hid->application & 0xffff) application & 0xffff] : “device”); + + 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 +}; + +#ifdef MODULE +void cleanup_module(void) +{ + usb_deregister(&hid_driver); +} + +int init_module(void) +#else +int hid_init(void) +#endif +{ + usb_register(&hid_driver); + return 0; +} diff -urN linux/drivers/usb/hid.h linux.usb/drivers/usb/hid.h — linux/drivers/usb/hid.h Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/hid.h Tue Jan 25 00:08:39 2000 @@ -0,0 +1,320 @@ +#ifndef __HID_H +#define __HID_H + +/* + * drivers/usb/hid.h Version 0.8 + * + * 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 +#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_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 + +/* + * 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 256 + +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 */ +}; + +#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 -urN linux/drivers/usb/hub.c linux.usb/drivers/usb/hub.c — linux/drivers/usb/hub.c Fri Apr 30 08:20:01 1999 +++ linux.usb/drivers/usb/hub.c Tue Jan 25 00:08:39 2000 @@ -1,12 +1,11 @@ /* * 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 + * + * $Id: linux-2.2.14-usb.diff,v 1.1 2000/01/28 02:51:18 trini Exp $ */ #include @@ -15,206 +14,275 @@ #include #include -#include +#include +#include + +#define DEBUG #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; -/* 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 event_list.next == &hub->event_list) { – list_add(&hub->event_list, &hub_event_list); – /* Wake up khubd */ – wake_up(&usb_hub_wait); + switch (status) { + case -ENODEV: + /* Just ignore it */ + break; + case 0: + /* 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(&khubd_wait); + } + spin_unlock_irqrestore(&hub_event_lock, flags); } – spin_unlock_irqrestore(&hub_event_lock, flags); + break; } return 1; } -static void usb_hub_configure(struct usb_hub *hub) +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 nports; i++) + usb_set_port_feature(hub->dev, i + 1, USB_PORT_FEAT_POWER); +} + +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[4], *bitmap; + struct usb_hub_descriptor *descriptor; + struct usb_descriptor_header *header; + struct usb_hub_status *hubsts; + int i; – usb_set_configuration(dev, dev->config[0].bConfigurationValue); + /* Get the length first */ + if (usb_get_hub_descriptor(dev, buffer, 4) bLength, GFP_KERNEL); + if (!bitmap) + return -1; + + if (usb_get_hub_descriptor(dev, bitmap, header->bLength) nports = dev->maxchild = hubdescriptor[2]; – printk(“hub: %d-port%s detected\n”, hub->nports, – (hub->nports == 1) ? “” : “s”); + descriptor = (struct usb_hub_descriptor *)bitmap; – charac = (hubdescriptor[4] nports = dev->maxchild = descriptor->bNbrPorts; + info(“%d port%s detected”, hub->nports, (hub->nports == 1) ? “” : “s”); + + 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 maxchild; i++) – printk(“hub: port %d is%s removable\n”, i + 1, – hubdescriptor[7 + ((i + 1)/8)] & (1 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] 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 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; + int 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)) + return NULL; /* If it’s not an interrupt endpoint, we’d better punt! */ if ((endpoint->bmAttributes & 3) != 3) – return -1; + 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; + 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) { + hub->irqpipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + ret = usb_request_irq(dev, hub->irqpipe, + hub_irq, endpoint->bInterval, + hub, &hub->irq_handle); + if (ret) { + err(“usb_request_irq failed (%d)”, ret); + /* 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); – /* Wake up khubd */ – wake_up(&usb_hub_wait); + spin_unlock_irqrestore(&hub_event_lock, flags); – return 0; + kfree(hub); + + return NULL; + } + + /* Wake up khubd */ + wake_up(&khubd_wait); + } + + return hub; } -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 = ptr; unsigned long flags; spin_lock_irqsave(&hub_event_lock, flags); @@ -222,9 +290,15 @@ /* 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->irq_handle) { + usb_release_irq(hub->dev, hub->irq_handle, hub->irqpipe); + } + /* Free the memory */ kfree(hub); } @@ -232,154 +306,222 @@ 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 tries; – usb_disconnect(&hub->children[port]); + wait_ms(100); + /* Check status */ + if (usb_get_port_status(hub, port + 1, &portsts)children[port])) { + usb_disconnect(&hub->children[port]); + /* Return now if nothing is connected */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) + return; + } + wait_ms(400); – wait_ms(50); /* FIXME: This is from the *BSD stack, thanks! 🙂 */ + /* Reset the port */ – if (usb_get_port_status(hub, port + 1, buf)) { – printk(“get_port_status failed\n”); – return; – } +#define MAX_TRIES 5 + + for(tries=0;triesbus->op->allocate(hub); + 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) { – printk(“couldn’t allocate usb_device\n”); + err(“couldn’t allocate usb_device”); return; } – usb_connect(usb); – 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) */ + if (usb_new_device(usb)) { + /* Woops, disable the port */ + dbg(“hub: disabling port %d”, port + 1); + usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_ENABLE); + } } 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; – spin_lock_irqsave(&hub_event_lock, flags); + /* + * 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); + + if (list_empty(&hub_event_list)) + goto he_unlock; + + /* Grab the next entry from the beginning of the list */ + tmp = hub_event_list.next; – tmp = head->next; – while (tmp != head) { 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 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) 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”); +/* + MOD_DEC_USE_COUNT; +*/ + + dbg(“usb_hub_thread exiting”); + khubd_running = 0; return 0; } @@ -394,29 +536,55 @@ /* * 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) { 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() */ + + + diff -urN linux/drivers/usb/hub.h linux.usb/drivers/usb/hub.h — linux/drivers/usb/hub.h Wed Apr 21 05:41:59 1999 +++ linux.usb/drivers/usb/hub.h Tue Jan 25 00:08:39 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,50 @@ #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 + +/* Hub descriptor */ +struct usb_hub_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bNbrPorts; + __u16 wHubCharacteristics; +#if 0 + __u8 wHubCharacteristics[2]; /* __u16 but not aligned! */ +#endif + __u8 bPwrOn2PwrGood; + __u8 bHubContrCurrent; + /* DeviceRemovable and PortPwrCtrlMask want to be variable-length + bitmaps that hold max 256 entries, but for now they’re ignored */ +#if 0 + __u8 filler; +#endif +} __attribute__ ((packed)); + struct usb_device; typedef enum { @@ -67,6 +112,13 @@ /* Device structure */ struct usb_device *dev; + /* Reference to the hub’s polling IRQ and its associated pipe */ + void *irq_handle; + unsigned int irqpipe; + + /* List of hubs */ + struct list_head hub_list; + /* Temporary event list */ struct list_head event_list; @@ -77,4 +129,3 @@ }; #endif – diff -urN linux/drivers/usb/ibmcam.c linux.usb/drivers/usb/ibmcam.c — linux/drivers/usb/ibmcam.c Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/ibmcam.c Tue Jan 25 00:08:39 2000 @@ -0,0 +1,2347 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include “usb.h” +#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 %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(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); +} + +/* + * 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 = ‘A’ && ch = ‘a’ && ch 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 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 curline++) { + int i; + + f = frame->data + (imgwidth * 3 * frame->curline); + for (i=0; i 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_find_header(const 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; + } + } + ++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]; + tmp = ibmcam_find_header(frame->hdr_sig, data, scratch_left(data)); + + 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+4; + + 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 */ + int y, u, v, i, frame_done=0, mono_plane, hue_corr, color_corr; + + hue_corr = (ibmcam->vpic.hue – 0x8000) >> 10; /* -32..+31 */ + color_corr = (ibmcam->vpic.colour – 0x8000) >> 10; /* -32..+31 */ + + 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—–| \ + * |———–+———————| \ + * ||| Total 72. pairs of lines + * |… … …| / + * |___________|_____________________| / + * – odd line- ——- even line — + * + * another format: + * ~~~~~~~~~~~~~~ + * ___________________________________ + * |—–Y—–|—UVUVUV…UVUV—–| \ + * |———–+———————| \ + * ||| Total 144. pairs of lines + * |… … …| / + * |___________|_____________________| / + * – odd line- ——- even line — + */ + + /* Make sure there’s enough data for the entire line */ + if (scratch_left(data) 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) > 1) frmwidth-1] + */ + chromaLine = data; + if (mono_plane) + chromaLine += frame->frmwidth; + + for (i = 0; i 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 (scratch_left(data) >= (4+2)) { + unsigned char *dp; + int j; + + for (j=0, dp=data; j 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; + } + + y = mono_plane ? data[0] : data[1]; + + if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */ + rv = gv = bv = y; + else { + if (frame->order_uv) { + u = chromaLine[(i >> 1) > 1) > 1) > 1) 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; +} + +/* + * 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) + newstate = usb_ibmcam_parse_lines(ibmcam, &copylen); + } + 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; +} + +#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 + +/* + * 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 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 = 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 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 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->remove_pending) + 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 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; +} + +static int usb_ibmcam_veio( + struct usb_device *dev, + 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 }; + const unsigned short len = sizeof(cp); + int i; + + if (req == 1) { + i = usb_control_msg( + dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + value, + index, + cp, + len, + HZ); +#if 0 + printk(KERN_DEBUG “USB => %02x%02x%02x%02x%02x%02x%02x%02x ” + “(req=$%02x val=$%04x ind=$%04x len=%d.)\n”, + cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7], + req, value, index, len); +#endif + } else { + i = usb_control_msg( + dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + value, + index, + NULL, + 0, + HZ); + } + if (i dev; + 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) { + int i; + ibmcam->vpic_old.contrast = new_contrast; + for (i=0; i dev; + const int ntries = 5; + int i; + + RESTRICT_TO_RANGE(lighting, LIGHTING_MIN, LIGHTING_MAX); + if (debug > 0) + printk(KERN_INFO “%s: Set lighting to %hu.\n”, proc, lighting); + + for (i=0; i dev; + 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 dev; + 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]); + + for (j=0; j dev; + const int ntries = 5; + int i; + + usb_ibmcam_veio(dev, 1, 0, 0x128); + usb_ibmcam_veio(dev, 1, 0x00, 0x0100); + usb_ibmcam_veio(dev, 0, 0x01, 0x0100); /* LED On */ + usb_ibmcam_veio(dev, 1, 0x00, 0x0100); + usb_ibmcam_veio(dev, 0, 0x81, 0x0100); /* LED Off */ + usb_ibmcam_veio(dev, 1, 0x00, 0x0100); + usb_ibmcam_veio(dev, 0, 0x01, 0x0100); /* LED On */ + usb_ibmcam_veio(dev, 0, 0x01, 0x0108); + + usb_ibmcam_veio(dev, 0, 0x03, 0x0112); + usb_ibmcam_veio(dev, 1, 0x00, 0x0115); + usb_ibmcam_veio(dev, 0, 0x06, 0x0115); + usb_ibmcam_veio(dev, 1, 0x00, 0x0116); + usb_ibmcam_veio(dev, 0, 0x44, 0x0116); + usb_ibmcam_veio(dev, 1, 0x00, 0x0116); + usb_ibmcam_veio(dev, 0, 0x40, 0x0116); + usb_ibmcam_veio(dev, 1, 0x00, 0x0115); + usb_ibmcam_veio(dev, 0, 0x0e, 0x0115); + usb_ibmcam_veio(dev, 0, 0x19, 0x012c); + + usb_ibmcam_Packet_Format1(dev, 0x00, 0x1e); + usb_ibmcam_Packet_Format1(dev, 0x39, 0x0d); + usb_ibmcam_Packet_Format1(dev, 0x39, 0x09); + usb_ibmcam_Packet_Format1(dev, 0x3b, 0x00); + usb_ibmcam_Packet_Format1(dev, 0x28, 0x22); + usb_ibmcam_Packet_Format1(dev, light_27, 0); + usb_ibmcam_Packet_Format1(dev, 0x2b, 0x1f); + usb_ibmcam_Packet_Format1(dev, 0x39, 0x08); + + for (i=0; i dev); + + usb_ibmcam_veio(ibmcam->dev, 0, 0x0001, 0x0114); + usb_ibmcam_veio(ibmcam->dev, 0, 0x00c0, 0x010c); + usb_clear_halt(ibmcam->dev, ibmcam->video_endp); + usb_ibmcam_setup_after_video_if(ibmcam->dev); +} + +static int ibmcam_init_isoc(struct usb_ibmcam *ibmcam) +{ + struct usb_device *dev = ibmcam->dev; + urb_t *urb; + int fx, err; + + ibmcam->compress = 0; + ibmcam->curframe = -1; + ibmcam->cursbuf = 0; + ibmcam->scratchlen = 0; + + /* Alternate interface 1 is is the biggest frame size */ + if (usb_set_interface(ibmcam->dev, 2, 1) sbuf[0].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[0].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 (fx = 0; fx iso_frame_desc[fx].offset = ibmcam->iso_packet_len * fx; + urb->iso_frame_desc[fx].length = ibmcam->iso_packet_len; + } + urb = usb_alloc_urb(FRAMES_PER_DESC); + if (!urb) { + printk(KERN_ERR “ibmcam_init_isoc: usb_init_isoc ret %d\n”, + 0); + return -ENOMEM; + } + ibmcam->sbuf[1].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[1].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 (fx = 0; fx iso_frame_desc[fx].offset = ibmcam->iso_packet_len * fx; + urb->iso_frame_desc[fx].length = ibmcam->iso_packet_len; + } + + ibmcam->sbuf[1].urb->next = ibmcam->sbuf[0].urb; + ibmcam->sbuf[0].urb->next = ibmcam->sbuf[1].urb; + + err = usb_submit_urb(ibmcam->sbuf[0].urb); + if (err) + printk(KERN_ERR “ibmcam_init_isoc: usb_run_isoc(0) ret %d\n”, + err); + err = usb_submit_urb(ibmcam->sbuf[1].urb); + if (err) + printk(KERN_ERR “ibmcam_init_isoc: usb_run_isoc(1) ret %d\n”, + 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. + */ +static void ibmcam_stop_isoc(struct usb_ibmcam *ibmcam) +{ + if (!ibmcam->streaming) + return; + + /* Unschedule all of the iso td’s */ + usb_unlink_urb(ibmcam->sbuf[1].urb); + usb_unlink_urb(ibmcam->sbuf[0].urb); + + /* printk(KERN_DEBUG “streaming=0\n”); */ + ibmcam->streaming = 0; + + /* Delete them all */ + usb_free_urb(ibmcam->sbuf[1].urb); + usb_free_urb(ibmcam->sbuf[0].urb); + + usb_ibmcam_setup_video_stop(ibmcam->dev); + + /* Set packet size to 0 */ + if (usb_set_interface(ibmcam->dev, 2, 0) 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; +#if 0 + /* This provides a “clean” frame but slows things down */ + memset(frame->data, 0, MAX_FRAME_SIZE); +#endif + 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_352x288: + frame->frmwidth = 352; + frame->frmheight = 288; + frame->order_uv = 0; /* V Y U Y … */ + frame->hdr_sig = 0x00; /* 00 FF 00 00 */ + break; + } + + 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). + */ +static int ibmcam_open(struct video_device *dev, int flags) +{ + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; + const int nbuffers = 2; + const int sb_size = FRAMES_PER_DESC * ibmcam->iso_packet_len; + int i, err = 0; + + down(&ibmcam->lock); + + if (ibmcam->user) + err = -EBUSY; + else { + /* Clean pointers so we know if we allocated something */ + for (i=0; i sbuf[i].data = NULL; + + /* Allocate memory for the frame buffers */ + ibmcam->fbuf_size = nbuffers * 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 frame[i].grabstate = FRAME_UNUSED; + ibmcam->frame[i].data = ibmcam->fbuf + i*MAX_FRAME_SIZE; + + ibmcam->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL); + if (ibmcam->sbuf[i].data == NULL) { + err = -ENOMEM; + break; + } + /* + * 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; + } + } + 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 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) { + err = usb_ibmcam_setup(ibmcam); + if (!err) + ibmcam->initialized = 1; + } + if (!err) { + ibmcam->user++; + MOD_INC_USE_COUNT; + } + } + } + + up(&ibmcam->lock); + 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. + */ +static void ibmcam_close(struct video_device *dev) +{ + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; + + down(&ibmcam->lock); + + ibmcam_stop_isoc(ibmcam); + + rvfree(ibmcam->fbuf, ibmcam->fbuf_size); + kfree(ibmcam->scratch); + kfree(ibmcam->sbuf[1].data); + kfree(ibmcam->sbuf[0].data); + + ibmcam->user–; + MOD_DEC_USE_COUNT; + + up(&ibmcam->lock); +} + +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->remove_pending) + 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 = 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: + 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 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->remove_pending) + return -EFAULT; + + if (!dev || !buf) + 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: + 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->remove_pending) + 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); + + memset(&ibmcam->vpic, 0, sizeof(ibmcam->vpic)); + memset(&ibmcam->vpic_old, 0x55, sizeof(ibmcam->vpic_old)); + + ibmcam->vpic.colour = init_color vpic.hue = init_hue vpic.brightness = init_brightness vpic.contrast = init_contrast vpic.whiteness = 105 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 /*| VID_TYPE_SUBCAPTURE*/; + 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”); +} + +/* + * 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() + */ +static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_ibmcam *ibmcam = NULL; + struct usb_interface_descriptor *interface; + + 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; + + /* Camera confirmed. We claim only interface 2 (video data) */ + if (ifnum != 2) + return NULL; + + /* We found an IBM camera */ + printk(KERN_INFO “IBM USB camera found (interface %u.)\n”, ifnum); + + if (debug >= 1) + printk(KERN_DEBUG “ibmcam_probe: new ibmcam alloc\n”); + ibmcam = kmalloc(sizeof(*ibmcam), GFP_KERNEL); + if (ibmcam == NULL) { + printk(KERN_ERR “couldn’t kmalloc ibmcam struct\n”); + return NULL; + } + memset(ibmcam, 0, sizeof(struct usb_ibmcam)); + ibmcam->dev = dev; + interface = &dev->actconfig->interface[ifnum].altsetting[0]; + ibmcam->iface = interface->bInterfaceNumber; + ibmcam->video_endp = 0x82; + init_waitqueue_head (&ibmcam->remove_ok); + ibmcam->iso_packet_len = 1014; + + memcpy(&ibmcam->vdev, &ibmcam_template, sizeof(ibmcam_template)); + usb_ibmcam_configure_video(ibmcam); + + init_waitqueue_head(&ibmcam->frame[0].wq); + init_waitqueue_head(&ibmcam->frame[1].wq); + + if (video_register_device(&ibmcam->vdev, VFL_TYPE_GRABBER) == -1) { + printk(KERN_ERR “video_register_device failed\n”); + return NULL; + } + if (debug > 1) + printk(KERN_DEBUG “video_register_device() successful\n”); + + ibmcam->compress = 0; + ibmcam->user=0; + init_MUTEX(&ibmcam->lock); /* to 1 == available */ + + return ibmcam; +} + +/* + * 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. + * + * TODO: This code behaves badly on surprise removal! + * + * History: + * 1/22/00 Added polling of MOD_IN_USE to delay removal until all users gone. + */ +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; + wait_queue_head_t wq; /* Wait here until removal is safe */ + + if (debug > 0) + printk(KERN_DEBUG “%s(%p,%p.)\n”, proc, dev, ptr); + + init_waitqueue_head(&wq); + ibmcam->remove_pending = 1; /* Now all ISO data will be ignored */ + + /* At this time we ask to cancel outstanding URBs */ + ibmcam_stop_isoc(ibmcam); + + if (MOD_IN_USE) { + printk(KERN_INFO “%s: In use, disconnect pending.\n”, proc); + while (MOD_IN_USE) + interruptible_sleep_on_timeout (&wq, HZ); + printk(KERN_INFO “%s: Released, wait.\n”, proc); +// interruptible_sleep_on_timeout (&wq, HZ*10); + } + video_unregister_device(&ibmcam->vdev); + printk(KERN_INFO “%s: Video dereg’d, wait.\n”, proc); +// interruptible_sleep_on_timeout (&wq, HZ*10); + + /* Free the memory */ + if (debug > 0) + printk(KERN_DEBUG “%s: freeing ibmcam=%p\n”, proc, ibmcam); + kfree(ibmcam); + + printk(KERN_INFO “%s: Memory freed, wait.\n”, proc); +// interruptible_sleep_on_timeout (&wq, HZ*10); + + printk(KERN_INFO “IBM USB camera disconnected.\n”); +} + +static struct usb_driver ibmcam_driver = { + “ibmcam”, + usb_ibmcam_probe, + usb_ibmcam_disconnect, + { NULL, NULL } +}; + +int usb_ibmcam_init(void) +{ + 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 -urN linux/drivers/usb/ibmcam.h linux.usb/drivers/usb/ibmcam.h — linux/drivers/usb/ibmcam.h Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/ibmcam.h Tue Jan 25 00:08:39 2000 @@ -0,0 +1,222 @@ +/* + * 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) (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 = 352) { \ + mx=351; \ + limiter++; \ + } \ + 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) 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); \ +} + +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 */ + 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 */ +}; + +struct usb_ibmcam { + struct video_device vdev; + + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; + + struct semaphore lock; + int user; /* user count for exclusive use */ + + int initialized; /* Had we already sent init sequence? */ + int streaming; /* Are we streaming Isochronous? */ + int grabbing; /* Are we grabbing? */ + + 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 */ + wait_queue_head_t remove_ok; /* Wait here until removal is safe */ + + /* + * 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 -urN linux/drivers/usb/inits.h linux.usb/drivers/usb/inits.h — linux/drivers/usb/inits.h Fri Apr 30 08:20:30 1999 +++ linux.usb/drivers/usb/inits.h Wed Dec 31 17: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 -urN linux/drivers/usb/inode.c linux.usb/drivers/usb/inode.c — linux/drivers/usb/inode.c Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/inode.c Tue Jan 25 00:08:39 2000 @@ -0,0 +1,729 @@ +/*****************************************************************************/ + +/* + * inode.c — Inode/Dentry functions for the USB device file system. + * + * Copyright (C) 2000 + * Thomas Sailer ([email protected]) + * + * 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: linux-2.2.14-usb.diff,v 1.1 2000/01/28 02:51:18 trini Exp $ + * + * History: + * 0.1 04.01.2000 Created + */ + +/*****************************************************************************/ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include + +#include “usb.h” +#include “usbdevice_fs.h” + +/* ——————————————————————— */ + +static LIST_HEAD(superlist); + +extern struct inode_operations usbdevfs_bus_inode_operations; + +static struct inode_operations devices_inode_operations = { + &usbdevfs_devices_fops +}; + +static struct inode_operations drivers_inode_operations = { + &usbdevfs_drivers_fops +}; + +struct special { + const char *name; + struct inode_operations *iops; + struct list_head inodes; +}; + +static struct special special[] = { + { “devices”, &devices_inode_operations, }, + { “drivers”, &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] ‘9’ || + name[1] ‘9’ || + 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 127 || busnum > 255) + return; + inode = iget(sb, IDEVICE | (busnum 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 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 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_op = NULL; + 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 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 = { + usbdevfs_revalidate, /* d_revalidate */ + NULL, /* d_hash */ + NULL, /* d_compare */ +}; + +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 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 127) + return ERR_PTR(-ENOENT); + inode = iget(dir->i_sb, IDEVICE | (dir->i_ino & (0xff 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) f_pos++; + i++; + /* fall through */ + + case 1: + if (filldir(dirent, “..”, 2, i, IROOT) f_pos++; + i++; + /* fall through */ + + default: + + while (i >= 2 && i f_pos-2]; + if (filldir(dirent, spec->name, strlen(spec->name), i, ISPECIAL | (filp->f_pos-2+IROOT)) f_pos++; + i++; + } + if (i 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) 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)) f_pos++; + } + for (i = 0; i maxchild; i++) { + if (!dev->children[i]) + continue; + pos = bus_readdir(dev->children[i], ino, pos, filp, dirent, filldir); + if (pos 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) f_pos++; + /* fall through */ + + case 1: + if (filldir(dirent, “..”, 2, filp->f_pos, IROOT) f_pos++; + /* fall through */ + + default: + lock_kernel(); + bus = usbdevfs_findbus(IBUSNR(ino)); + bus_readdir(bus->root_hub, IDEVICE | ((bus->busnum & 0xff) 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 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_inode(struct inode *inode) +{ +} + +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)); + MOD_DEC_USE_COUNT; +} + +static int usbdevfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +{ + struct statfs tmp; + + tmp.f_type = USBDEVICE_SUPER_MAGIC; + tmp.f_bsize = PAGE_SIZE/sizeof(long); /* ??? */ + tmp.f_blocks = 0; + tmp.f_bfree = 0; + tmp.f_bavail = 0; + tmp.f_files = 0; + tmp.f_ffree = 0; + tmp.f_namelen = NAME_MAX; + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; +} + +static struct super_operations usbdevfs_sops = { + usbdevfs_read_inode, + NULL, + usbdevfs_put_inode, + NULL, + NULL, + usbdevfs_put_super, + NULL, + usbdevfs_statfs, + NULL +}; + +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 */ + MOD_INC_USE_COUNT; + lock_super(s); + 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); + if (!s->s_root) + goto out_no_root; + list_add_tail(&s->u.usbdevfs_sb.slist, &superlist); + unlock_super(s); + for (i = 0; i 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); + s->s_dev = 0; + unlock_super(s); + MOD_DEC_USE_COUNT; + return NULL; + + opterr: + printk(KERN_WARNING “usbdevfs: mount parameter error\n”); + s->s_dev = 0; + return NULL; +} + +static struct file_system_type usbdevice_fs_type = { + “usbdevfs”, + 0, + usbdevfs_read_super, + NULL +}; + +/* ——————————————————————— */ + +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(); +} + +/* ——————————————————————— */ + +static struct proc_dir_entry *usbdir = NULL; + +int __init usbdevfs_init(void) +{ + int ret; + + for (ret = 0; ret , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR(“Vojtech Pavlik “); + +#ifndef MODULE +EXPORT_SYMBOL(input_register_device); +EXPORT_SYMBOL(input_unregister_device); +EXPORT_SYMBOL(input_register_handler); +EXPORT_SYMBOL(input_unregister_handler); +EXPORT_SYMBOL(input_open_device); +EXPORT_SYMBOL(input_close_device); +EXPORT_SYMBOL(input_event); +#endif + +static struct input_dev *input_dev = NULL; +static struct input_handler *input_handler = NULL; + +static int input_number = 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) || (value == dev->abs[code])) + 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 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]); +} + +void input_register_device(struct input_dev *dev) +{ + struct input_handler *handler = input_handler; + +/* + * 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. + */ + + MOD_INC_USE_COUNT; + dev->number = input_number++; + dev->next = input_dev; + input_dev = dev; + +/* + * Notify handlers. + */ + + while (handler) { + handler->connect(handler, dev); + handler = handler->next; + } +} + +void input_unregister_device(struct input_dev *dev) +{ + struct input_handle *handle = dev->handle; + struct input_dev **devptr = &input_dev; + +/* + * Kill any pending repeat timers. + */ + + del_timer(&dev->timer); + +/* + * Notify handlers. + */ + + while (handle) { + handle->handler->disconnect(handle); + handle = handle->dnext; + } + +/* + * Remove the device. + */ + + while (*devptr && (*devptr != dev)) + devptr = &((*devptr)->next); + *devptr = (*devptr)->next; + + input_number–; + MOD_DEC_USE_COUNT; +} + +void input_register_handler(struct input_handler *handler) +{ + struct input_dev *dev = input_dev; + +/* + * Add the handler. + */ + + handler->next = input_handler; + input_handler = handler; + +/* + * Notify it about all existing devices. + */ + + while (dev) { + handler->connect(handler, dev); + dev = dev->next; + } +} + +void input_unregister_handler(struct input_handler *handler) +{ + struct input_handler **handlerptr = &input_handler; + struct input_handle *handle = handler->handle; + +/* + * Tell the handler to disconnect from all devices it keeps open. + */ + + while (handle) { + handler->disconnect(handle); + handle = handle->hnext; + } + +/* + * Remove it. + */ + + while (*handlerptr && (*handlerptr != handler)) + handlerptr = &((*handlerptr)->next); + + *handlerptr = (*handlerptr)->next; + +} + +void input_open_device(struct input_handle *handle) +{ + handle->dnext = handle->dev->handle; + handle->hnext = handle->handler->handle; + handle->dev->handle = handle; + handle->handler->handle = handle; + + if (handle->dev->open) + handle->dev->open(handle->dev); +} + +void input_close_device(struct input_handle *handle) +{ + struct input_handle **handleptr; + + if (handle->dev->close) + handle->dev->close(handle->dev); +/* + * Remove from device list of handles. + */ + + handleptr = &handle->dev->handle; + + while (*handleptr && (*handleptr != handle)) + handleptr = &((*handleptr)->dnext); + *handleptr = (*handleptr)->dnext; + +/* + * Remove from handler list of handles. + */ + + handleptr = &handle->handler->handle; + + while (*handleptr && (*handleptr != handle)) + handleptr = &((*handleptr)->hnext); + *handleptr = (*handleptr)->hnext; +} + + +#ifdef MODULE +int init_module(void) +#else +int __init input_init(void) +#endif +{ +#ifndef MODULE +#ifdef CONFIG_INPUT_KEYBDEV + keybdev_init(); +#endif +#ifdef CONFIG_INPUT_MOUSEDEV + mousedev_init(); +#endif +#ifdef CONFIG_INPUT_JOYDEV + joydev_init(); +#endif +#ifdef CONFIG_INPUT_EVDEV + evdev_init(); +#endif +#endif + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ +} +#endif diff -urN linux/drivers/usb/joydev.c linux.usb/drivers/usb/joydev.c — linux/drivers/usb/joydev.c Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/joydev.c Tue Jan 25 00:08:39 2000 @@ -0,0 +1,487 @@ +/* + * joydev.c Version 0.1 + * + * Copyright (c) 1999 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_MAJOR 15 +#define JOYDEV_BUFFER_SIZE 64 + +struct joydev { + char name[32]; + int used; + struct input_handle handle; + int minor; + wait_queue_head_t wait; + 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 unsigned long joydev_minors = 0; +static struct joydev *joydev_base[BITS_PER_LONG]; + +MODULE_AUTHOR(“Vojtech Pavlik “); +MODULE_SUPPORTED_DEVICE(“js”); + +static int js_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 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; + + return value; +} + +static void js_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 keymap[code – BTN_MISC]; + event.value = value; + break; + + case EV_ABS: + event.type = JS_EVENT_AXIS; + event.number = joydev->absmap[code]; + event.value = js_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; + + if (list->fasync) + kill_fasync(list->fasync, SIGIO, POLL_IN); + + 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 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->used) { + clear_bit(list->joydev->minor, &joydev_minors); + kfree(list->joydev); + } + + kfree(list); + + MOD_DEC_USE_COUNT; + return 0; +} + +static int joydev_open(struct inode *inode, struct file *file) +{ + struct joydev_list *list; + int i = MINOR(inode->i_rdev); + + if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR) + return -EINVAL; + + if (i > BITS_PER_LONG || !test_bit(i, &joydev_minors)) + return -ENODEV; + + if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL))) + return -ENOMEM; + + memset(list, 0, sizeof(struct joydev_list)); + + list->joydev = joydev_base[i]; + list->next = joydev_base[i]->list; + joydev_base[i]->list = list; + + file->private_data = list; + + list->joydev->used++; + + MOD_INC_USE_COUNT; + 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 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 = ((js_correct(input->abs[ABS_X], &joydev->corr[0]) / 256) + 128) >> joydev->glue.JS_CORR.x; + data.y = ((js_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 nabs + joydev->nkey && retval + sizeof(struct js_event) startup 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 = js_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) 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; +} + +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 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; + + 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 name) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + if (copy_to_user((char *) arg, joydev->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 int joydev_connect(struct input_handler *handler, struct input_dev *dev) +{ + struct joydev *joydev; + int i, j; + + 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 -1; + + if (!(joydev = kmalloc(sizeof(struct joydev), GFP_KERNEL))) + return -1; + + memset(joydev, 0, sizeof(struct joydev)); + + init_waitqueue_head(&joydev->wait); + + if (joydev_minors == -1) { + printk(“Can’t register new joystick – 32 devices already taken.\n”); + return -1; + } + + sprintf(joydev->name, “joydev%d”, joydev->minor); + + joydev->handle.dev = dev; + joydev->handle.handler = handler; + joydev->handle.private = joydev; + + joydev->used = 1; + + for (i = 0; i absbit)) { + joydev->absmap[i] = joydev->nabs; + joydev->abspam[joydev->nabs] = i; + joydev->nabs++; + } + + for (i = BTN_JOYSTICK – BTN_MISC; i keybit)) { + joydev->keymap[i] = joydev->nkey; + joydev->keypam[joydev->nkey] = i + BTN_MISC; + joydev->nkey++; + } + + for (i = 0; i keybit)) { + joydev->keymap[i] = joydev->nkey; + joydev->keypam[joydev->nkey] = i + BTN_MISC; + joydev->nkey++; + } + + joydev->minor = ffz(joydev_minors); + set_bit(joydev->minor, &joydev_minors); + joydev_base[joydev->minor] = joydev; + + for (i = 0; i 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 absmax[j] – dev->absmin[j]) / 2 – 2 * dev->absflat[j]); + joydev->corr[i].coef[3] = (1 absmax[j] – dev->absmin[j]) / 2 – 2 * dev->absflat[j]); + } + + input_open_device(&joydev->handle); + + printk(“%s: Joystick device for input%d on /dev/js%d\n”, joydev->name, dev->number, joydev->minor); + + return 0; +} + +static void joydev_disconnect(struct input_handle *handle) +{ + struct joydev *joydev = handle->private; + + input_close_device(handle); + + if (!–joydev->used) { + clear_bit(joydev->minor, &joydev_minors); + kfree(joydev); + } +} + +static struct input_handler joydev_handler = { + event: js_event, + connect: joydev_connect, + disconnect: joydev_disconnect, +}; + +#ifdef MODULE +void cleanup_module(void) +{ + input_unregister_handler(&joydev_handler); + if (unregister_chrdev(JOYSTICK_MAJOR, “js”)) + printk(KERN_ERR “js: can’t unregister device\n”); +} + +int init_module(void) +#else +int __init joydev_init(void) +#endif +{ + if (register_chrdev(JOYDEV_MAJOR, “js”, &joydev_fops)) { + printk(KERN_ERR “joydev: unable to get major %d for joystick\n”, JOYDEV_MAJOR); + return -EBUSY; + } + input_register_handler(&joydev_handler); + + return 0; +} diff -urN linux/drivers/usb/keybdev.c linux.usb/drivers/usb/keybdev.c — linux/drivers/usb/keybdev.c Wed Dec 31 17:00:00 1969 +++ linux.usb/drivers/usb/keybdev.c Tue Jan 25 00:08:39 2000 @@ -0,0 +1,173 @@ +/* + * keybdev.c Version 0.1 + * + * Copyright (c) 1999 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 + +#ifdef CONFIG_X86 + +static unsigned char keybdev_x86_e0s[] = + { 0x1c, 0x1d, 0x35, 0x2a, 0x38, 0x39, 0x47, 0x48, + 0x49, 0x4b, 0x4d, 0x4f, 0x50, 0x51, 0x52, 0x53, + 0x26, 0x25, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x00, + 0x23, 0x24, 0x25, 0x26, 0x27 }; + +#elif CONFIG_MAC_KEYBOARD + +static unsigned char keybdev_mac_codes[256] = + { 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,105, 10,103,111, 0, 0, 0, 0, 0, 0, 0, + 76,125, 75, 0,124, 0,115, 62,116, 59, 60,119, 61,121,114,117, + 0, 0, 0, 0,127, 24, 0,113, 0, 0, 0, 0, 0, 55, 55, 0 }; + +#endif + +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 || code > 255) return; + +#ifdef CONFIG_X86 + + if (code >= 189) { + printk(KERN_WARNING “keybdev.c: can’t emulate keycode %d\n”, code); + return; + } else if (code >= 162) { + handle_scancode(0xe0, 1); + handle_scancode(code – 161, down); + } else if (code >= 125) { + handle_scancode(0xe0, 1); + handle_scancode(code – 34, down); + } else if (code == 119) { + handle_scancode(0xe1, 1); + handle_scancode(0x1d, down); + handle_scancode(0x45, down); + } else if (code >= 96) { + handle_scancode(0xe0, 1); + handle_scancode(keybdev_x86_e0s[code – 96], down); + if (code == 99) { + handle_scancode(0xe0, 1); + handle_scancode(0x37, down); + } + } else handle_scancode(code, down); + +#elif CONFIG_MAC_KEYBOARD + + if (code evbit) || !test_bit(KEY_A, dev->keybit) || !test_bit(KEY_Z, dev->keybit)) + return -1; + + if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL))) + return -1; + memset(handle, 0, sizeof(struct input_handle)); + + handle->dev = dev; + handle->handler = handler; + + input_open_device(handle); + + printk(“keybdev.c: Adding keyboard: input%d\n”, dev->number); + + return 0; +} + +static void keybdev_disconnect(struct input_handle *handle) +{ + printk(“keybdev.c: Removing keyboard: input%d\n”, handle->dev->number); + + input_close_device(handle); + + kfree(handle); +} + +struct input_handler keybdev_handler = { + event: keybdev_event, + connect: keybdev_connect, + disconnect: keybdev_disconnect, +}; + +#ifdef MODULE +void cleanup_module(void) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) + kbd_ledfunc = NULL; +#endif + input_unregister_handler(&keybdev_handler); +} +int init_module(void) +#else +int __init keybdev_init(void) +#endif +{ + input_register_handler(&keybdev_handler); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) + kbd_ledfunc = keybdev_ledfunc; +#endif + return 0; +} diff -urN linux/drivers/usb/keyboard.c linux.usb/drivers/usb/keyboard.c — linux/drivers/usb/keyboard.c Mon Apr 26 13:35:01 1999 +++ linux.usb/drivers/usb/keyboard.c Wed Dec 31 17: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)) 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 > 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 repeat_key = 0; – for(i = 0; i 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 -urN linux/drivers/usb/keymap.c linux.usb/drivers/usb/keymap.c — linux/drivers/usb/keymap.c Mon Apr 26 12:19:05 1999 +++ linux.usb/drivers/usb/keymap.c Wed Dec 31 17: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 -urN linux/drivers/usb/maps/fixup.map linux.usb/drivers/usb/maps/fixup.map — linux/drivers/usb/maps/fixup.map Fri Apr 16 15:38:24 1999 +++ linux.usb/drivers/usb/maps/fixup.map Wed Dec 31 17: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 -urN linux/drivers/usb/maps/serial.map linux.usb/drivers/usb/maps/serial.map — linux/drivers/usb/maps/serial.map Mon Apr 19 11:47:54 1999 +++ linux.usb/drivers/usb/maps/serial.map Wed Dec 31 17: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 -urN linux/drivers/usb/maps/usb.map linux.usb/drivers/usb/maps/usb.map — linux/drivers/usb/maps/usb.map Fri Apr 16 15:38:24 1999 +++ linux.usb/drivers/usb/maps/usb.map Wed Dec 31 17: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 -urN linux/drivers/usb/mkmap linux.usb/drivers/usb/mkmap — linux/drivers/usb/mkmap Mon Apr 19 11:53:27 1999 +++ linux.usb/drivers/usb/mkmap Wed Dec 31 17:00:00 1969 @@ -1,83 +0,0 @@ -#!/usr/bin/perl – -($ME = $0) =~ s|.*/||; – -$file = “maps/serial.map”; -$line = 1; -open(PC, $file) || die(“$!”); -while(