diff -u --recursive --new-file v2.3.33/linux/CREDITS linux/CREDITS --- v2.3.33/linux/CREDITS Tue Dec 14 01:27:23 1999 +++ linux/CREDITS Mon Dec 20 14:44:50 1999 @@ -480,6 +480,11 @@ D: AX25-HOWTO, HAM-HOWTO, IPX-HOWTO, NET-2-HOWTO D: ax25-utils maintainer. +N: Peter Denison +E: peterd@pnd-pc.demon.co.uk +W: http://www.pnd-pc.demon.co.uk/promise/ +D: Promise DC4030VL caching HD controller drivers + N: Todd J. Derr E: tjd@fore.com W: http://www.wordsmith.org/~tjd @@ -616,7 +621,7 @@ S: Hungary N: Jürgen Fischer -E: fischer@et-inf.fho-emden.de (=?iso-8859-1?q?J=FCrgen?= Fischer) +E: fischer@norbit.de (=?iso-8859-1?q?J=FCrgen?= Fischer) D: Author of Adaptec AHA-152x SCSI driver S: Schulstraße 18 S: 26506 Norden @@ -727,7 +732,7 @@ D: Many other patches, documentation files, mini kernels, utilities, ... N: John E. Gotts -E: jgotts@engin.umich.edu +E: jgotts@linuxsavvy.com D: kernel hacker S: 8124 Constitution Apt. 7 S: Sterling Heights, Michigan 48313 @@ -820,6 +825,7 @@ E: andre@suse.com D: Random SMP kernel hacker... D: Uniform Multi-Platform E-IDE driver +D: AEC6210UF Ultra33 D: Aladdin 1533/1543(C) chipset D: Active-Chipset maddness.......... D: HighPoint HPT343/5 Ultra/33 & HPT366 Ultra/66 chipsets @@ -827,7 +833,8 @@ D: Promise PDC20246/20247 & PDC20262 chipsets D: SiS5513 Ultra/66/33 chipsets D: VIA 82C586/596/686 chipsets -S: Nashville, TN +S: 580 Second Street, Suite 2 +S: Oakland, CA S: USA N: Jochen Hein @@ -1075,12 +1082,13 @@ S: The Netherlands N: Karl Keyte -E: kkeyte@koft.rhein-main.de -E: kkeyte@esoc.esa.de +E: karl@koft.com D: Disk usage statistics and modifications to line printer driver -S: Erbacher Strasse 6 -S: D-64283 Darmstadt -S: Germany +S: 26a Sheen Road +S: Richmond +S: Surrey +S: TW9 1AE +S: United Kingdom N: Russell King E: rmk@arm.uk.linux.org @@ -2376,6 +2384,13 @@ D: Xiafs filesystem [defunct] S: 542 West 112th Street, 5N S: New York, New York 10025 +S: USA + +N: Victor Yodaiken +E: yodaiken@fsmlabs.com +D: RTLinux (RealTime Linux) +S: POB 1822 +S: Socorro NM, 87801 S: USA N: Eric Youngdale diff -u --recursive --new-file v2.3.33/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.3.33/linux/Documentation/Configure.help Tue Dec 14 01:27:23 1999 +++ linux/Documentation/Configure.help Mon Dec 20 15:43:01 1999 @@ -449,6 +449,8 @@ to the SCSI protocol. If you have an SCSI tape drive however, you can say N here. + This now includes the OnStream DI-30 tape drive support. + If you say Y here, the tape drive will be identified at boot time along with other IDE devices, as "hdb" or "hdc", or something similar, and will be mapped to a character device such as "ht0" @@ -5549,6 +5551,9 @@ channel=1..? meaningful in adhoc mode all other parameters can be set via proc interface These parameters belong to .._card module, but alas, they are here + if you have problems with screwin up card, both_bap_lock=1 is conservative + value (performance hit 15%) + for any other configuration options look at ..._proc module Aironet 4500/4800 ISA/PCI/PNP/365 support CONFIG_AIRONET4500_NONCS @@ -5604,7 +5609,8 @@ NOTE: it takes lot of memory. Compile it as module and remove after configuration module: aironet4500_proc - + additional info: look into drivers/net/aironet4500_rids.c + this is quite human-readable(no need to know C) @@ -7880,36 +7886,15 @@ 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 and some others) support? -CONFIG_USB_OHCI +OHCI-HCD (compaq and some others) support? +CONFIG_USB_OHCI_HCD The Open Host Controller Interface is a standard by Compaq 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 conform to this standard. - There are currently two OHCI drivers in development. You should - compile at most one. The other one is "OHCI-HCD (other OHCI opt. - Virt. Root Hub) support?", below. - - You may want to read the file drivers/usb/README.ohci. - - 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. - -Enable tons of OHCI debugging output -CONFIG_USB_OHCI_DEBUG - Say Y here in order to have the OHCI code generate verbose debugging - output. - -OHCI-HCD (other OHCI opt. Virt. Root Hub) support? -CONFIG_USB_OHCI_HCD - This is an alternative driver for USB PC hardware (also called USB - host controller) which complies with Compaq's Open Host Controller - Interface. You may want to read the file - drivers/usb/README.ohci_hcd. + You may want to read the file drivers/usb/README.ohci_hcd. There are currently two OHCI drivers in development. You should compile at most one. The other one is "OHCI (compaq and some others) @@ -7920,29 +7905,6 @@ The module will be called usb-ohci-hcd.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -OHCI-HCD Virtual Root Hub -CONFIG_USB_OHCI_VROOTHUB - The virtual root hub support is currently unstable, so you probably - want to say N unless you are a hacker. But you aren't a hacker since - you are reading help texts. - -Enable lots of ISOC debugging output -CONFIG_USB_DEBUG_ISOC - Say Y here if you want to get lots of debugging output related to - the USB code. - -USB hub support -CONFIG_USB_HUB - Say Y here if you want to connect several USB devices to a single - USB port. You will need an USB hub to do this. - - 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 hub.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. - USB mouse support CONFIG_USB_MOUSE Say Y here if you want to connect a USB mouse to your computer's USB @@ -8086,6 +8048,13 @@ Note that you must say Y to "/proc filesystem support" below for this to work. +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. + ACPI support CONFIG_ACPI Advanced Configuration and Power Interface (ACPI) is an interface @@ -10209,11 +10178,6 @@ 11) exchange RAM chips 12) exchange the motherboard. - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - If you want to compile it as a module, say M here and read - Documentation/modules.txt. The module will be called apm.o. - Ignore USER SUSPEND CONFIG_APM_IGNORE_USER_SUSPEND This option will ignore USER SUSPEND requests. On machines with a @@ -10258,17 +10222,6 @@ backlight at all, or it might print a lot of errors to the console, especially if you are using gpm. -Power off on shutdown -CONFIG_APM_POWER_OFF - Enable the ability to power off the computer after the Linux kernel - is halted. You will need software (e.g., a suitable version of the - halt(8) command ("man 8 halt")) to cause the computer to power down. - Recent versions of the sysvinit package available from - ftp://metalab.unc.edu/pub/Linux/system/daemons/init/ contain support - for this ("halt -p" shuts down Linux and powers off the computer, if - executed from runlevel 0). As with the other APM options, this - option may not work reliably with some APM BIOS implementations. - Ignore multiple suspend/standby events CONFIG_APM_IGNORE_MULTIPLE_SUSPEND This option is necessary on the IBM Thinkpad 560, but should work on @@ -10306,6 +10259,20 @@ many of the newer IBM Thinkpads. If you experience hangs when you suspend, try setting this to Y. Otherwise, say N. +Entry point offset fix (some Acer laptops) +CONFIG_APM_BAD_ENTRY_OFFSET + Some implementations of the APM BIOS provide the driver with a bad + entry point offset. If you set this option to Y, then the upper + sixteen bits of the offset will be set to zero. This is usually + unnecessary but harmless. This is required for the Acer Travelmate + 510DX, Travelmate 510T and Extensa 503T. For others, say N. + +Use real mode APM BIOS call to power off +CONFIG_APM_REAL_MODE_POWER_OFF + Use real mode APM BIOS calls to switch off the computer. This is + a work-around for a number of buggy BIOSes. Switch this option on if + your computer crashes instead of powering off properly. + Watchdog Timer Support CONFIG_WATCHDOG If you say Y here (and to one of the following options) and create a @@ -13179,6 +13146,58 @@ The kHTTPd is experimental. Be careful when using it on a production machine. Also note that kHTTPd doesn't support virtual servers yet. + +I2C support +CONFIG_I2C + I2C (pronounce: I-square-C) is a slow bus protocol developed by + Philips. SMBus, or System Management Bus is a sub-protocol of I2C. + + Both I2C and SMBus are supported here. You will need this for + hardware sensors support, and in the future for Video for Linux + support. + + Beside this option, you will also need to select specific drivers + for your bus adapter(s). + +I2C bit-banging interfaces +CONFIG_I2C_ALGOBIT + This allows you to use a range of I2C adapters called bit-banging + adapters. Why they are called so is rather technical and uninteresting; + but you need to select this if you own one of the adapters listed + under it. + +Philips style parallel port adapter +CONFIG_I2C_PHILIPSPAR + This supports parallel-port I2C adapters made by Philips. Unless you + own such an adapter, you do not need to select this. + +ELV adapter +CONFIG_I2C_ELV + This supports parallel-port I2C adapters called ELV. Unless you + own such an adapter, you do not need to select this. + +Velleman K9000 adapter +CONFIG_I2C_VELLEMAN + This supports the Velleman K9000 parallel-port I2C adapter. Unless + you own such an adapter, you do not need to select this. + +I2C PCF 8584 interfaces +CONFIG_I2C_ALGOPCF + This allows you to use a range of I2C adapters called PCF + adapters. Why they are called so is rather technical and uninteresting; + but you need to select this if you own one of the adapters listed + under it. + +Elektor ISA card +CONFIG_I2C_ELEKTOR + This supports the PCF8584 ISA bus I2C adapter. Unless you own such + an adapter, you do not need to select this. + +I2C device interface +CONFIG_I2C_CHARDEV + Here you find the drivers which allow you to use the i2c-* device + files, usually found in the /dev directory on your system. They + make it possible to have user-space programs use the I2C bus. # # A couple of things I keep forgetting: diff -u --recursive --new-file v2.3.33/linux/Documentation/devices.txt linux/Documentation/devices.txt --- v2.3.33/linux/Documentation/devices.txt Sat Oct 9 11:47:49 1999 +++ linux/Documentation/devices.txt Thu Dec 16 14:00:34 1999 @@ -1,21 +1,20 @@ LINUX ALLOCATED DEVICES Maintained by H. Peter Anvin - Last revised: August 10, 1998 + Last revised: December 16, 1999 This list is the Linux Device List, the official registry of allocated device numbers and /dev directory nodes for the Linux operating system. The latest version of this list is included with the Linux kernel -sources in LaTeX and ASCII form. It is also available separately from -ftp://ftp.kernel.org/pub/linux/docs/device-list/. In case of -discrepancy between the text and LaTeX versions, the LaTeX version is -authoritative. - -This document is included by reference into the Linux Filesystem -Standard (FSSTND). The FSSTND is available from -ftp://tsx-11.mit.edu/pub/linux/docs/linux-standards/fsstnd/. +sources. It is also available separately from +http://www.kernel.org/pub/linux/docs/device-list/ or +ftp://ftp.kernel.org/pub/linux/docs/device-list/. The LaTeX version +of this document is no longer maintained. + +This document is included by reference into the Filesystem Hierarchy +Standard (FHS). The FHS is available from http://www.pathname.com/fhs/. Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga platform only. Allocations marked (68k/Atari) apply to Linux/68k on @@ -33,6 +32,7 @@ on this list. Any such information requests will be deleted without reply. + **** PLEASE READ THIS BEFORE SUBMITTING A DEVICE ENTRY **** To have a major number allocated, or a minor number in situations @@ -53,6 +53,13 @@ found to ensure I have all the requisite information to publish your device and avoid conflicts. +Finally, sometimes I have to play "namespace police." Please don't be +offended. I often get submissions for /dev names that would be bound +to cause conflicts down the road. I am trying to avoid getting in a +situation where we would have to suffer an incompatible forward +change. + + Your cooperation is appreciated. @@ -98,14 +105,14 @@ demand. block Floppy disks - 0 = /dev/fd0 Controller 1, drive 1 autodetect - 1 = /dev/fd1 Controller 1, drive 2 autodetect - 2 = /dev/fd2 Controller 1, drive 3 autodetect - 3 = /dev/fd3 Controller 1, drive 4 autodetect - 128 = /dev/fd4 Controller 2, drive 1 autodetect - 129 = /dev/fd5 Controller 2, drive 2 autodetect - 130 = /dev/fd6 Controller 2, drive 3 autodetect - 131 = /dev/fd7 Controller 2, drive 4 autodetect + 0 = /dev/fd0 Controller 0, drive 0, autodetect + 1 = /dev/fd1 Controller 0, drive 1, autodetect + 2 = /dev/fd2 Controller 0, drive 2, autodetect + 3 = /dev/fd3 Controller 0, drive 3, autodetect + 128 = /dev/fd4 Controller 1, drive 0, autodetect + 129 = /dev/fd5 Controller 1, drive 1, autodetect + 130 = /dev/fd6 Controller 1, drive 2, autodetect + 131 = /dev/fd7 Controller 1, drive 3, autodetect To specify format, add to the autodetect device number: 0 = /dev/fd? Autodetect format @@ -183,17 +190,11 @@ 0 = /dev/tty0 Current virtual console 1 = /dev/tty1 First virtual console - ... + ... 63 = /dev/tty63 63rd virtual console - 64 = /dev/ttyS0 First serial port - ... - 127 = /dev/ttyS63 64th serial port - 128 = /dev/ptyp0 OBSOLETE - ... - 191 = /dev/ptysf OBSOLETE - 192 = /dev/ttyp0 OBSOLETE - ... - 255 = /dev/ttysf OBSOLETE + 64 = /dev/ttyS0 First UART serial port + ... + 255 = /dev/ttyS191 192nd UART serial port Older versions of the Linux kernel used this major number for BSD PTY devices. As of Linux 2.1.115, this @@ -203,31 +204,31 @@ 0 = /dev/tty Current TTY device 1 = /dev/console System console 2 = /dev/ptmx PTY master multiplex - 64 = /dev/cua0 Callout device corresponding to ttyS0 - ... - 127 = /dev/cua63 Callout device corresponding to ttyS63 + 64 = /dev/cua0 Callout device for ttyS0 + ... + 255 = /dev/cua191 Callout device for ttyS191 (5,1) is /dev/console starting with Linux 2.1.71. See the section on terminal devices for more information on /dev/console. 6 char Parallel printer devices - 0 = /dev/lp0 First parallel printer (0x3bc) - 1 = /dev/lp1 Second parallel printer (0x378) - 2 = /dev/lp2 Third parallel printer (0x278) - - Not all computers have the 0x3bc parallel port; hence - the "first" printer may be either /dev/lp0 or - /dev/lp1. + 0 = /dev/lp0 Parallel printer on parport0 + 1 = /dev/lp1 Parallel printer on parport1 + ... + + Current Linux kernels no longer have a fixed mapping + between parallel ports and I/O addresses. Instead, + they are redirected through the parport multiplex layer. 7 char Virtual console capture devices 0 = /dev/vcs Current vc text contents 1 = /dev/vcs1 tty1 text contents - ... + ... 63 = /dev/vcs63 tty63 text contents 128 = /dev/vcsa Current vc text/attribute contents 129 = /dev/vcsa1 tty1 text/attribute contents - ... + ... 191 = /dev/vcsa63 tty63 text/attribute contents NOTE: These devices permit both read and write access. @@ -235,7 +236,7 @@ block Loopback devices 0 = /dev/loop0 First loopback device 1 = /dev/loop1 Second loopback device - ... + ... The loopback devices are used to mount filesystems not associated with block devices. The binding to the @@ -245,7 +246,7 @@ 0 = /dev/sda First SCSI disk whole disk 16 = /dev/sdb Second SCSI disk whole disk 32 = /dev/sdc Third SCSI disk whole disk - ... + ... 240 = /dev/sdp Sixteenth SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -255,28 +256,28 @@ 9 char SCSI tape devices 0 = /dev/st0 First SCSI tape, mode 0 1 = /dev/st1 Second SCSI tape, mode 0 - ... + ... 32 = /dev/st0l First SCSI tape, mode 1 33 = /dev/st1l Second SCSI tape, mode 1 - ... + ... 64 = /dev/st0m First SCSI tape, mode 2 65 = /dev/st1m Second SCSI tape, mode 2 - ... + ... 96 = /dev/st0a First SCSI tape, mode 3 97 = /dev/st1a Second SCSI tape, mode 3 ... 128 = /dev/nst0 First SCSI tape, mode 0, no rewind 129 = /dev/nst1 Second SCSI tape, mode 0, no rewind - ... + ... 160 = /dev/nst0l First SCSI tape, mode 1, no rewind 161 = /dev/nst1l Second SCSI tape, mode 1, no rewind - ... + ... 192 = /dev/nst0m First SCSI tape, mode 2, no rewind 193 = /dev/nst1m Second SCSI tape, mode 2, no rewind - ... + ... 224 = /dev/nst0a First SCSI tape, mode 3, no rewind 225 = /dev/nst1a Second SCSI tape, mode 3, no rewind - ... + ... "No rewind" refers to the omission of the default automatic rewind on device close. The MTREW or MTOFFL @@ -286,7 +287,7 @@ block Metadisk (RAID) devices 0 = /dev/md0 First metadisk group 1 = /dev/md1 Second metadisk group - ... + ... The metadisk driver is used to span a filesystem across multiple physical disks. @@ -326,6 +327,32 @@ 151 = /dev/led Front panel LEDs 153 = /dev/mergemem Memory merge device 154 = /dev/pmu Macintosh PowerBook power manager + 155 = /dev/isictl MultiTech ISICom serial control + 156 = /dev/lcd Front panel LCD display + 157 = /dev/ac Applicom Intl Profibus card + 158 = /dev/nwbutton Netwinder external button + 159 = /dev/nwdebug Netwinder debug interface + 160 = /dev/nwflash Netwinder flash memory + 161 = /dev/userdma User-space DMA access + 162 = /dev/smbus System Management Bus + 163 = /dev/lik Logitech Internet Keyboard + 164 = /dev/ipmo Intel Intelligent Platform Management + 165 = /dev/vmmon VMWare virtual machine monitor + 166 = /dev/i2o/ctl I2O configuration manager + 167 = /dev/specialix_sxctl Specialix serial control + 168 = /dev/tcldrv Technology Concepts serial control + 169 = /dev/specialix_rioctl Specialix RIO serial control + 170 = /dev/smapi IBM Thinkpad SMAPI + 171 = /dev/srripc QNX4 API IPC manager + 172 = /dev/usemaclone Semaphore clone device + 173 = /dev/ipmikcs Intelligent Platform Management + 174 = /dev/uctrl SPARCbook 3 microcontroller + 175 = /dev/agpgart AGP Graphics Address Remapping Table + 176 = /dev/gtrsc Gorgy Timing radio clock + 177 = /dev/cbm Serial CBM bus + 178 = /dev/jsflash JavaStation OS flash SIMM + 179 = /dev/xsvc High-speed shared-mem/semaphore service + 240-255 Reserved for local use 11 char Raw keyboard device 0 = /dev/kbd Raw keyboard device @@ -335,7 +362,7 @@ block SCSI CD-ROM devices 0 = /dev/sr0 First SCSI CD-ROM 1 = /dev/sr1 Second SCSI CD-ROM - ... + ... The prefix /dev/scd instead of /dev/sr has been used as well, and might make more sense. @@ -356,13 +383,17 @@ block MSCDEX CD-ROM callback support 0 = /dev/dos_cd0 First MSCDEX CD-ROM 1 = /dev/dos_cd1 Second MSCDEX CD-ROM - ... + ... - 13 char PC speaker + 13 char PC speaker (OBSOLETE) 0 = /dev/pcmixer Emulates /dev/mixer 1 = /dev/pcsp Emulates /dev/dsp (8-bit) 4 = /dev/pcaudio Emulates /dev/audio 5 = /dev/pcsp16 Emulates /dev/dsp (16-bit) + + The current PC speaker driver uses the Open Sound + System interface, and these devices are obsolete. + block 8-bit MFM/RLL/IDE controller 0 = /dev/xda First XT disk whole disk 64 = /dev/xdb Second XT disk whole disk @@ -370,13 +401,14 @@ Partitions are handled in the same way as IDE disks (see major number 3). - 14 char Sound card + 14 char Open Sound System (OSS) 0 = /dev/mixer Mixer control 1 = /dev/sequencer Audio sequencer 2 = /dev/midi00 First MIDI port 3 = /dev/dsp Digital audio 4 = /dev/audio Sun-compatible digital audio 6 = /dev/sndstat Sound card status information + 7 = /dev/audioctl SPARC audio control device 8 = /dev/sequencer2 Sequencer -- alternate device 16 = /dev/mixer1 Second soundcard mixer control 17 = /dev/patmgr0 Sequencer patch manager @@ -413,43 +445,43 @@ 17 char Chase serial card 0 = /dev/ttyH0 First Chase port 1 = /dev/ttyH1 Second Chase port - ... + ... block Optics Storage CD-ROM 0 = /dev/optcd Optics Storage CD-ROM 18 char Chase serial card - alternate devices - 0 = /dev/cuh0 Callout device corresponding to ttyH0 - 1 = /dev/cuh1 Callout device corresponding to ttyH1 - ... + 0 = /dev/cuh0 Callout device for ttyH0 + 1 = /dev/cuh1 Callout device for ttyH1 + ... block Sanyo CD-ROM 0 = /dev/sjcd Sanyo CD-ROM 19 char Cyclades serial card 0 = /dev/ttyC0 First Cyclades port - ... + ... 31 = /dev/ttyC31 32nd Cyclades port block "Double" compressed disk 0 = /dev/double0 First compressed disk - ... + ... 7 = /dev/double7 Eighth compressed disk 128 = /dev/cdouble0 Mirror of first compressed disk - ... + ... 135 = /dev/cdouble7 Mirror of eighth compressed disk See the Double documentation for the meaning of the mirror devices. 20 char Cyclades serial card - alternate devices - 0 = /dev/cub0 Callout device corresponding to ttyC0 - ... - 31 = /dev/cub31 Callout device corresponding to ttyC31 + 0 = /dev/cub0 Callout device for ttyC0 + ... + 31 = /dev/cub31 Callout device for ttyC31 block Hitachi CD-ROM (under development) 0 = /dev/hitcd Hitachi CD-ROM 21 char Generic SCSI access 0 = /dev/sg0 First generic SCSI device 1 = /dev/sg1 Second generic SCSI device - ... + ... Most distributions name these /dev/sga, /dev/sgb...; this sets an unnecessary limit of 26 SCSI devices in @@ -467,7 +499,7 @@ 22 char Digiboard serial card 0 = /dev/ttyD0 First Digiboard port 1 = /dev/ttyD1 Second Digiboard port - ... + ... block Second IDE hard disk/CD-ROM interface 0 = /dev/hdc Master: whole disk (or CD-ROM) 64 = /dev/hdd Slave: whole disk (or CD-ROM) @@ -476,8 +508,8 @@ interface (see major number 3). 23 char Digiboard serial card - alternate devices - 0 = /dev/cud0 Callout device corresponding to ttyD0 - 1 = /dev/cud1 Callout device corresponding to ttyD1 + 0 = /dev/cud0 Callout device for ttyD0 + 1 = /dev/cud1 Callout device for ttyD1 ... block Mitsumi proprietary CD-ROM 0 = /dev/mcd Mitsumi CD-ROM @@ -485,31 +517,31 @@ 24 char Stallion serial card 0 = /dev/ttyE0 Stallion port 0 card 0 1 = /dev/ttyE1 Stallion port 1 card 0 - ... + ... 64 = /dev/ttyE64 Stallion port 0 card 1 65 = /dev/ttyE65 Stallion port 1 card 1 ... 128 = /dev/ttyE128 Stallion port 0 card 2 129 = /dev/ttyE129 Stallion port 1 card 2 - ... + ... 192 = /dev/ttyE192 Stallion port 0 card 3 193 = /dev/ttyE193 Stallion port 1 card 3 - ... + ... block Sony CDU-535 CD-ROM 0 = /dev/cdu535 Sony CDU-535 CD-ROM 25 char Stallion serial card - alternate devices - 0 = /dev/cue0 Callout device corresponding to ttyE0 - 1 = /dev/cue1 Callout device corresponding to ttyE1 - ... - 64 = /dev/cue64 Callout device corresponding to ttyE64 - 65 = /dev/cue65 Callout device corresponding to ttyE65 - ... - 128 = /dev/cue128 Callout device corresponding to ttyE128 - 129 = /dev/cue129 Callout device corresponding to ttyE129 - ... - 192 = /dev/cue192 Callout device corresponding to ttyE192 - 193 = /dev/cue193 Callout device corresponding to ttyE193 + 0 = /dev/cue0 Callout device for ttyE0 + 1 = /dev/cue1 Callout device for ttyE1 + ... + 64 = /dev/cue64 Callout device for ttyE64 + 65 = /dev/cue65 Callout device for ttyE65 + ... + 128 = /dev/cue128 Callout device for ttyE128 + 129 = /dev/cue129 Callout device for ttyE129 + ... + 192 = /dev/cue192 Callout device for ttyE192 + 193 = /dev/cue193 Callout device for ttyE193 ... block First Matsushita (Panasonic/SoundBlaster) CD-ROM 0 = /dev/sbpcd0 Panasonic CD-ROM controller 0 unit 0 @@ -537,7 +569,7 @@ 16 = /dev/zqft0 Unit 0, rewind-on-close, compression 17 = /dev/zqft1 Unit 1, rewind-on-close, compression 18 = /dev/zqft2 Unit 2, rewind-on-close, compression - 19 = /dev/zqt3 Unit 3, rewind-on-close, compression + 19 = /dev/zqtf3 Unit 3, rewind-on-close, compression 20 = /dev/nzqft0 Unit 0, no rewind-on-close, compression 21 = /dev/nzqft1 Unit 1, no rewind-on-close, compression 22 = /dev/nzqft2 Unit 2, no rewind-on-close, compression @@ -546,10 +578,10 @@ 33 = /dev/rawqft1 Unit 1, rewind-on-close, no file marks 34 = /dev/rawqft2 Unit 2, rewind-on-close, no file marks 35 = /dev/rawqft3 Unit 3, rewind-on-close, no file marks - 32 = /dev/nrawqft0 Unit 0, no rewind-on-close, no file marks - 33 = /dev/nrawqft1 Unit 1, no rewind-on-close, no file marks - 34 = /dev/nrawqft2 Unit 2, no rewind-on-close, no file marks - 35 = /dev/nrawqft3 Unit 3, no rewind-on-close, no file marks + 36 = /dev/nrawqft0 Unit 0, no rewind-on-close, no file marks + 37 = /dev/nrawqft1 Unit 1, no rewind-on-close, no file marks + 38 = /dev/nrawqft2 Unit 2, no rewind-on-close, no file marks + 39 = /dev/nrawqft3 Unit 3, no rewind-on-close, no file marks block Third Matsushita (Panasonic/SoundBlaster) CD-ROM 0 = /dev/sbpcd8 Panasonic CD-ROM controller 2 unit 0 1 = /dev/sbpcd9 Panasonic CD-ROM controller 2 unit 1 @@ -564,7 +596,7 @@ char Atari SLM ACSI laser printer (68k/Atari) 0 = /dev/slm0 First SLM laser printer 1 = /dev/slm1 Second SLM laser printer - ... + ... block Fourth Matsushita (Panasonic/SoundBlaster) CD-ROM 0 = /dev/sbpcd12 Panasonic CD-ROM controller 3 unit 0 1 = /dev/sbpcd13 Panasonic CD-ROM controller 3 unit 1 @@ -574,7 +606,7 @@ 0 = /dev/ada First ACSI disk whole disk 16 = /dev/adb Second ACSI disk whole disk 32 = /dev/adc Third ACSI disk whole disk - ... + ... 240 = /dev/adp 16th ACSI disk whole disk Partitions are handled in the same way as for IDE @@ -584,8 +616,8 @@ 29 char Universal frame buffer 0 = /dev/fb0 First frame buffer 32 = /dev/fb1 Second frame buffer - ... - 240 = /dev/fb7 Eighth frame buffer + ... + 224 = /dev/fb7 Eighth frame buffer All additional minor numbers are reserved. @@ -619,13 +651,13 @@ ... 7 = /dev/rom7 Eighth ROM card (rw) 8 = /dev/rrom0 First ROM card (ro) - ... + ... 15 = /dev/rrom7 Eighth ROM card (ro) 16 = /dev/flash0 First flash memory card (rw) - ... + ... 23 = /dev/flash7 Eighth flash memory card (rw) 24 = /dev/rflash0 First flash memory card (ro) - ... + ... 31 = /dev/rflash7 Eighth flash memory card (ro) The read-write (rw) devices support back-caching @@ -636,14 +668,14 @@ 32 char Specialix serial card 0 = /dev/ttyX0 First Specialix port 1 = /dev/ttyX1 Second Specialix port - ... + ... block Philips LMS CM-206 CD-ROM 0 = /dev/cm206cd Philips LMS CM-206 CD-ROM 33 char Specialix serial card - alternate devices - 0 = /dev/cux0 Callout device corresponding to ttyX0 - 1 = /dev/cux1 Callout device corresponding to ttyX1 - ... + 0 = /dev/cux0 Callout device for ttyX0 + 1 = /dev/cux1 Callout device for ttyX1 + ... block Third IDE hard disk/CD-ROM interface 0 = /dev/hde Master: whole disk (or CD-ROM) 64 = /dev/hdf Slave: whole disk (or CD-ROM) @@ -656,7 +688,7 @@ 1 = /dev/scc1 First Z8530, second port 2 = /dev/scc2 Second Z8530, first port 3 = /dev/scc3 Second Z8530, second port - ... + ... In a previous version these devices were named /dev/sc1 for /dev/scc0, /dev/sc2 for /dev/scc1, and so @@ -688,17 +720,25 @@ 36 char Netlink support 0 = /dev/route Routing, device updates, kernel to user 1 = /dev/skip enSKIP security cache control + 3 = /dec/fwmonitor Firewall packet copies + 16 = /dev/tap0 First Ethertap device + ... + 31 = /dev/tap15 16th Ethertap device block MCA ESDI hard disk 0 = /dev/eda First ESDI disk whole disk 64 = /dev/edb Second ESDI disk whole disk - ... + ... Partitions are handled in the same way as IDE disks (see major number 3). 37 char IDE tape 0 = /dev/ht0 First IDE tape + 1 = /dev/ht1 Second IDE tape + ... 128 = /dev/nht0 First IDE tape, no rewind-on-close + 129 = /dev/nht1 Second IDE tape, no rewind-on-close + ... Currently, only one IDE tape drive is supported. @@ -708,7 +748,7 @@ 38 char Myricom PCI Myrinet board 0 = /dev/mlanai0 First Myrinet board 1 = /dev/mlanai1 Second Myrinet board - ... + ... This device is used for status query, board control and "user level packet I/O." This board is also @@ -719,7 +759,7 @@ 39 char ML-16P experimental I/O board 0 = /dev/ml16pa-a0 First card, first analog channel 1 = /dev/ml16pa-a1 First card, second analog channel - ... + ... 15 = /dev/ml16pa-a15 First card, 16th analog channel 16 = /dev/ml16pa-d First card, digital lines 17 = /dev/ml16pa-c0 First card, first counter/timer @@ -727,7 +767,7 @@ 19 = /dev/ml16pa-c2 First card, third counter/timer 32 = /dev/ml16pb-a0 Second card, first analog channel 33 = /dev/ml16pb-a1 Second card, second analog channel - ... + ... 47 = /dev/ml16pb-a15 Second card, 16th analog channel 48 = /dev/ml16pb-d Second card, digital lines 49 = /dev/ml16pb-c0 Second card, first counter/timer @@ -771,12 +811,12 @@ 43 char isdn4linux virtual modem 0 = /dev/ttyI0 First virtual modem - ... + ... 63 = /dev/ttyI63 64th virtual modem block Network block devices 0 = /dev/nb0 First network block device 1 = /dev/nb1 Second network block device - ... + ... Network Block Device is somehow similar to loopback devices: If you read from it, it sends packet accross @@ -786,14 +826,14 @@ the net, implementing block device in userland etc. 44 char isdn4linux virtual modem - alternate devices - 0 = /dev/cui0 Callout device corresponding to ttyI0 - ... - 63 = /dev/cui63 Callout device corresponding to ttyI63 + 0 = /dev/cui0 Callout device for ttyI0 + ... + 63 = /dev/cui63 Callout device for ttyI63 block Flash Translatio Layer (FTL) filesystems 0 = /dev/ftla FTL on first Memory Technology Device 16 = /dev/ftlb FTL on second Memory Technology Device 32 = /dev/ftlc FTL on third Memory Technology Device - ... + ... 240 = /dev/ftlp FTL on 16th Memory Technology Device Partitions are handled in the same way as for IDE @@ -802,14 +842,14 @@ 45 char isdn4linux ISDN BRI driver 0 = /dev/isdn0 First virtual B channel raw data - ... + ... 63 = /dev/isdn63 64th virtual B channel raw data 64 = /dev/isdnctrl0 First channel control/debug - ... + ... 127 = /dev/isdnctrl63 64th channel control/debug 128 = /dev/ippp0 First SyncPPP device - ... + ... 191 = /dev/ippp63 64th SyncPPP device 255 = /dev/isdninfo ISDN monitor interface @@ -826,7 +866,7 @@ 46 char Comtrol Rocketport serial card 0 = /dev/ttyR0 First Rocketport port 1 = /dev/ttyR1 Second Rocketport port - ... + ... block Parallel port ATAPI CD-ROM devices 0 = /dev/pcd0 First parallel port ATAPI CD-ROM 1 = /dev/pcd1 Second parallel port ATAPI CD-ROM @@ -834,9 +874,9 @@ 3 = /dev/pcd3 Fourth parallel port ATAPI CD-ROM 47 char Comtrol Rocketport serial card - alternate devices - 0 = /dev/cur0 Callout device corresponding to ttyR0 - 1 = /dev/cur1 Callout device corresponding to ttyR1 - ... + 0 = /dev/cur0 Callout device for ttyR0 + 1 = /dev/cur1 Callout device for ttyR1 + ... block Parallel port ATAPI disk devices 0 = /dev/pf0 First parallel port ATAPI disk 1 = /dev/pf1 Second parallel port ATAPI disk @@ -849,13 +889,13 @@ 48 char SDL RISCom serial card 0 = /dev/ttyL0 First RISCom port 1 = /dev/ttyL1 Second RISCom port - ... + ... block Reserved for Mylex DAC960 PCI RAID controller 49 char SDL RISCom serial card - alternate devices - 0 = /dev/cul0 Callout device corresponding to ttyL0 - 1 = /dev/cul1 Callout device corresponding to ttyL1 - ... + 0 = /dev/cul0 Callout device for ttyL0 + 1 = /dev/cul1 Callout device for ttyL1 + ... block Reserved for Mylex DAC960 PCI RAID controller 50 char Reserved for GLINT @@ -864,7 +904,7 @@ 51 char Baycom radio modem 0 = /dev/bc0 First Baycom radio modem 1 = /dev/bc1 Second Baycom radio modem - ... + ... block Reserved for Mylex DAC960 PCI RAID controller 52 char Spellcaster DataComm/BRI ISDN card @@ -921,7 +961,7 @@ 57 char Hayes ESP serial card 0 = /dev/ttyP0 First ESP port 1 = /dev/ttyP1 Second ESP port - ... + ... block Sixth IDE hard disk/CD-ROM interface 0 = /dev/hdk Master: whole disk (or CD-ROM) @@ -931,14 +971,25 @@ interface (see major number 3). 58 char Hayes ESP serial card - alternate devices - 0 = /dev/cup0 Callout device corresponding to ttyP0 - 1 = /dev/cup1 Callout device corresponding to ttyP1 - ... + 0 = /dev/cup0 Callout device for ttyP0 + 1 = /dev/cup1 Callout device for ttyP1 + ... block Reserved for logical volume manager 59 char sf firewall package 0 = /dev/firewall Communication with sf kernel module + block Generic PDA filesystem device + 0 = /dev/pda0 First PDA device + 1 = /dev/pda1 Second PDA device + ... + + The pda devices are used to mount filesystems on + remote pda's (basically slow handheld machines with + proprietary OS's and limited memory and storage + running small fs translation drivers) through serial / + IRDA / parallel links. + 60-63 LOCAL/EXPERIMENTAL USE Allocated for local/experimental use. For devices not assigned official numbers, these ranges should be @@ -972,7 +1023,7 @@ 0 = /dev/sdq 16th SCSI disk whole disk 16 = /dev/sdr 17th SCSI disk whole disk 32 = /dev/sds 18th SCSI disk whole disk - ... + ... 240 = /dev/sdaf 32nd SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -982,13 +1033,13 @@ 66 char YARC PowerPC PCI coprocessor card 0 = /dev/yppcpci0 First YARC card 1 = /dev/yppcpci1 Second YARC card - ... + ... block SCSI disk devices (32-47) 0 = /dev/sdag 33th SCSI disk whole disk 16 = /dev/sdah 34th SCSI disk whole disk 32 = /dev/sdai 35th SCSI disk whole disk - ... + ... 240 = /dev/sdav 48nd SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -1004,7 +1055,7 @@ 0 = /dev/sdaw 49th SCSI disk whole disk 16 = /dev/sdax 50th SCSI disk whole disk 32 = /dev/sday 51st SCSI disk whole disk - ... + ... 240 = /dev/sdbl 64th SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -1015,7 +1066,7 @@ 0 = /dev/capi20 Control device 1 = /dev/capi20.00 First CAPI 2.0 application 2 = /dev/capi20.01 Second CAPI 2.0 application - ... + ... 20 = /dev/capi20.19 19th CAPI 2.0 application ISDN CAPI 2.0 driver for use with CAPI 2.0 @@ -1025,7 +1076,7 @@ 0 = /dev/sdbm 64th SCSI disk whole disk 16 = /dev/sdbn 65th SCSI disk whole disk 32 = /dev/sdbo 66th SCSI disk whole disk - ... + ... 240 = /dev/sdcb 80th SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -1039,7 +1090,7 @@ 0 = /dev/sdcc 81st SCSI disk whole disk 16 = /dev/sdcd 82nd SCSI disk whole disk 32 = /dev/sdce 83th SCSI disk whole disk - ... + ... 240 = /dev/sdcr 96th SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -1059,7 +1110,7 @@ 0 = /dev/sdcs 97th SCSI disk whole disk 16 = /dev/sdct 98th SCSI disk whole disk 32 = /dev/sdcu 99th SCSI disk whole disk - ... + ... 240 = /dev/sddh 112nd SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -1069,26 +1120,26 @@ 71 char Computone IntelliPort II serial card 0 = /dev/ttyF0 IntelliPort II board 0, port 0 1 = /dev/ttyF1 IntelliPort II board 0, port 1 - ... + ... 63 = /dev/ttyF63 IntelliPort II board 0, port 63 64 = /dev/ttyF64 IntelliPort II board 1, port 0 65 = /dev/ttyF65 IntelliPort II board 1, port 1 - ... + ... 127 = /dev/ttyF127 IntelliPort II board 1, port 63 128 = /dev/ttyF128 IntelliPort II board 2, port 0 129 = /dev/ttyF129 IntelliPort II board 2, port 1 - ... + ... 191 = /dev/ttyF191 IntelliPort II board 2, port 63 192 = /dev/ttyF192 IntelliPort II board 3, port 0 193 = /dev/ttyF193 IntelliPort II board 3, port 1 - ... + ... 255 = /dev/ttyF255 IntelliPort II board 3, port 63 block SCSI disk devices (112-127) 0 = /dev/sddi 113th SCSI disk whole disk 16 = /dev/sddj 114th SCSI disk whole disk 32 = /dev/sddk 115th SCSI disk whole disk - ... + ... 240 = /dev/sddx 128th SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -1096,22 +1147,22 @@ partitions is 15. 72 char Computone IntelliPort II serial card - alternate devices - 0 = /dev/cuf0 Callout device corresponding to ttyF0 - 1 = /dev/cuf1 Callout device corresponding to ttyF1 - ... - 63 = /dev/cuf63 Callout device corresponding to ttyF63 - 64 = /dev/cuf64 Callout device corresponding to ttyF64 - 65 = /dev/cuf65 Callout device corresponding to ttyF65 - ... - 127 = /dev/cuf127 Callout device corresponding to ttyF127 - 128 = /dev/cuf128 Callout device corresponding to ttyF128 - 129 = /dev/cuf129 Callout device corresponding to ttyF129 - ... - 191 = /dev/cuf191 Callout device corresponding to ttyF191 - 192 = /dev/cuf192 Callout device corresponding to ttyF192 - 193 = /dev/cuf193 Callout device corresponding to ttyF193 - ... - 255 = /dev/cuf255 Callout device corresponding to ttyF255 + 0 = /dev/cuf0 Callout device for ttyF0 + 1 = /dev/cuf1 Callout device for ttyF1 + ... + 63 = /dev/cuf63 Callout device for ttyF63 + 64 = /dev/cuf64 Callout device for ttyF64 + 65 = /dev/cuf65 Callout device for ttyF65 + ... + 127 = /dev/cuf127 Callout device for ttyF127 + 128 = /dev/cuf128 Callout device for ttyF128 + 129 = /dev/cuf129 Callout device for ttyF129 + ... + 191 = /dev/cuf191 Callout device for ttyF191 + 192 = /dev/cuf192 Callout device for ttyF192 + 193 = /dev/cuf193 Callout device for ttyF193 + ... + 255 = /dev/cuf255 Callout device for ttyF255 73 char Computone IntelliPort II serial card - control devices 0 = /dev/ip2ipl0 Loadware device for board 0 @@ -1126,7 +1177,7 @@ 74 char SCI bridge 0 = /dev/SCI/0 SCI device 0 1 = /dev/SCI/1 SCI device 1 - ... + ... Currently for Dolphin Interconnect Solutions' PCI-SCI bridge. @@ -1134,16 +1185,16 @@ 75 char Specialix IO8+ serial card 0 = /dev/ttyW0 First IO8+ port, first card 1 = /dev/ttyW1 Second IO8+ port, first card - ... + ... 8 = /dev/ttyW8 First IO8+ port, second card - ... + ... 76 char Specialix IO8+ serial card - alternate devices - 0 = /dev/cuw0 Callout device corresponding to ttyW0 - 1 = /dev/cuw1 Callout device corresponding to ttyW1 - ... - 8 = /dev/cuw8 Callout device corresponding to ttyW8 - ... + 0 = /dev/cuw0 Callout device for ttyW0 + 1 = /dev/cuw1 Callout device for ttyW1 + ... + 8 = /dev/cuw8 Callout device for ttyW8 + ... 77 char ComScire Quantum Noise Generator 0 = /dev/qng ComScire Quantum Noise Generator @@ -1151,38 +1202,68 @@ 78 char PAM Software's multimodem boards 0 = /dev/ttyM0 First PAM modem 1 = /dev/ttyM1 Second PAM modem - ... + ... 79 char PAM Software's multimodem boards - alternate devices - 0 = /dev/cum0 Callout device corresponding to ttyM0 - 1 = /dev/cum1 Callout device corresponding to ttyM1 - ... + 0 = /dev/cum0 Callout device for ttyM0 + 1 = /dev/cum1 Callout device for ttyM1 + ... 80 char Photometrics AT200 CCD camera 0 = /dev/at200 Photometrics AT200 CCD camera + block I2O hard disk + 0 = /dev/i2o/hda First I2O hard disk, whole disk + 16 = /dev/i2o/hdb Second I2O hard disk, whole disk + ... + 240 = /dev/i2o/hdp 16th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 81 char video4linux 0 = /dev/video0 Video capture/overlay device - ... + ... 63 = /dev/video63 Video capture/overlay device 64 = /dev/radio0 Radio device - ... + ... 127 = /dev/radio63 Radio device 192 = /dev/vtx0 Teletext device - ... + ... 223 = /dev/vtx31 Teletext device 224 = /dev/vbi0 Vertical blank interrupt - ... + ... 255 = /dev/vbi31 Vertical blank interrupt + block I2O hard disk + 0 = /dev/i2o/hdq 17th I2O hard disk, whole disk + 16 = /dev/i2o/hdr 18th I2O hard disk, whole disk + ... + 240 = /dev/i2o/hdaf 32nd I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 82 char WiNRADiO communications receiver card 0 = /dev/winradio0 First WiNRADiO card 1 = /dev/winradio1 Second WiNRADiO card - ... + ... The driver and documentation may be obtained from http://www.proximity.com.au/~brian/winradio/ + block I2O hard disk + 0 = /dev/i2o/hdag 33rd I2O hard disk, whole disk + 16 = /dev/i2o/hdah 34th I2O hard disk, whole disk + ... + 240 = /dev/i2o/hdav 48th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 83 char Teletext/videotext interfaces 0 = /dev/vtx Teletext decoder 16 = /dev/vttuner TV tuner on teletext interface @@ -1190,26 +1271,76 @@ Devices for the driver contained in the VideoteXt package. More information on http://home.pages.de/~videotext/ + block I2O hard disk + 0 = /dev/i2o/hdaw 49th I2O hard disk, whole disk + 16 = /dev/i2o/hdax 50th I2O hard disk, whole disk + ... + 240 = /dev/i2o/hdbl 64th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 84 char Ikon 1011[57] Versatec Greensheet Interface 0 = /dev/ihcp0 First Greensheet port 1 = /dev/ihcp1 Second Greensheet port + block I2O hard disk + 0 = /dev/i2o/hdbm 65th I2O hard disk, whole disk + 16 = /dev/i2o/hdbn 66th I2O hard disk, whole disk + ... + 240 = /dev/i2o/hdcb 80th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 85 char Linux/SGI shared memory input queue 0 = /dev/shmiq Master shared input queue 1 = /dev/qcntl0 First device pushed 2 = /dev/qcntl1 Second device pushed ... + block I2O hard disk + 0 = /dev/i2o/hdcc 81st I2O hard disk, whole disk + 16 = /dev/i2o/hdcd 82nd I2O hard disk, whole disk + ... + 240 = /dev/i2o/hdcr 96th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 86 char SCSI media changer 0 = /dev/sch0 First SCSI media changer 1 = /dev/sch1 Second SCSI media changer ... + block I2O hard disk + 0 = /dev/i2o/hdcs 97th I2O hard disk, whole disk + 16 = /dev/i2o/hdct 98th I2O hard disk, whole disk + ... + 240 = /dev/i2o/hddh 112th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 87 char Sony Control-A1 stereo control bus 0 = /dev/controla0 First device on chain 1 = /dev/controla1 Second device on chain ... + block I2O hard disk + 0 = /dev/i2o/hddi 113rd I2O hard disk, whole disk + 16 = /dev/i2o/hddj 114th I2O hard disk, whole disk + ... + 240 = /dev/i2o/hddx 128th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 88 char COMX synchronous serial card 0 = /dev/comx0 COMX channel 0 1 = /dev/comx1 COMX channel 1 @@ -1222,6 +1353,7 @@ Partitions are handled the same way as for the first interface (see major number 3). + 89 char I2C bus interface 0 = /dev/i2c0 First I2C adapter 1 = /dev/i2c1 Second I2C adapter @@ -1262,6 +1394,15 @@ 92 char Reserved for ith Kommunikationstechnik MIC ISDN card + block PPDD encrypted disk driver + 0 = /dev/ppdd0 First encrypted disk + 1 = /dev/ppdd1 Second encrypted disk + ... + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 93 char IBM Smart Capture Card frame grabber 0 = /dev/iscc0 First Smart Capture Card 1 = /dev/iscc1 Second Smart Capture Card @@ -1270,6 +1411,12 @@ 129 = /dev/isccctl1 Second Smart Capture Card control ... + block NAND Flash Translation Layer filesystem + 0 = /dev/nftla First NFTL layer + 16 = /dev/nftlb Second NFTL layer + ... + 240 = /dev/nftlp 16th NTFL layer + 94 char miroVIDEO DC10/30 capture/playback device 0 = /dev/dcxx0 First capture card 1 = /dev/dcxx1 Second capture card @@ -1281,15 +1428,29 @@ 2 = /dev/ipstate State information log file 3 = /dev/ipauth Authentication control device/log file + block IBM S/390 DASD block storage + 0 = /dev/dasd0 First DASD device, major + 1 = /dev/dasd0a First DASD device, block 1 + 2 = /dev/dasd0b First DASD device, block 2 + 3 = /dev/dasd0c First DASD device, block 3 + 4 = /dev/dasd1 Second DASD device, major + 5 = /dev/dasd1a Second DASD device, block 1 + 6 = /dev/dasd1b Second DASD device, block 2 + 7 = /dev/dasd1c Second DASD device, block 3 + ... + 96 char Parallel port ATAPI tape devices 0 = /dev/pt0 First parallel port ATAPI tape 1 = /dev/pt1 Second parallel port ATAPI tape - 2 = /dev/pt2 Third parallel port ATAPI tape - 3 = /dev/pt3 Fourth parallel port ATAPI tape + ... 128 = /dev/npt0 First p.p. ATAPI tape, no rewind 129 = /dev/npt1 Second p.p. ATAPI tape, no rewind - 130 = /dev/npt2 Third p.p. ATAPI tape, no rewind - 131 = /dev/npt3 Fourth p.p. ATAPI tape, no rewind + ... + + block IBM S/390 VM/ESA minidisk + 0 = /dev/msd0 First VM/ESA minidisk + 1 = /dev/msd1 Second VM/ESA minidisk + ... 97 char Parallel port generic ATAPI interface 0 = /dev/pg0 First parallel port ATAPI device @@ -1364,7 +1525,64 @@ 1 = /dev/srnd1 Second miroMEDIA Surround board ... -111-119 UNALLOCATED +111 char Philips SAA7146-based audio/video card + 0 = /dev/av0 First A/V card + 1 = /dev/av1 Second A/V card + ... + +112 char ISI serial card + 0 = /dev/ttyM0 First ISI port + 1 = /dev/ttyM1 Second ISI port + ... + + There is currently a device-naming conflict between + these and PAM multimodems (major 78). + +113 char ISI serial card - alternate devices + 0 = /dev/cum0 Callout device for ttyM0 + 1 = /dev/cum1 Callout device for ttyM1 + ... + +114 char Picture Elements ISE board + 0 = /dev/ise0 First ISE board + 1 = /dev/ise1 Second ISE board + ... + 128 = /dev/isex0 Control node for first ISE board + 129 = /dev/isex1 Control node for second ISE board + ... + + The ISE board is an embedded computer, optimized for + image processing. The /dev/iseN nodes are the general + I/O access to the board, the /dev/isex0 nodes command + nodes used to control the board. + +115 char Console driver speaker + 0 = /dev/speaker Speaker device file + + Plays music using IBM BASIC style strings. + +116 char Advanced Linux System Driver (ALSA) + +117 char COSA/SRP synchronous serial card + 0 = /dev/cosa0c0 1st board, 1st channel + 1 = /dev/cosa0c1 1st board, 2nd channel + ... + 16 = /dev/cosa1c0 2nd board, 1st channel + 17 = /dev/cosa1c1 2nd board, 2nd channel + ... + +118 char Solidum ??? + 0 = /dev/solnp0 + 1 = /dev/solnp1 + ... + 128 = /dev/solnpctl0 + 129 = /dev/solnpctl1 + ... + +119 char VMware virtual network control + 0 = /dev/vnet0 1st virtual network + 1 = /dev/vnet1 2nd virtual network + ... 120-127 LOCAL/EXPERIMENTAL USE @@ -1377,13 +1595,285 @@ 136-143 char Unix98 PTY slaves 0 = /dev/pts/0 First Unix98 pseudo-TTY 1 = /dev/pts/1 Second Unix98 pesudo-TTY + ... These device nodes are automatically generated with the proper permissions and modes by mounting the devpts filesystem onto /dev/pts with the appropriate - mount options (distribution dependent). + mount options (distribution dependent, however, on + *most* distributions the appropriate options are + "mode=0620,gid=".) + +144 char Encapsulated PPP + 0 = /dev/pppox0 First PPP over Ethernet + ... + 63 = /dev/pppox63 64th PPP over Ethernet + + This is primarily used for ADSL. + + The SST 5136-DN DeviceNet interface driver has been + relocated to major 183 due to an unfortunate conflict. + +145 char SAM9407-based soundcard + 0 = /dev/sam0_mixer + 1 = /dev/sam0_sequencer + 2 = /dev/sam0_midi00 + 3 = /dev/sam0_dsp + 4 = /dev/sam0_audio + 6 = /dev/sam0_sndstat + 18 = /dev/sam0_midi01 + 34 = /dev/sam0_midi02 + 50 = /dev/sam0_midi03 + 64 = /dev/sam1_mixer + ... + 128 = /dev/sam2_mixer + ... + 192 = /dev/sam3_mixer + ... + + Device functions match OSS, but offer a number of + addons, which are sam9407 specific. OSS can be + operated simultaneously, taking care of the codec. + +146 char SYSTRAM SCRAMNet mirrored-memory network + 0 = /dev/scramnet0 First SCRAMNet device + 1 = /dev/scramnet1 Second SCRAMNet device + ... + +147 char Aueral Semiconductor Vortex Audio device + 0 = /dev/aureal0 First Aureal Vortex + 1 = /dev/aureal1 Second Aureal Vortex + ... + +148 char Technology Concepts serial card + 0 = /dev/ttyT0 First TCL port + 1 = /dev/ttyT1 Second TCL port + ... + +149 char Technology Concepts serial card - alternate devices + 0 = /dev/cut0 Callout device for ttyT0 + 1 = /dev/cut0 Callout device for ttyT1 + ... + +150 char Real-Time Linux FIFOs + 0 = /dev/rtf0 First RTLinux FIFO + 1 = /dev/rtf1 Second RTLinux FIFO + ... + +151 char DPT I2O SmartRaid V controller + 0 = /dev/dpti0 First DPT I2O adapter + 1 = /dev/dpti1 Second DPT I2O adapter + ... + +154 char Specialix RIO serial card + 0 = /dev/ttySR0 First RIO port + ... + 255 = /dev/ttySR255 256th RIO port + +155 char Specialix RIO serial card - alternate devices + 0 = /dev/cusr0 Callout device for ttySR0 + ... + 255 = /dev/cusr255 Callout device for ttySR255 + +156 char Specialix RIO serial card + 0 = /dev/ttySR256 257th RIO port + ... + 255 = /dev/ttySR511 512th RIO port + +157 char Specialix RIO serial card - alternate devices + 0 = /dev/cusr256 Callout device for ttySR256 + ... + 255 = /dev/cusr511 Callout device for ttySR511 + +158 char Dialogic GammaLink fax driver + 0 = /dev/gfax0 GammaLink channel 0 + 1 = /dev/gfax1 GammaLink channel 1 + ... + +159 char Quicknet Technologies Internet PhoneJack/LineJack + 0 = /dev/ixj0 First device + 1 = /dev/ixj1 Second device + ... + +160 char General Purpose Instrument Bus (GPIB) + 0 = /dev/gpib0 First GPIB bus + 1 = /dev/gpib1 Second GPIB bus + ... + +161 char IrCOMM devices (IrDA serial/parallel emulation) + 0 = /dev/ircomm0 First IrCOMM device + 1 = /dev/ircomm1 Second IrCOMM device + ... + 16 = /dev/irlpt0 First IrLPT device + 17 = /dev/irlpt1 Second IrLPT device + ... + +162 char Raw block device interface + 0 = /dev/raw Raw I/O control device + 1 = /dev/raw1 First raw I/O device + 2 = /dev/raw2 Second raw I/O device + ... + +163 char Radio Tech BIM-XXX-RS232 radio modem + 0 = /dev/bimrt0 First BIM radio modem + 1 = /dev/bimrt1 Second BIM radio modem + ... + +164 char Chase Research AT/PCI-Fast serial card + 0 = /dev/ttyCH0 AT/PCI-Fast board 0, port 0 + ... + 15 = /dev/ttyCH15 AT/PCI-Fast board 0, port 15 + 16 = /dev/ttyCH16 AT/PCI-Fast board 1, port 0 + ... + 31 = /dev/ttyCH31 AT/PCI-Fast board 1, port 15 + 32 = /dev/ttyCH32 AT/PCI-Fast board 2, port 0 + ... + 47 = /dev/ttyCH47 AT/PCI-Fast board 2, port 15 + 48 = /dev/ttyCH48 AT/PCI-Fast board 3, port 0 + ... + 63 = /dev/ttyCH63 AT/PCI-Fast board 3, port 15 + +165 char Chase Research AT/PCI-Fast serial card - alternate devices + 0 = /dev/cuch0 Callout device corresponding to ttyCH0 + ... + 63 = /dev/cuch63 Callout device corresponding to ttyCH63 + +166 char ACM USB modems + 0 = /dev/ttyACM0 First ACM modem + 1 = /dev/ttyACM1 Second ACM modem + ... + +167 char ACM USB modems - alternate devices + 0 = /dev/cuacm0 Callout device for ttyACM0 + 1 = /dev/cuacm1 Callout device for ttyACM1 + ... + +168 char Eracom CSA7000 PCI encryption adaptor + 0 = /dev/ecsa0 First CSA7000 + 1 = /dev/ecsa1 Second CSA7000 + ... + +169 char Eracom CSA8000 PCI encryption adaptor + 0 = /dev/ecsa8-0 First CSA8000 + 1 = /dev/ecsa8-1 Second CSA8000 + ... + +170 char AMI MegaRAC remote access controller + 0 = /dev/megarac0 First MegaRAC card + 1 = /dev/megarac1 Second MegaRAC card + ... + +171 char Reserved for IEEE 1394 (Firewire) + + +172 char Moxa Intellio serial card + 0 = /dev/ttyMX0 First Moxa port + 1 = /dev/ttyMX1 Second Moxa port + ... + 127 = /dev/ttyMX127 128th Moxa port + 128 = /dev/moxactl Moxa control port + +173 char Moxa Intellio serial card - alternate devices + 0 = /dev/cumx0 Callout device for ttyMX0 + 1 = /dev/cumx1 Callout device for ttyMX1 + ... + 127 = /dev/cumx127 Callout device for ttyMX127 + +174 char SmartIO serial card + 0 = /dev/ttySI0 First SmartIO port + 1 = /dev/ttySI1 Second SmartIO port + ... + +175 char SmartIO serial card - alternate devices + 0 = /dev/cusi0 Callout device for ttySI0 + 1 = /dev/cusi1 Callout device for ttySI1 + ... + +176 char nCipher nFast PCI crypto accelerator + 0 = /dev/nfastpci0 First nFast PCI device + 1 = /dev/nfastpci1 First nFast PCI device + ... + +177 char TI PCILynx memory spaces + 0 = /dev/pcilynx/aux0 AUX space of first PCILynx card + ... + 15 = /dev/pcilynx/aux15 AUX space of 16th PCILynx card + 16 = /dev/pcilynx/rom0 ROM space of first PCILynx card + ... + 31 = /dev/pcilynx/rom15 ROM space of 16th PCILynx card + 32 = /dev/pcilynx/ram0 RAM space of first PCILynx card + ... + 47 = /dev/pcilynx/ram15 RAM space of 16th PCILynx card + +178 char Giganet cLAN1xxx virtual interface adapter + 0 = /dev/clanvi0 First cLAN adapter + 1 = /dev/clanvi1 Second cLAN adapter + ... + +179 char CCube DVXChip-based PCI products + 0 = /dev/dvxirq0 First DVX device + 1 = /dev/dvxirq1 Second DVX device + ... + +180 char USB devices + 0 = /dev/usb/lp0 First USB printer + ... + 15 = /dev/usb/lp15 16th USB printer + 16 = /dev/usb/mouse0 First USB mouse + ... + 31 = /dev/usb/mouse15 16th USB mouse + 32 = /dev/usb/ez0 First USB firmware loader + ... + 47 = /dev/usb/ez15 16th USB firmware loader + 48 = /dev/usb/scanner0 First USB scanner + ... + 63 = /dev/usb/scanner15 16th USB scanner + +181 char Conrad Electronic parallel port radio clocks + 0 = /dev/pcfclock0 First Conrad radio clock + 1 = /dev/pcfclock1 Second Conrad radio clock + ... + +182 char Picture Elements THR2 binarizer + 0 = /dev/pethr0 First THR2 board + 1 = /dev/pethr1 Second THR2 board + ... + +183 char SST 5136-DN DeviceNet interface + 0 = /dev/ss5136dn0 First DeviceNet interface + 1 = /dev/ss5136dn1 Second DeviceNet interface + ... + + This device used to be assigned to major number 144. + It had to be moved due to an unfortunate conflict. + +184 char Picture Elements' video simulator/sender + 0 = /dev/pevss0 First sender board + 1 = /dev/pevss1 Second sender board + ... + +185 char Reserved for InterMezzo high availability file system + +186 char Object-based storage control device + 0 = /dev/obd0 First obd control device + 1 = /dev/obd1 Second obd control device + ... + + See ftp://ftp.lustre.org/pub/obd for code and information. + +187 char UNALLOCATED + +188 char USB serial converters + 0 = /dev/ttyUSB0 First USB serial converter + 1 = /dev/ttyUSB1 Second USB serial converter + ... + +189 char USB serial converters - alternate devices + 0 = /dev/cuusb0 Callout device corresponding to ttyUSB0 + 1 = /dev/cuusb1 Callout device corresponding to ttyUSB1 + ... -144-239 UNALLOCATED +190-239 UNALLOCATED 240-254 LOCAL/EXPERIMENTAL USE @@ -1412,8 +1902,9 @@ /dev/stderr fd/2 symbolic stderr file descriptor /dev/nfsd socksys symbolic Required by iBCS-2 /dev/X0R null symbolic Required by iBCS-2 +/dev/i2o* /dev/i2o/* symbolic Backward compatibility -Note: the last device is --. +Note: /dev/X0R is --. Recommended links diff -u --recursive --new-file v2.3.33/linux/Documentation/filesystems/vfat.txt linux/Documentation/filesystems/vfat.txt --- v2.3.33/linux/Documentation/filesystems/vfat.txt Thu Apr 29 11:53:41 1999 +++ linux/Documentation/filesystems/vfat.txt Mon Dec 20 14:41:47 1999 @@ -52,11 +52,6 @@ TODO ---------------------------------------------------------------------- -* When only shortnames exist, translate them from the codepage character - set to the iocharset. Currently, translations only occur when longnames - exist. To translate, first convert from codepage to Unicode and then - to the output character set. - * Need to get rid of the raw scanning stuff. Instead, always use a get next directory entry approach. The only thing left that uses raw scanning is the directory renaming code. diff -u --recursive --new-file v2.3.33/linux/Documentation/i2c/dev-interface linux/Documentation/i2c/dev-interface --- v2.3.33/linux/Documentation/i2c/dev-interface Wed Dec 31 16:00:00 1969 +++ linux/Documentation/i2c/dev-interface Thu Dec 16 13:59:38 1999 @@ -0,0 +1,124 @@ +Usually, i2c devices are controlled by a kernel driver. But it is also +possible to access all devices on an adapter from userspace, through +the /dev interface. You need to load module i2c-dev for this. + +Each registered i2c adapter gets a number, counting from 0. You can +examine /proc/bus/i2c to see what number corresponds to which adapter. +I2C device files are character device files with major device number 89 +and a minor device number corresponding to the number assigned as +explained above. They should be called "i2c-%d" (i2c-0, i2c-1, ..., +i2c-10, ...). All 256 minor device numbers are reserved for i2c. + + +C example +========= + +So let's say you want to access an i2c adapter from a C program. The +first thing to do is `#include " and "#include . +Yes, I know, you should never include kernel header files, but until glibc +knows about i2c, there is not much choice. + +Now, you have to decide which adapter you want to access. You should +inspect /proc/bus/i2c to decide this. Adapter numbers are assigned +somewhat dynamically, so you can not even assume /dev/i2c-0 is the +first adapter. + +Next thing, open the device file, as follows: + int file; + int adapter_nr = 2; /* probably dynamically determined */ + char filename[20]; + + sprintf(filename,"/dev/i2c-%d",adapter_nr); + if ((file = open(filename,O_RDWR)) < 0) { + /* ERROR HANDLING; you can check errno to see what went wrong */ + exit(1); + } + +When you have opened the device, you must specify with what device +address you want to communicate: + int addr = 0x40; /* The I2C address */ + if (ioctl(file,I2C_SLAVE,addr) < 0) { + /* ERROR HANDLING; you can check errno to see what went wrong */ + exit(1); + } + +Well, you are all set up now. You can now use SMBus commands or plain +I2C to communicate with your device. SMBus commands are preferred if +the device supports them. Both are illustrated below. + __u8 register = 0x10; /* Device register to access */ + __s32 res; + char buf[10]; + /* Using SMBus commands */ + res = i2c_smbus_read_word_data(file,register); + if (res < 0) { + /* ERROR HANDLING: i2c transaction failed */ + } else { + /* res contains the read word */ + } + /* Using I2C Write, equivalent of + i2c_smbus_write_word_data(file,register,0x6543) */ + buf[0] = register; + buf[1] = 0x43; + buf[2] = 0x65; + if ( write(file,buf,3) != 3) { + /* ERROR HANDLING: i2c transaction failed */ + } + /* Using I2C Read, equivalent of i2c_smbus_read_byte(file) */ + if (read(file,buf,1) != 1) { + /* ERROR HANDLING: i2c transaction failed */ + } else { + /* buf[0] contains the read byte */ + } + + +Full interface description +========================== + +The following IOCTLs are defined and fully supported +(see also i2c-dev.h and i2c.h): + +ioctl(file,I2C_SLAVE,long addr) + Change slave address. The address is passed in the 7 lower bits of the + argument (except for 10 bit addresses, passed in the 10 lower bits in this + case). + +ioctl(file,I2C_TENBIT,long select) + Selects ten bit addresses if select not equals 0, selects normal 7 bit + addresses if select equals 0. + +ioctl(file,I2C_FUNCS,unsigned long *funcs) + Gets the adapter functionality and puts it in *funcs. + +Other values are NOT supported at this moment, except for I2C_SMBUS, +which you should never directly call; instead, use the access functions +below. + +You can do plain i2c transactions by using read(2) and write(2) calls. +Combined read/write transactions are not yet supported (they will in +the future, through an ioctl). You do not need to pass the address +byte; instead, set it through ioctl I2C_SLAVE before you try to +access the device. + +You can do SMBus level transactions (see documentation file smbus-protocol +for details) through the following functions: + __s32 i2c_smbus_write_quick(int file, __u8 value); + __s32 i2c_smbus_read_byte(int file); + __s32 i2c_smbus_write_byte(int file, __u8 value); + __s32 i2c_smbus_read_byte_data(int file, __u8 command); + __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value); + __s32 i2c_smbus_read_word_data(int file, __u8 command); + __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value); + __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value); + __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values); + __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length, + __u8 *values); +All these tranactions return -1 on failure; you can read errno to see +what happened. The 'write' transactions return 0 on success; the +'read' transactions return the read value, except for read_block, which +returns the number of values read. The block buffers need not be longer +than 32 bytes. + +The above functions are all macros, that resolve to calls to the +i2c_smbus_access function, that on its turn calls a specific ioctl +with the data in a specific format. Read the source code if you +want to know what happens behind the screens. diff -u --recursive --new-file v2.3.33/linux/Documentation/i2c/i2c-protocol linux/Documentation/i2c/i2c-protocol --- v2.3.33/linux/Documentation/i2c/i2c-protocol Wed Dec 31 16:00:00 1969 +++ linux/Documentation/i2c/i2c-protocol Thu Dec 16 13:59:38 1999 @@ -0,0 +1,46 @@ +This document describes the i2c protocol. Or will, when it is finished :-) + +Key to symbols +============== + +S (1 bit) : Start bit +P (1 bit) : Stop bit +Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0. +A, NA (1 bit) : Accept and reverse accept bit. +Addr (7 bits): I2C 7 bit address. Note that this can be expanded as usual to + get a 10 bit I2C address. +Comm (8 bits): Command byte, a data byte which often selects a register on + the device. +Data (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh + for 16 bit data. +Count (8 bits): A data byte containing the length of a block operation. + +[..]: Data sent by I2C device, as opposed to data sent by the host adapter. + + +Simple send tranaction +====================== + +This corresponds to i2c_master_send. + + S Addr Wr [A] Data [A] Data [A] ... [A] Data [A] P + + +Simple receive transaction +=========================== + +This corresponds to i2c_master_recv + + S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P + + +Combined tranactions +==================== + +This corresponds to i2c_transfer + +They are just like the above transactions, but instead of a stop bit P +a start bit S is sent and the transaction continues. An example of +a byte read, followed by a byte write: + + S Addr Rd [A] [Data] NA S Addr Wr [A] Data [A] P diff -u --recursive --new-file v2.3.33/linux/Documentation/i2c/proc-interface linux/Documentation/i2c/proc-interface --- v2.3.33/linux/Documentation/i2c/proc-interface Wed Dec 31 16:00:00 1969 +++ linux/Documentation/i2c/proc-interface Thu Dec 16 13:59:38 1999 @@ -0,0 +1,53 @@ +i2c-core is the core i2c module (surprise!) which offers general routines on +which other modules build. You will find that all i2c-related modules depend +on this module, so it will (need to) be loaded whenever another i2c-related +module is loaded. Seen from the outside, the most interesting is the /proc +interface. Note that there is no corresponding sysctl interface! + +/proc/bus/i2c +============= + +Whenever i2c-core is loaded, you will find a file /proc/bus/i2c, which lists +all currently registered I2C adapters. Each line contains exactly one +I2C adapter. Each line has the following format: "i2c-%d\t%9s\t%-32s't%-32s\n", +which works out to four columns separated by tabs. Note that the file +will be empty, if no adapters are registered at all. + +Adapters are numbered from 0 upwards. The first column contains the number +of the adapter, for example "i2c-4" for adapter 4. The name listed is also +the name of the /proc file which lists all devices attached to it, and +of the /dev file which corresponds to this adapter. + +The second column documents what kind of adapter this is. Some adapters +understand the full I2C protocol, others only a subset called SMBus, +and yet others are some kind of pseudo-adapters that do not understand +i2c at all. Possible values in here are "i2c", "smbus", "i2c/smbus" +and "dummy". Because the SMBus protocol can be fully emulated by i2c +adapters, if you see "i2c" here, SMBus is supported too. There may +be some future adapters which support both specific SMBus commands and +general I2C, and they will display "i2c/smbus". + +The third and fourth column are respectively the algorithm and adapter +name of this adapter. Each adapter is associated with an algorithm, +and several adapters can share the same algorithm. The combination of +algorithm name and adapter name should be unique for an adapter, but +you can't really count on that yet. + + +/proc/bus/i2c-* +=============== + +Each registered adapter gets its own file in /proc/bus/, which lists +the devices registered to the adapter. Each line in such a file contains +one registered device. Each line has the following format: +"%02x\t%-32s\t%-32s\n", which works out to three columns separated by +tabs. Note that this file can be empty, if no devices are found on +the adapter. + +The first column contains the (hexadecimal) address of the client. As +only 7-bit addresses are supported at this moment, two digits are +enough. + +The second and third column are respectively the client name and the +driver name of this client. Each client is associated with a driver, +and several clients can share the same driver. diff -u --recursive --new-file v2.3.33/linux/Documentation/i2c/smbus-protocol linux/Documentation/i2c/smbus-protocol --- v2.3.33/linux/Documentation/i2c/smbus-protocol Wed Dec 31 16:00:00 1969 +++ linux/Documentation/i2c/smbus-protocol Thu Dec 16 13:59:38 1999 @@ -0,0 +1,127 @@ +Some adapters understand only the SMBus (System Management Bus) protocol, +which is a subset from the I2C protocol. Fortunately, many devices use +only the same subset, which makes it possible to put them on an SMBus. +If you write a driver for some I2C device, please try to use the SMBus +commands if at all possible (if the device uses only that subset of the +I2C protocol). This makes it possible to use the device driver on both +SMBus adapters and I2C adapters (the SMBus command set is automatically +translated to I2C on I2C adapters, but plain I2C commands can not be +handled at all on a pure SMBus adapter). + +Below is a list of SMBus commands. + +Key to symbols +============== + +S (1 bit) : Start bit +P (1 bit) : Stop bit +Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0. +A, NA (1 bit) : Accept and reverse accept bit. +Addr (7 bits): I2C 7 bit address. Note that this can be expanded as usual to + get a 10 bit I2C address. +Comm (8 bits): Command byte, a data byte which often selects a register on + the device. +Data (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh + for 16 bit data. +Count (8 bits): A data byte containing the length of a block operation. + +[..]: Data sent by I2C device, as opposed to data sent by the host adapter. + + +SMBus Write Quick +================= + +This sends a single byte to the device, at the place of the Rd/Wr bit. +There is no equivalent Read Quick command. + +A Addr Rd/Wr [A] P + + +SMBus Read Byte +=============== + +This reads a single byte from a device, without specifying a device +register. Some devices are so simple that this interface is enough; for +others, it is a shorthand if you want to read the same register as in +the previous SMBus command. + +S Addr Rd [A] [Data] NA P + + +SMBus Write Byte +================ + +This is the reverse of Read Byte: it sends a single byte to a device. +See Read Byte for more information. + +S Addr Wr [A] Data NA P + + +SMBus Read Byte Data +==================== + +This reads a single byte from a device, from a designated register. +The register is specified through the Comm byte. + +S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P + + +SMBus Read Word Data +==================== + +This command is very like Read Byte Data; again, data is read from a +device, from a designated register that is specified through the Comm +byte. But this time, the data is a complete word (16 bits). + +S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P + + +SMBus Write Byte Data +===================== + +This writes a single byte to a device, to a designated register. The +register is specified through the Comm byte. This is the opposite of +the Read Byte Data command. + +S Addr Wr [A] Comm [A] Data [A] P + + +SMBus Write Word Data +===================== + +This is the opposite operation of the Read Word Data command. 16 bits +of data is read from a device, from a designated register that is +specified through the Comm byte. + +S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P + + +SMBus Process Call +================== + +This command selects a device register (through the Comm byte), sends +16 bits of data to it, and reads 16 bits of data in return. + +S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] + S Addr Rd [A] [DataLow] A [DataHigh] NA P + + +SMBus Block Read +================ + +This command reads a block of upto 32 bytes from a device, from a +designated register that is specified through the Comm byte. The amount +of data is specified by the device in the Count byte. + +S Addr Wr [A] Comm [A] + S Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P + + +SMBus Block Write +================= + +The opposite of the Block Read command, this writes upto 32 bytes to +a device, to a designated register that is specified through the +Comm byte. The amount of data is specified in the Count byte. + +S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P diff -u --recursive --new-file v2.3.33/linux/Documentation/i2c/summary linux/Documentation/i2c/summary --- v2.3.33/linux/Documentation/i2c/summary Wed Dec 31 16:00:00 1969 +++ linux/Documentation/i2c/summary Thu Dec 16 13:59:38 1999 @@ -0,0 +1,63 @@ +This is an explanation of what i2c is, and what is supported. + +I2C and SMBus +============= + +I2C (pronounce: I square C) is a protocol developed by Philips. It is a +slow two-wire protocol (10-100 kHz), but it suffices for many types of +devices. + +SMBus (System Management Bus) is a subset of the I2C protocol. Many +modern mainboards have a System Management Bus. There are a lot of +devices which can be connected to a SMBus; the most notable are modern +memory chips with EEPROM memories and chips for hardware monitoring. + +Because the SMBus is just a special case of the generalized I2C bus, we +can simulate the SMBus protocol on plain I2C busses. The reverse is +regretfully impossible. + + +Terminology +=========== + +When we talk about I2C, we use the following terms: + Bus -> Algorithm + Adapter + Device -> Driver + Client +An Algorithm driver contains general code that can be used for a whole class +of I2C adapters. Each specific adapter driver depends on one algorithm +driver. +A Driver driver (yes, this sounds ridiculous, sorry) contains the general +code to access some type of device. Each detected device gets its own +data in the Client structure. Usually, Driver and Client are more closely +integrated than Algorithm and Adapter. + +For a given configuration, you will need a driver for your I2C bus (usually +a separate Adapter and Algorithm driver), and drivers for your I2C devices +(usually one driver for each device). + + +Included Drivers +================ + +Base modules +------------ + +i2c-core: The basic I2C code, including the /proc interface +i2c-dev: The /dev interface + +Algorithm drivers +----------------- + +i2c-algo-bit: A bit-banging algorithm +i2c-algo-pcf: A PCF 8584 style algorithm + +Adapter drivers +--------------- + +i2c-elektor: Elektor ISA card (uses i2c-algo-pcf) +i2c-elv: ELV parallel port adapter (uses i2c-algo-bit) +i2c-philips-par: Philips style parallel port adapter (uses i2c-algo-bit) +i2c-velleman: Velleman K9000 parallel port adapter (uses i2c-algo-bit) + diff -u --recursive --new-file v2.3.33/linux/Documentation/i2c/ten-bit-addresses linux/Documentation/i2c/ten-bit-addresses --- v2.3.33/linux/Documentation/i2c/ten-bit-addresses Wed Dec 31 16:00:00 1969 +++ linux/Documentation/i2c/ten-bit-addresses Thu Dec 16 13:59:38 1999 @@ -0,0 +1,22 @@ +The I2C protocol knows about two kinds of device addresses: normal 7 bit +addresses, and an extended set of 10 bit addresses. The sets of addresses +do not intersect: the 7 bit address 0x10 is not the same as the 10 bit +address 0x10 (though a single device could respond to both of them). You +select a 10 bit address by adding an extra byte after the address +byte: + S Addr7 Rd/Wr .... +becomes + S 11110 Addr10 Rd/Wr +S is the start bit, Rd/Wr the read/write bit, and if you count the number +of bits, you will see the there are 8 after the S bit for 7 bit addresses, +and 16 after the S bit for 10 bit addresses. + +WARNING! The current 10 bit address support is EXPERIMENTAL. There are +several places in the code that will cause SEVERE PROBLEMS with 10 bit +addresses, even though there is some basic handling and hooks. Also, +almost no supported adapter handles the 10 bit addresses correctly. + +As soon as a real 10 bit address device is spotted 'in the wild', we +can and will add proper support. Right now, 10 bit address devices +are defined by the I2C protocol, but we have never seen a single device +which supports them. diff -u --recursive --new-file v2.3.33/linux/Documentation/i2c/writing-clients linux/Documentation/i2c/writing-clients --- v2.3.33/linux/Documentation/i2c/writing-clients Wed Dec 31 16:00:00 1969 +++ linux/Documentation/i2c/writing-clients Thu Dec 16 13:59:38 1999 @@ -0,0 +1,862 @@ +This is a small guide for those who want to write kernel drivers for I2C +or SMBus devices. + +To set up a driver, you need to do several things. Some are optional, and +some things can be done slightly or completely different. Use this as a +guide, not as a rule book! + + +General remarks +=============== + +Try to keep the kernel namespace as clean as possible. The best way to +do this is to use a unique prefix for all global symbols. This is +especially important for exported symbols, but it is a good idea to do +it for non-exported symbols too. We will use the prefix `foo_' in this +tutorial, and `FOO_' for preprocessor variables. + + +The driver structure +==================== + +Usually, you will implement a single driver structure, and instantiate +all clients from it. Remember, a driver structure contains general access +routines, a client structure specific information like the actual I2C +address. + + struct i2c_driver foo_driver + { + /* name */ "Foo version 2.3 and later driver", + /* id */ I2C_DRIVERID_FOO, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &foo_attach_adapter, + /* detach_client */ &foo_detach_client, + /* command */ &foo_command, /* May be NULL */ + /* inc_use */ &foo_inc_use, /* May be NULL */ + /* dec_use */ &foo_dev_use /* May be NULL */ + } + +The name can be choosen freely, and may be upto 40 characters long. Please +use something descriptive here. + +The id should be a unique ID. The range 0xf000 to 0xffff is reserved for +local use, and you can use one of those until you start distributing the +driver. Before you do that, contact the i2c authors to get your own ID(s). + +Don't worry about the flags field; just put I2C_DF_NOTIFY into it. This +means that your driver will be notified when new adapters are found. +This is almost always what you want. + +All other fields are for call-back functions which will be explained +below. + + +Module usage count +================== + +If your driver can also be compiled as a module, there are moments at +which the module can not be removed from memory. For example, when you +are doing a lengthy transaction, or when you create a /proc directory, +and some process has entered that directory (this last case is the +main reason why these call-backs were introduced). + +To increase or decrease the module usage count, you can use the +MOD_{INC,DEC}_USE_COUNT macros. They must be called from the module +which needs to get its usage count changed; that is why each driver +module has to implement its own callback. + + void foo_inc_use (struct i2c_client *client) + { + #ifdef MODULE + MOD_INC_USE_COUNT; + #endif + } + + void foo_dec_use (struct i2c_client *client) + { + #ifdef MODULE + MOD_DEC_USE_COUNT; + #endif + } + +Do not call these call-back functions directly; instead, use one of the +following functions defined in i2c.h: + void i2c_inc_use_client(struct i2c_client *); + void i2c_dec_use_client(struct i2c_client *); + +You should *not* increase the module count just because a device is +detected and a client created. This would make it impossible to remove +an adapter driver! + + +Extra client data +================= + +The client structure has a special `data' field that can point to any +structure at all. You can use this to keep client-specific data. You +do not always need this, but especially for `sensors' drivers, it can +be very useful. + +An example structure is below. + + struct foo_data { + struct semaphore lock; /* For ISA access in `sensors' drivers. */ + int sysctl_id; /* To keep the /proc directory entry for + `sensors' drivers. */ + enum chips type; /* To keep the chips type for `sensors' drivers. */ + + /* Because the i2c bus is slow, it is often useful to cache the read + information of a chip for some time (for example, 1 or 2 seconds). + It depends of course on the device whether this is really worthwhile + or even sensible. */ + struct semaphore update_lock; /* When we are reading lots of information, + another process should not update the + below information */ + char valid; /* != 0 if the following fields are valid. */ + unsigned long last_updated; /* In jiffies */ + /* Add the read information here too */ + }; + + +Accessing the client +==================== + +Let's say we have a valid client structure. At some time, we will need +to gather information from the client, or write new information to the +client. How we will export this information to user-space is less +important at this moment (perhaps we do not need to do this at all for +some obscure clients). But we need generic reading and writing routines. + +I have found it useful to define foo_read and foo_write function for this. +For some cases, it will be easier to call the i2c functions directly, +but many chips have some kind of register-value idea that can easily +be encapsulated. Also, some chips have both ISA and I2C interfaces, and +it useful to abstract from this (only for `sensors' drivers). + +The below functions are simple examples, and should not be copied +literally. + + int foo_read_value(struct i2c_client *client, u8 reg) + { + if (reg < 0x10) /* byte-sized register */ + return i2c_smbus_read_byte_data(client,reg); + else /* word-sized register */ + return i2c_smbus_read_word_data(client,reg); + } + + int foo_write_value(struct i2c_client *client, u8 reg, u16 value) + { + if (reg == 0x10) /* Impossible to write - driver error! */ { + return -1; + else if (reg < 0x10) /* byte-sized register */ + return i2c_smbus_write_byte_data(client,reg,value); + else /* word-sized register */ + return i2c_smbus_write_word_data(client,reg,value); + } + +For sensors code, you may have to cope with ISA registers too. Something +like the below often works. Note the locking! + + int foo_read_value(struct i2c_client *client, u8 reg) + { + int res; + if (i2c_is_isa_client(client)) { + down(&(((struct foo_data *) (client->data)) -> lock)); + outb_p(reg,client->addr + FOO_ADDR_REG_OFFSET); + res = inb_p(client->addr + FOO_DATA_REG_OFFSET); + up(&(((struct foo_data *) (client->data)) -> lock)); + return res; + } else + return i2c_smbus_read_byte_data(client,reg); + } + +Writing is done the same way. + + +Probing and attaching +===================== + +Most i2c devices can be present on several i2c addresses; for some this +is determined in hardware (by soldering some chip pins to Vcc or Ground), +for others this can be changed in software (by writing to specific client +registers). Some devices are usually on a specific address, but not always; +and some are even more tricky. So you will probably need to scan several +i2c addresses for your clients, and do some sort of detection to see +whether it is actually a device supported by your driver. + +To give the user a maximum of possibilities, some default module parameters +are defined to help determine what addresses are scanned. Several macros +are defined in i2c.h to help you support them, as well as a generic +detection algorithm. + +You do not have to use this parameter interface; but don't try to use +function i2c_probe() (or sensors_detect()) if you don't. + +NOTE: If you want to write a `sensors' driver, the interface is slightly + different! See below. + + + +Probing classes (i2c) +--------------------- + +All parameters are given as lists of unsigned 16-bit integers. Lists are +terminated by I2C_CLIENT_END. +The following lists are used internally: + + normal_i2c: filled in by the module writer. + A list of I2C addresses which should normally be examined. + normal_i2c_range: filled in by the module writer. + A list of pairs of I2C addresses, each pair being an inclusive range of + addresses which should normally be examined. + probe: insmod parameter. + A list of pairs. The first value is a bus number (-1 for any I2C bus), + the second is the address. These addresses are also probed, as if they + were in the 'normal' list. + probe_range: insmod parameter. + A list of triples. The first value is a bus number (-1 for any I2C bus), + the second and third are addresses. These form an inclusive range of + addresses that are also probed, as if they were in the 'normal' list. + ignore: insmod parameter. + A list of pairs. The first value is a bus number (-1 for any I2C bus), + the second is the I2C address. These addresses are never probed. + This parameter overrules 'normal' and 'probe', but not the 'force' lists. + ignore_range: insmod parameter. + A list of triples. The first value is a bus number (-1 for any I2C bus), + the second and third are addresses. These form an inclusive range of + I2C addresses that are never probed. + This parameter overrules 'normal' and 'probe', but not the 'force' lists. + force: insmod parameter. + A list of pairs. The first value is a bus number (-1 for any I2C bus), + the second is the I2C address. A device is blindly assumed to be on + the given address, no probing is done. + +Fortunately, as a module writer, you just have to define the `normal' +and/or `normal_range' parameters. The complete declaration could look +like this: + + /* Scan 0x20 to 0x2f, 0x37, and 0x40 to 0x4f */ + static unsigned short normal_i2c[] = { 0x37,I2C_CLIENT_END }; + static unsigned short normal_i2c_range[] = { 0x20, 0x2f, 0x40, 0x4f, + I2C_CLIENT_END }; + + /* Magic definition of all other variables and things */ + I2C_CLIENT_INSMOD; + +Note that you *have* to call the two defined variables `normal_i2c' and +`normal_i2c_range', without any prefix! + + +Probing classes (sensors) +------------------------- + +If you write a `sensors' driver, you use a slightly different interface. +As well as I2C addresses, we have to cope with ISA addresses. Also, we +use a enum of chip types. Don't forget to include `sensors.h'. + +The following lists are used internally. They are all lists of integers. + + normal_i2c: filled in by the module writer. Terminated by SENSORS_I2C_END. + A list of I2C addresses which should normally be examined. + normal_i2c_range: filled in by the module writer. Terminated by + SENSORS_I2C_END + A list of pairs of I2C addresses, each pair being an inclusive range of + addresses which should normally be examined. + normal_isa: filled in by the module writer. Terminated by SENSORS_ISA_END. + A list of ISA addresses which should normally be examined. + normal_isa_range: filled in by the module writer. Terminated by + SENSORS_ISA_END + A list of triples. The first two elements are ISA addresses, being an + range of addresses which should normally be examined. The third is the + modulo parameter: only addresses which are 0 module this value relative + to the first address of the range are actually considered. + probe: insmod parameter. Initialize this list with SENSORS_I2C_END values. + A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second is the address. These + addresses are also probed, as if they were in the 'normal' list. + probe_range: insmod parameter. Initialize this list with SENSORS_I2C_END + values. + A list of triples. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second and third are addresses. + These form an inclusive range of addresses that are also probed, as + if they were in the 'normal' list. + ignore: insmod parameter. Initialize this list with SENSORS_I2C_END values. + A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second is the I2C address. These + addresses are never probed. This parameter overrules 'normal' and + 'probe', but not the 'force' lists. + ignore_range: insmod parameter. Initialize this list with SENSORS_I2C_END + values. + A list of triples. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second and third are addresses. + These form an inclusive range of I2C addresses that are never probed. + This parameter overrules 'normal' and 'probe', but not the 'force' lists. + +Also used is a list of pointers to sensors_force_data structures: + force_data: insmod parameters. A list, ending with an element of which + the force field is NULL. + Each element contains the type of chip and a list of pairs. + The first value is a bus number (SENSORS_ISA_BUS for the ISA bus, + -1 for any I2C bus), the second is the address. + These are automatically translated to insmod variables of the form + force_foo. + +So we have a generic insmod variabled `force', and chip-specific variables +`force_CHIPNAME'. + +Fortunately, as a module writer, you just have to define the `normal' +and/or `normal_range' parameters, and define what chip names are used. +The complete declaration could look like this: + /* Scan i2c addresses 0x20 to 0x2f, 0x37, and 0x40 to 0x4f + static unsigned short normal_i2c[] = {0x37,SENSORS_I2C_END}; + static unsigned short normal_i2c_range[] = {0x20,0x2f,0x40,0x4f, + SENSORS_I2C_END}; + /* Scan ISA address 0x290 */ + static unsigned int normal_isa[] = {0x0290,SENSORS_ISA_END}; + static unsigned int normal_isa_range[] = {SENSORS_ISA_END}; + + /* Define chips foo and bar, as well as all module parameters and things */ + SENSORS_INSMOD_2(foo,bar); + +If you have one chip, you use macro SENSORS_INSMOD_1(chip), if you have 2 +you use macro SENSORS_INSMOD_2(chip1,chip2), etc. If you do not want to +bother with chip types, you can use SENSORS_INSMOD_0. + +A enum is automatically defined as follows: + enum chips { any_chip, chip1, chip2, ... } + + +Attaching to an adapter +----------------------- + +Whenever a new adapter is inserted, or for all adapters if the driver is +being registered, the callback attach_adapter() is called. Now is the +time to determine what devices are present on the adapter, and to register +a client for each of them. + +The attach_adapter callback is really easy: we just call the generic +detection function. This function will scan the bus for us, using the +information as defined in the lists explained above. If a device is +detected at a specific address, another callback is called. + + int foo_attach_adapter(struct i2c_adapter *adapter) + { + return i2c_probe(adapter,&addr_data,&foo_detect_client); + } + +For `sensors' drivers, use the sensors_detect function instead: + + int foo_attach_adapter(struct i2c_adapter *adapter) + { + return sensors_detect(adapter,&addr_data,&foo_detect_client); + } + +Remember, structure `addr_data' is defined by the macros explained above, +so you do not have to define it yourself. + +The i2c_probe or sensors_detect function will call the foo_detect_client +function only for those i2c addresses that actually have a device on +them (unless a `force' parameter was used). + + +The detect client function +-------------------------- + +The detect client function is called by i2c_probe or sensors_detect. +The `kind' parameter contains 0 if this call is due to a `force' +parameter, and 0 otherwise (for sensors_detect, it contains 0 if +this call is due to the generic `force' parameter, and the chip type +number if it is due to a specific `force' parameter). + +Below, some things are only needed if this is a `sensors' driver. Those +parts are between /* SENSORS ONLY START */ and /* SENSORS ONLY END */ +markers. + +This function should only return an error (any value != 0) if there is +some reason why no more detection should be done anymore. If the +detection just fails for this address, return 0. + +For now, you can ignore the `flags' parameter. It is there for future use. + + /* Unique ID allocation */ + static int foo_id = 0; + + int foo_detect_client(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) + { + int err = 0; + int i; + struct i2c_client *new_client; + struct foo_data *data; + const char *client_name = ""; /* For non-`sensors' drivers, put the real + name here! */ + + /* Let's see whether this adapter can support what we need. + Please substitute the things you need here! + For `sensors' drivers, add `! is_isa &&' to the if statement */ + if (i2c_check_functionality(adapter,I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE)) + goto ERROR0; + + /* SENSORS ONLY START */ + const char *type_name = ""; + int is_isa = i2c_is_isa_adapter(adapter); + + if (is_isa) { + + /* If this client can't be on the ISA bus at all, we can stop now + (call `goto ERROR0'). But for kicks, we will assume it is all + right. */ + + /* Discard immediately if this ISA range is already used */ + if (check_region(address,FOO_EXTENT)) + goto ERROR0; + + /* Probe whether there is anything on this address. + Some example code is below, but you will have to adapt this + for your own driver */ + + if (kind < 0) /* Only if no force parameter was used */ { + /* We may need long timeouts at least for some chips. */ + #define REALLY_SLOW_IO + i = inb_p(address + 1); + if (inb_p(address + 2) != i) + goto ERROR0; + if (inb_p(address + 3) != i) + goto ERROR0; + if (inb_p(address + 7) != i) + goto ERROR0; + #undef REALLY_SLOW_IO + + /* Let's just hope nothing breaks here */ + i = inb_p(address + 5) & 0x7f; + outb_p(~i & 0x7f,address+5); + if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { + outb_p(i,address+5); + return 0; + } + } + } + + /* SENSORS ONLY END */ + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access several i2c functions safely */ + + /* Note that we reserve some space for foo_data too. If you don't + need it, remove it. We do it here to help to lessen memory + fragmentation. */ + if (! (new_client = kmalloc(sizeof(struct i2c_client)) + + sizeof(struct foo_data), + GFP_KERNEL)) { + err = -ENOMEM; + goto ERROR0; + } + + /* This is tricky, but it will set the data to the right value. */ + client->data = new_client + 1; + data = (struct foo_data *) (client->data); + + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &foo_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. If no `force' parameter is used. */ + + /* First, the generic detection (if any), that is skipped if any force + parameter was used. */ + if (kind < 0) { + /* The below is of course bogus */ + if (foo_read(new_client,FOO_REG_GENERIC) != FOO_GENERIC_VALUE) + goto ERROR1; + } + + /* SENSORS ONLY START */ + + /* Next, specific detection. This is especially important for `sensors' + devices. */ + + /* Determine the chip type. Not needed if a `force_CHIPTYPE' parameter + was used. */ + if (kind <= 0) { + i = foo_read(new_client,FOO_REG_CHIPTYPE); + if (i == FOO_TYPE_1) + kind = chip1; /* As defined in the enum */ + else if (i == FOO_TYPE_2) + kind = chip2; + else { + printk("foo: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n",i2c_adapter_id(adapter),address); + goto ERROR1; + } + } + + /* Now set the type and chip names */ + if (kind == chip1) { + type_name = "chip1"; /* For /proc entry */ + client_name = "CHIP 1"; + } else if (kind == chip2) { + type_name = "chip2"; /* For /proc entry */ + client_name = "CHIP 2"; + } + + /* Reserve the ISA region */ + if (is_isa) + request_region(address,FOO_EXTENT,type_name); + + /* SENSORS ONLY END */ + + /* Fill in the remaining client fields. */ + strcpy(new_client->name,client_name); + + /* SENSORS ONLY BEGIN */ + data->type = kind; + /* SENSORS ONLY END */ + + new_client->id = foo_id++; /* Automatically unique */ + data->valid = 0; /* Only if you use this field */ + init_MUTEX(&data->update_lock); /* Only if you use this field */ + + /* Any other initializations in data must be done here too. */ + + /* Tell the i2c layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* SENSORS ONLY BEGIN */ + /* Register a new directory entry with module sensors. See below for + the `template' structure. */ + if ((i = sensors_register_entry(new_client, type_name, + foo_dir_table_template,THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* SENSORS ONLY END */ + + /* This function can write default values to the client registers, if + needed. */ + foo_init_client(new_client); + return 0; + + /* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR2: + /* SENSORS ONLY START */ + if (is_isa) + release_region(address,FOO_EXTENT); + /* SENSORS ONLY END */ + ERROR1: + kfree(new_client); + ERROR0: + return err; + } + + +Removing the client +=================== + +The detach_client call back function is called when a client should be +removed. It may actually fail, but only when panicking. This code is +much simpler than the attachment code, fortunately! + + int foo_detach_client(struct i2c_client *client) + { + int err,i; + + /* SENSORS ONLY START */ + /* Deregister with the `sensors' module. */ + sensors_deregister_entry(((struct lm78_data *)(client->data))->sysctl_id); + /* SENSORS ONLY END */ + + /* Try to detach the client from i2c space */ + if ((err = i2c_detach_client(client))) { + printk("foo.o: Client deregistration failed, client not detached.\n"); + return err; + } + + /* SENSORS ONLY START */ + if i2c_is_isa_client(client) + release_region(client->addr,LM78_EXTENT); + /* SENSORS ONLY END */ + + kfree(client); /* Frees client data too, if allocated at the same time */ + return 0; + } + + +Initializing the module or kernel +================================= + +When the kernel is booted, or when your foo driver module is inserted, +you have to do some initializing. Fortunately, just attaching (registering) +the driver module is usually enough. + + /* Keep track of how far we got in the initialization process. If several + things have to initialized, and we fail halfway, only those things + have to be cleaned up! */ + static int __initdata foo_initialized = 0; + + int __init foo_init(void) + { + int res; + printk("foo version %s (%s)\n",FOO_VERSION,FOO_DATE); + + if ((res = i2c_add_driver(&foo_driver))) { + printk("foo: Driver registration failed, module not inserted.\n"); + foo_cleanup(); + return res; + } + foo_initialized ++; + return 0; + } + + int __init foo_cleanup(void) + { + int res; + if (foo_initialized == 1) { + if ((res = i2c_del_driver(&foo_driver))) { + printk("foo: Driver registration failed, module not removed.\n"); + return res; + } + foo_initialized --; + } + return 0; + } + + #ifdef MODULE + + /* Substitute your own name and email address */ + MODULE_AUTHOR("Frodo Looijaard " + MODULE_DESCRIPTION("Driver for Barf Inc. Foo I2C devices"); + + int init_module(void) + { + return foo_init(); + } + + int cleanup_module(void) + { + return foo_cleanup(); + } + + #endif /* def MODULE */ + +Note that some functions are marked by `__init', and some data structures +by `__init_data'. If this driver is compiled as part of the kernel (instead +of as a module), those functions and structures can be removed after +kernel booting is completed. + +Command function +================ + +A generic ioctl-like function call back is supported. You will seldomly +need this. You may even set it to NULL. + + /* No commands defined */ + int foo_command(struct i2c_client *client, unsigned int cmd, void *arg) + { + return 0; + } + +You should never directly call this function, but instead use the +general function below: + + extern int i2c_control(struct i2c_client *,unsigned int, unsigned long); + + + +Sending and receiving +===================== + +If you want to communicate with your device, there are several functions +to do this. You can find all of them in i2c.h. + +If you can choose between plain i2c communication and SMBus level +communication, please use the last. All adapters understand SMBus level +commands, but only some of them understand plain i2c! + + +Plain i2c communication +----------------------- + + extern int i2c_master_send(struct i2c_client *,const char* ,int); + extern int i2c_master_recv(struct i2c_client *,char* ,int); + +These routines read and write some bytes from/to a client. The client +contains the i2c address, so you do not have to include it. The second +parameter contains the bytes the read/write, the third the length of the +buffer. Returned is the actual number of bytes read/written. + + extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num); + +This sends a series of messages. Each message can be a read or write, +and they can be mixed in any way. The transactions are combined: no +stop bit is sent between transaction. The i2c_msg structure contains +for each message the client address, the number of bytes of the message +and the message data itself. + +You can read the file `i2c-protocol' for more information about the +actual i2c protocol. + + +SMBus communication +------------------- + + extern s32 i2c_smbus_xfer (struct i2c_adapter * adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data); + + This is the generic SMBus function. All functions below are implemented + in terms of it. Never use this function directly! + + + extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value); + extern s32 i2c_smbus_read_byte(struct i2c_client * client); + extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value); + extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command); + extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, + u8 command, u8 value); + extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command); + extern s32 i2c_smbus_write_word_data(struct i2c_client * client, + u8 command, u16 value); + extern s32 i2c_smbus_process_call(struct i2c_client * client, + u8 command, u16 value); + extern s32 i2c_smbus_read_block_data(struct i2c_client * client, + u8 command, u8 *values); + extern s32 i2c_smbus_write_block_data(struct i2c_client * client, + u8 command, u8 length, + u8 *values); + +All these tranactions return -1 on failure. The 'write' transactions +return 0 on success; the 'read' transactions return the read value, except +for read_block, which returns the number of values read. The block buffers +need not be longer than 32 bytes. + +You can read the file `smbus-protocol' for more information about the +actual SMBus protocol. + + +General purpose routines +======================== + +Below all general purpose routines are listed, that were not mentioned +before. + + /* This call returns a unique low identifier for each registered adapter, + * or -1 if the adapter was not regisitered. + */ + extern int i2c_adapter_id(struct i2c_adapter *adap); + + +The sensors sysctl/proc interface +================================= + +This section only applies if you write `sensors' drivers. + +Each sensors driver creates a directory in /proc/sys/dev/sensors for each +registered client. The directory is called something like foo-i2c-4-65. +The sensors module helps you to do this as easily as possible. + +The template +------------ + +You will need to define a ctl_table template. This template will automatically +be copied to a newly allocated structure and filled in where necessary when +you call sensors_register_entry. + +First, I will give an example definition. + static ctl_table foo_dir_table_template[] = { + { FOO_SYSCTL_FUNC1, "func1", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real,NULL,&foo_func }, + { FOO_SYSCTL_FUNC2, "func2", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real,NULL,&foo_func }, + { FOO_SYSCTL_DATA, "data", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real,NULL,&foo_data }, + { 0 } + }; + +In the above example, three entries are defined. They can either be +accessed through the /proc interface, in the /proc/sys/dev/sensors/* +directories, as files named func1, func2 and data, or alternatively +through the sysctl interface, in the appropriate table, with identifiers +FOO_SYSCTL_FUNC1, FOO_SYSCTL_FUNC2 and FOO_SYSCTL_DATA. + +The third, sixth and ninth parameters should always be NULL, and the +fourth should always be 0. The fifth is the mode of the /proc file; +0644 is safe, as the file will be owned by root:root. + +The seventh and eigth parameters should be &sensors_proc_real and +&sensors_sysctl_real if you want to export lists of reals (scaled +integers). You can also use your own function for them, as usual. +Finally, the last parameter is the call-back to gather the data +(see below) if you use the *_proc_real functions. + + +Gathering the data +------------------ + +The call back functions (foo_func and foo_data in the above example) +can be called in several ways; the operation parameter determines +what should be done: + + * If operation == SENSORS_PROC_REAL_INFO, you must return the + magnitude (scaling) in nrels_mag; + * If operation == SENSORS_PROC_REAL_READ, you must read information + from the chip and return it in results. The number of integers + to display should be put in nrels_mag; + * If operation == SENSORS_PROC_REAL_WRITE, you must write the + supplied information to the chip. nrels_mag will contain the number + of integers, results the integers themselves. + +The *_proc_real functions will display the elements as reals for the +/proc interface. If you set the magnitude to 2, and supply 345 for +SENSORS_PROC_REAL_READ, it would display 3.45; and if the user would +write 45.6 to the /proc file, it would be returned as 4560 for +SENSORS_PROC_REAL_WRITE. A magnitude may even be negative! + +An example function: + + /* FOO_FROM_REG and FOO_TO_REG translate between scaled values and + register values. Note the use of the read cache. */ + void foo_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) + { + struct foo_data *data = client->data; + int nr = ctl_name - FOO_SYSCTL_FUNC1; /* reduce to 0 upwards */ + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + /* Update the readings cache (if necessary) */ + foo_update_client(client); + /* Get the readings from the cache */ + results[0] = FOO_FROM_REG(data->foo_func_base[nr]); + results[1] = FOO_FROM_REG(data->foo_func_more[nr]); + results[2] = FOO_FROM_REG(data->foo_func_readonly[nr]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + /* Update the cache */ + data->foo_base[nr] = FOO_TO_REG(results[0]); + /* Update the chip */ + foo_write_value(client,FOO_REG_FUNC_BASE(nr),data->foo_base[nr]); + } + if (*nrels_mag >= 2) { + /* Update the cache */ + data->foo_more[nr] = FOO_TO_REG(results[1]); + /* Update the chip */ + foo_write_value(client,FOO_REG_FUNC_MORE(nr),data->foo_more[nr]); + } + } + } diff -u --recursive --new-file v2.3.33/linux/Documentation/kernel-parameters.txt linux/Documentation/kernel-parameters.txt --- v2.3.33/linux/Documentation/kernel-parameters.txt Mon Nov 1 13:56:26 1999 +++ linux/Documentation/kernel-parameters.txt Thu Dec 16 13:57:04 1999 @@ -52,7 +52,7 @@ it will appear as a kernel argument readable via /proc/cmdline by programs running once the system is up. - 53c7xx= [HW,SCSI] + 53c7xx= [HW,SCSI] Amiga SCSI controllers adb_buttons= [HW,MOUSE] @@ -70,6 +70,8 @@ arcrimi= [HW,NET] + ataflop= [HW, M68k] + atamouse= [HW,MOUSE] Atari Mouse. atascsi= [HW,SCSI] Atari SCSI. @@ -98,22 +100,22 @@ com90xx= [HW,NET] - console= + console= [KNL] output console + comm spec (speed, control, parity) cyclades= [HW,SERIAL] Cyclades multi-serial port adapter. - debug [KNL] Enable kernel debugging. + debug [KNL] Enable kernel debugging (events log level). decnet= [HW,NET] - digi= [HW,SERIAL] + digi= [HW,SERIAL] io parameters + enable/disable command digiepca= [HW,SERIAL] dmascc= [HW,AX25,SERIAL] AX.25 Z80SCC driver with DMA support available. - dmasound= [HW,SOUND] + dmasound= [HW,SOUND] (sound subsystem buffers) dtc3181e= [HW,SCSI] @@ -123,7 +125,7 @@ edb= [HW,PS2] - ether= [HW,NET] Ethernet. + ether= [HW,NET] Ethernet cards parameters (iomem,irq,dev_name). fd_mcs= [HW,SCSI] @@ -131,7 +133,7 @@ floppy= [HW] - ftape= [HW] Floppy Tape subsystem. + ftape= [HW] Floppy Tape subsystem debugging options. gdth= [HW,SCSI] @@ -139,7 +141,8 @@ gvp11= [HW,SCSI] - hd= [EIDE] IDE and EIDE hard drive subsystem. + hd= [EIDE] (E)IDE hard drive subsystem + geometry (Cyl/heads/sectors) or tune parameters. hfmodem= [HW,AX25] @@ -147,13 +150,27 @@ hisax= [HW,ISDN] + in2000= [HW,SCSI] + + init= [KNL] + + + ibmmcascsi= [HW,MCA,SCSI] IBM MicroChannel SCSI adapter. icn= [HW,ISDN] + ide?= [HW] (E)IDE subsystem : config (iomem/irq), tuning or + debugging (serialize,reset,no{dma,tune,probe}) or + chipset specific parameters + + idebus= [HW] (E)IDE subsystem : VLB/PCI bus speed + in2000= [HW,SCSI] - init= [KNL] + init= [KNL] default init level + + initrd= [KNL] initial ramdisk path ip= [PNP] @@ -187,7 +204,7 @@ kbd-reset [VT] - load_ramdisk= [RAM] + load_ramdisk= [RAM] initrd loading boolean lp=0 [LP] Specify parallel ports to use, e.g, or lp=port[,port...] lp=none,parport0 (lp0 not configured, lp1 uses @@ -223,10 +240,13 @@ mcdx= [HW,CD] - md= [HW] + md= [HW] RAID subsystems devices and level mdacon= [MDA] + mem= [KNL] force use XX Mb of memory when the kernel is not able + to see the whole system memory or for test + msmouse= [HW,MOUSE] Microsoft Mouse. ncr5380= [HW,SCSI] @@ -241,7 +261,9 @@ nfsaddrs= [NFS] - nfsroot= [NFS] + nfsroot= [NFS] nfs root filesystem for disk-less boxes + + nmi_watchdog= [KNL, BUGS=ix86] debugging features for SMP kernels no387 [BUGS=ix86] Tells the kernel to use the 387 maths emulation library even if a 387 maths coprocessor @@ -250,6 +272,10 @@ noapic [SMP,APIC] Tells the kernel not to make use of any APIC that may be present on the system. + noasync [HW, M68K] Disables async and sync negotiation for all devices. + + nodisconnect [HW,SCSI, M68K] Disables SCSI disconnects. + no-halt [BUGS=ix86] noinitrd [RAM] Tells the kernel not to load any configured @@ -259,9 +285,11 @@ nosmp [SMP] Tells an SMP kernel to act as a UP kernel. + nosync [HW, M68K] Disables sync negotiation for all devices. + optcd= [HW,CD] - panic= + panic= [KNL] kernel behaviour on panic parport=0 [HW,PPT] Specify parallel ports. 0 or parport=auto disables. Use 'auto' to force the driver @@ -293,11 +321,11 @@ pg. [PARIDE] - pirq= [SMP,APIC] + pirq= [SMP,APIC] mp-table plip= [PPT,NET] Parallel port network link. - profile= + profile= [KNL] enable kernel profiling via /proc/profile (param:log level) prompt_ramdisk= [RAM] Whether to prompt for ramdisk before loading its contents into memory. @@ -312,16 +340,18 @@ reboot= [BUGS=ix86] - reserve= + reserve= [KNL,BUGS] force the kernel to ignore some iomem area riscom8= [HW,SERIAL] ro [KNL] Mount root device read-only on boot. - root= + root= [KNL] root filesystem rw [KNL] Mount root device read-write on boot. + S [KNL] run init in single mode + sbpcd= [HW,CD] Soundblaster CD adapter. scsi_logging= [SCSI] @@ -336,12 +366,14 @@ specialix= [HW,SERIAL] Specialix multi-serial port adapter. - st= [HW] + st= [HW] SCSI tape parameters (buffers, ..) st0x= [HW,SCSI] stram_swap= [HW] + switches= [HW, M68K] + sym53c416= [HW,SCSI] sym53c8xx= [HW,SCSI] @@ -356,7 +388,10 @@ u14-34f= [HW,SCSI] - video= [FB] + video= [FB] frame buffer configuration + + vga= [KNL] on ix386, enable to choose a peculiar video mode + vga=ask wd33c93= [HW,SCSI] @@ -364,6 +399,6 @@ wdt= [HW] - xd= [HW,XT] + xd= [HW,XT] Original XT pre-IDE (RLL encoded) disks xd_geo= [HW,XT] diff -u --recursive --new-file v2.3.33/linux/Documentation/proc_usb_info.txt linux/Documentation/proc_usb_info.txt --- v2.3.33/linux/Documentation/proc_usb_info.txt Tue Jul 27 16:05:50 1999 +++ linux/Documentation/proc_usb_info.txt Mon Dec 20 14:10:06 1999 @@ -1,6 +1,6 @@ /proc/bus/usb filesystem output =============================== -(version 19990722) +(version 19991218) The /proc filesystem for USB devices generates @@ -22,9 +22,11 @@ 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. @@ -41,19 +43,26 @@ Topology info: -T: Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd If#=ddd MxCh=dd Driver=%s -| | | | | | | | | |__DriverName -| | | | | | | | |__MaxChildren -| | | | | | | |__Configured InterfaceNumber -| | | | | | |__Device Speed in Mbps -| | | | | |__DeviceNumber -| | | | |__Count of devices at this level -| | | |__Connector/Port on Parent for this device -| | |__Parent DeviceNumber -| |__Level in topology +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 @@ -77,6 +86,17 @@ |__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 @@ -89,7 +109,8 @@ Interface descriptor info (can be multiple per Config): -I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx +I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss +| | | | | | | |__Driver name | | | | | | |__InterfaceProtocol | | | | | |__InterfaceSubClass | | | | |__InterfaceClass @@ -103,8 +124,8 @@ E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms -| | | | |__Interval -| | | |__EndpointMaxPacketSize +| | | | |__Interval +| | | |__EndpointMaxPacketSize | | |__Attributes(EndpointType) | |__EndpointAddress(I=In,O=Out) |__Endpoint info tag diff -u --recursive --new-file v2.3.33/linux/Documentation/serial-console.txt linux/Documentation/serial-console.txt --- v2.3.33/linux/Documentation/serial-console.txt Thu Aug 26 13:05:34 1999 +++ linux/Documentation/serial-console.txt Mon Dec 20 14:54:38 1999 @@ -11,6 +11,7 @@ device: tty0 for the foreground virtual console ttyX for any other virtual console ttySx for a serial port + lp0 for the first parallel port options: depend on the driver. For the serial port this defines the baudrate/parity/bits of the port, diff -u --recursive --new-file v2.3.33/linux/Documentation/video4linux/bttv/CARDLIST linux/Documentation/video4linux/bttv/CARDLIST --- v2.3.33/linux/Documentation/video4linux/bttv/CARDLIST Wed Dec 31 16:00:00 1969 +++ linux/Documentation/video4linux/bttv/CARDLIST Thu Dec 16 13:59:38 1999 @@ -0,0 +1,53 @@ +make[1]: Entering directory `/home/kraxel/2/src/bttv-0.7.10/driver' +bttv.o + card=0 - unknown + card=1 - MIRO PCTV + card=2 - Hauppauge old + card=3 - STB + card=4 - Intel + card=5 - Diamond DTV2000 + card=6 - AVerMedia TVPhone + card=7 - MATRIX-Vision MV-Delta + card=8 - Fly Video II + card=9 - TurboTV + card=10 - Hauppauge new (bt878) + card=11 - MIRO PCTV pro + card=12 - ADS Technologies Channel Surfer TV + card=13 - AVerMedia TVCapture 98 + card=14 - Aimslab VHX + card=15 - Zoltrix TV-Max + card=16 - Pixelview PlayTV (bt878) + card=17 - Leadtek WinView 601 + card=18 - AVEC Intercapture + card=19 - LifeView FlyKit w/o Tuner + card=20 - CEI Raffles Card + card=21 - Lucky Star Image World ConferenceTV + card=22 - Phoebe Tv Master + FM + card=23 - Modular Technology MM205 PCTV, bt878 + card=24 - Magic TView CPH061 (bt878) + card=25 - Terratec/Vobis TV-Boostar + card=26 - Newer Hauppauge WinCam (bt878) + card=27 - MAXI TV Video PCI2 + card=28 - Terratec TerraTV+ + card=29 - Imagenation PXC200 + card=30 - FlyVideo 98 + card=31 - iProTV + card=32 - Intel Create and Share PCI + card=33 - Askey/Typhoon/Anubis Magic TView + +tuner.o + type=0 - Temic PAL + type=1 - Philips PAL_I + type=2 - Philips NTSC + type=3 - Philips SECAM + type=4 - NoTuner + type=5 - Philips PAL + type=6 - Temic NTSC + type=7 - Temic PAL_I + type=8 - Temic 4036 FY5 NTSC + type=9 - Alps HSBH1 + type=10 - Alps TSBE1 + type=11 - Alps TSBB5 + type=12 - Alps TSBE5 + type=13 - Alps TSBC5 +make[1]: Leaving directory `/home/kraxel/2/src/bttv-0.7.10/driver' diff -u --recursive --new-file v2.3.33/linux/Documentation/video4linux/bttv/CARDS linux/Documentation/video4linux/bttv/CARDS --- v2.3.33/linux/Documentation/video4linux/bttv/CARDS Wed Oct 27 16:34:12 1999 +++ linux/Documentation/video4linux/bttv/CARDS Wed Dec 31 16:00:00 1969 @@ -1,114 +0,0 @@ -Suppported cards: - - -Bt848/Bt848a/Bt849/Bt878/Bt879 cards ------------------------------------- - -All cards with Bt848/Bt848a/Bt849/Bt878/Bt879 and normal Composite/S-VHS inputs -are supported. -Teletext and Intercast support (PAL only) via VBI samples decoding in software. - -Some cards with additional multiplexing of inputs are only partially -supported (unless specifications by the card manufacturer are given). - -All other cards only differ by additional components as tuners, sound decoders, -EEPROMs, teletext decoders ... - -Tuner and sound decoder support for Bt878/879 is not fully working yet. - - -MATRIX Vision -------------- - -MV-Delta -- Bt848A -- 4 Composite inputs, 1 S-VHS input (shared with 4th composite) -- EEPROM - -http://www.matrix-vision.de/ - -This card has no tuner but supports all 4 composite (1 shared with an -S-VHS input) of the Bt848A. -Very nice card if you only have satellite TV but several tuners connected -to the card via composite. - -Many thanks to Matrix-Vision for giving us 2 cards for free which made -Bt848a/Bt849 single crytal operation support possible!!! - - - -Miro/Pinnacle PCTV ------------------- - -- Bt848 - some (all??) come with 2 crystals for PAL/SECAM and NTSC -- PAL, SECAM or NTSC TV tuner (Philips or TEMIC) -- MSP34xx sound decoder on add on board - decoder is supported but AFAIK does not yet work - (other sound MUX setting in GPIO port needed??? somebody who fixed this???) -- 1 tuner, 1 composite and 1 S-VHS input -- tuner type is autodetected - -http://www.miro.de/ -http://www.miro.com/ - - -Many thanks for the free card which made first NTSC support possible back -in 1997! - - -Hauppauge Win/TV pci --------------------- - -There are many different versions of the Hauppauge cards with different -tuners (TV+Radio ...), teletext decoders. -Note that even cards with same model numbers have (depending on the revision) -different chips on it. - -- Bt848 (and others but always in 2 crystal operation???) - newer cards have a Bt878, I2C support for it is still experimental -- PAL, SECAM, NTSC or tuner with or without Radio support - -e.g.: - PAL: - TDA5737: VHF, hyperband and UHF mixer/oscillator for TV and VCR 3-band tuners - TSA5522: 1.4 GHz I2C-bus controlled synthesizer, I2C 0xc2-0xc3 - - NTSC: - TDA5731: VHF, hyperband and UHF mixer/oscillator for TV and VCR 3-band tuners - TSA5518: no datasheet available on Philips site -- Philips SAA5246 or SAA5284 ( or no) Teletext decoder chip - with buffer RAM (e.g. Winbond W24257AS-35: 32Kx8 CMOS static RAM) - SAA5246 (I2C 0x22) is supported -- 256 bytes EEPROM: Microchip 24LC02B or Philips 8582E2Y - with configuration information - I2C address 0xa0 (24LC02B also responds to 0xa2-0xaf) -- 1 tuner, 1 composite and (depending on model) 1 S-VHS input -- 14052B: mux for selection of sound source -- sound decoder: TDA9800, MSP34xx (stereo cards) - - -AverMedia ---------- -... - - -ADS Channel Surfer ------------------- -... - - -Maxi TV Video PCI 2 card ------------------------- -... - - -Image World ConferenceTV ------------------------- -Doesn't work: - - autodetect. Use card=21 - - sound mute. Use the line-in volume of your soundcard - - radio tuner. Since the card doesn't have an antenna, it is quite - understandable ;) However, you can hear some stations if you - ``ln -s /dev/bttv0 /dev/radio'' - =20 diff -u --recursive --new-file v2.3.33/linux/Documentation/video4linux/bttv/INSTALL linux/Documentation/video4linux/bttv/INSTALL --- v2.3.33/linux/Documentation/video4linux/bttv/INSTALL Mon May 10 13:00:10 1999 +++ linux/Documentation/video4linux/bttv/INSTALL Wed Dec 31 16:00:00 1969 @@ -1,82 +0,0 @@ -- Make sure you have a recent 2.0.x kernel (I recommend AT LEAST 2.0.33!) - or a recent 2.1.x kernel. - Older kernels might lead to problems. - -- Do NOT compile videodev into your kernel! - Use the module supplied with bttv. - -- Edit "driver/Makefile": - - - If you do NOT have a Miro card: - Adjust TUNER to a number between 0 and 7. - - This number has the following meaning: - - 0: Temic PAL tuner - 1: Philips PAL_I tuner - 2: Philips NTSC tuner - 3: Philips SECAM tuner - 4: no tuner - 5: Philips PAL tuner - 6: Temic NTSC tuner - 7: Temic PAL tuner - 8: Temic 4036 FY5 NTSC tuner - - The number corresponds to the number (-1) given at the GPIO port of the - Bt848 on Miro cards. - - - - Adjust CARD to one of the numbers below: - - 0: Auto-Detect - 1: Miro - 2: Hauppauge - 3: STB - 4: Intel - 5: Diamond - 6: AVerMedia - 7: Matrix Vision MV-Delta - 8: Fly Video II - 9: TurboTV - 10: Newer Hauppauge (Bt878) - 11: Miro PCTV Pro - 12: ADS Tech Channel Surfer TV (and maybe TV+FM) - 13: AVerMedia TVCapture 98 - 14: Aimslab VHX - 15: Zoltrix TV-Max - - - You may have to adjust BTTV_MAJOR to a different number depending on your - kernel version. The official number 81 does not work on some setups. - But instead of changing it, better update to a newer kernel. - - - If you have a Bt848a or Bt849 on your board you might have to - uncomment: -DUSE_PLL - -- do a "make" in the main directory. - -If you have Hauppauge card read "README.HAUPPAUGE" before proceeding. - -- type "make ins" - - This creates the bttv devices in /dev and installs the bttv module - - Look in the kernel log file (/var/adm/syslog or /var/log/kernel or something - else depending on your /etc/syslogd.conf or just call "dmesg") - and see what bttv reported (lines starting with "bttv:") - If the installation failed and you send e-mail to me always include those - lines! Dumps of the insmod output alone do not help at all. - -- Start X11 in hi or true color mode - 8 bit color is also supported but really ugly! - (If you have an S3 card you might have to start X11 before installing - the module!) - - If you have Motif or LessTif, "xtvscreen" in the "XTV" directory should - have been compiled with the "make" above. - Otherwise use the statically linked version which should be available - on the web site you got bttv from. - Read the documentation in "XTV" and start xtvscreen. - -- make applications by typing "make" in "apps" - - diff -u --recursive --new-file v2.3.33/linux/Documentation/video4linux/bttv/Insmod-options linux/Documentation/video4linux/bttv/Insmod-options --- v2.3.33/linux/Documentation/video4linux/bttv/Insmod-options Wed Dec 31 16:00:00 1969 +++ linux/Documentation/video4linux/bttv/Insmod-options Thu Dec 16 13:59:38 1999 @@ -0,0 +1,82 @@ + +bttv.o + the bt848 (grabber chip) driver + + insmod args: + card=n card type, see cardlist for a list. + radio=0/1 card supports radio + pll=0/1/2 pll settings + 0: don't use PLL + 1: 28 MHz crystal installed + 2: 35 MHz crystal installed + triton1=0/1 for Triton1 compatibility + Triton1 is automatically recognized + but this might also help with other chipsets + bigendian=n Set the endianness of the gfx framebuffer. + Default is native endian. + fieldnr=1 Count fields. Some TV descrambling software + needs this, for others it only generates + 50 useless IRQs/sec. + + remap, card, radio and pll accept up to four comma-separted arguments + (for multiple boards). + +msp3400.o + The driver for the msp34xx sound processor chips. If you have a + stereo card, you probably want to insmod this one. + + insmod args: + debug=1/2 print some debug info to the syslog, + 2 is more verbose. + simple=1 Use the "short programming" method. Newer + msp34xx versions support this. You need this + for dbx stereo. + once=1 Don't check the TV-stations Audio mode + every few seconds, but only once after + channel switches. + amsound=1 Audio carrier is AM/NICAM at 6.5 Mhz. This + should improve things for french people, the + carrier autoscan seems to work with FM only... + mixer=n allocate mixer device #n. Default is the + first free slot. + +tea6300.o + The driver for the tea6300 fader chip. If you have a stereo + card and the msp3400.o doesn't work, you might want to try this + one. This chip is seen on most STB TV/FM cards (usually from + Gateway OEM sold surplus on auction sites). + + insmod args: + debug=1 print some debug info to the syslog. + +tda8425.o + The driver for the tda8425 fader chip. This driver used to be + part of bttv.c, so if your sound used to work but does not + anymore, try loading this module. + + insmod args: + debug=1 print some debug info to the syslog. + +tda9855.o + The driver for the tda9855 audio chip. Afaik, only the + Diamond DTV2000 has this chip. + + insmod args: + debug=1 print some debug info to the syslog. + +tuner.o + The tuner driver. You need this unless you want to use only + with a camera or external tuner ... + + insmod args: + debug=1 print some debug info to the syslog + type=n type of the tuner chip. n as follows: + 0: Temic PAL tuner + 1: Philips PAL_I tuner + 2: Philips NTSC tuner + 3: Philips SECAM tuner + 4: no tuner + 5: Philips PAL tuner + 6: Temic NTSC tuner + 7: Temic PAL tuner + diff -u --recursive --new-file v2.3.33/linux/Documentation/video4linux/bttv/MAKEDEV linux/Documentation/video4linux/bttv/MAKEDEV --- v2.3.33/linux/Documentation/video4linux/bttv/MAKEDEV Wed Dec 31 16:00:00 1969 +++ linux/Documentation/video4linux/bttv/MAKEDEV Thu Dec 16 13:59:38 1999 @@ -0,0 +1,28 @@ +#!/bin/bash + +function makedev () { + + for dev in 0 1 2 3; do + echo "/dev/$1$dev: char 81 $[ $2 + $dev ]" + rm -f /dev/$1$dev + mknod /dev/$1$dev c 81 $[ $2 + $dev ] + chmod 666 /dev/$1$dev + done + + # symlink for default device + rm -f /dev/$1 + ln -s /dev/${1}0 /dev/$1 +} + +# see http://roadrunner.swansea.uk.linux.org/v4lapi.shtml + +echo "*** new device names ***" +makedev video 0 +makedev radio 64 +makedev vtx 192 +makedev vbi 224 + +#echo "*** old device names (for compatibility only) ***" +#makedev bttv 0 +#makedev bttv-fm 64 +#makedev bttv-vbi 224 diff -u --recursive --new-file v2.3.33/linux/Documentation/video4linux/bttv/Modules.conf linux/Documentation/video4linux/bttv/Modules.conf --- v2.3.33/linux/Documentation/video4linux/bttv/Modules.conf Wed Dec 31 16:00:00 1969 +++ linux/Documentation/video4linux/bttv/Modules.conf Thu Dec 16 13:59:38 1999 @@ -0,0 +1,15 @@ +# i2c +alias char-major-89 i2c-dev +options i2c-core i2c_debug=1 +options i2c-algo-bit bit_test=1 + +# bttv +alias char-major-81 videodev +alias char-major-81-0 bttv +options bttv card=2 radio=1 +options tuner debug=1 + +# make alsa + msp3400 play nicely +options snd-card-ens snd_index=0 +options msp3400 mixer=1 + diff -u --recursive --new-file v2.3.33/linux/Documentation/video4linux/bttv/README linux/Documentation/video4linux/bttv/README --- v2.3.33/linux/Documentation/video4linux/bttv/README Sun Aug 23 13:32:25 1998 +++ linux/Documentation/video4linux/bttv/README Thu Dec 16 13:59:38 1999 @@ -1,41 +1,88 @@ -bttv - BT848 frame grabber driver -Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de) - & Marcus Metzler (mocm@thp.uni-koeln.de) - according to GNU GPL in file COPYING. - - -Bttv is a device driver for frame grabber cards using the Bt848 family -of video decoder chips. -Among those are the Bt848, Bt848A, Bt849, Bt878 and Bt879. -The only major differences between the cards by different manufacturers -are the types of tuners and extra components on the boards. -E.g., some cards by Hauppauge have an additional Videotext decoder -and/or sound decoder chip. -Also type (Composite or S-VHS) and number of inputs differ. -Other Brooktree chips (e.g. the Bt829) or chips by other manufacturers -(Philips, Zoran, ...) are NOT supported by bttv. - -You can use several cards at the same time. -Interrupts can be shared with other Bt848 cards or any other drivers -which allow it. -The (arbitrary) maximum number of cards is 4 but can be adjusted by -changing BTTV_MAX at the beginning of bttv.c if you need more. -(But which board has more than 4 PCI slots plus 1 for the VGA card?) - -Bttv is a standard component of all newer 2.1.x kernels. -This distribution additionally supports 2.0.x kernels and all other -changes and improvements which did not make it into the kernel version -yet. -It also includes versions of videodev.c, i2.c, tuner.c and others -which are the same as in the latest 2.1.x kernel but with 2.0.x support. -A kernel version >2.0.30 is recommended. - -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -Although bttv is now used and tested by many people it still might crash your -computer! Take all precautions to avoid data loss until you are certain -bttv runs on your setup without problems. -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +Release notes for bttv-0.7.x +============================ -The latest version of bttv can be found at: -http://www.thp.uni-koeln.de/~rjkm/linux/bttv.html +This version is based on Ralphs 0.6.4 release. There are alot of +changes. Bugfixes, merged patches from other people, merged fixes +from the kernel version, port to the new i2c stack, removed support +for 2.0.x, code cleanups, ... + +You'll need the new i2c stack, download it from + http://www2.lm-sensors.nu/~lm78/download.html + +You'll find Ralphs original (mostly outdated) documentation in the +ralphs-doc subdirectory. + + +Compile bttv +------------ + +If you are compiling the kernel version, just say 'm' if you are asked +for bttv. I /strongly/ suggest to compile bttv as module, because +there are some insmod options for configuring the driver. + +If you downloaded the separate bttv bundle: You need configured kernel +sources to compile the bttv driver. The driver uses some Makefile +magic to compile the modules with your kernel's configuration +(wrt. module-versions, SMP, ...). If you already have compiled the +kernel at least once, you probably don't have do worry about this. If +not, go to /usr/src/linux and run at least "make config". Even +better, compile your own kernel, you'll never become a real hacker +else ;-) + + +Make bttv work with your card +----------------------------- + +Of cource you have to load the modules as very first thing. The +separate bttv bundle comes with a script called "update". I use this +one to load a new version while doing driver hacking. You can use it +too, but check the module arguments first. They work for my setup, +and probably do *not* for yours. Another way is to setup your +/etc/modules.conf file and let kmod load the modules. See also: + +Modules.conf: some sample entries for /etc/modules.conf +Insmod-options: list of all insmod options available for bttv and + the helper modules. +MAKEDEV: a script to create the special files for v4l +CARDLIST: List of all supported cards + +Loading just the bttv modules is'nt enouth for most cards. The +drivers for the i2c tuner/sound chips must also be loaded. bttv tries +to load them automagically by calling request_module() now, but this +obviously works only with kmod enabled. + +The most important insmod option for bttv is "card=n" to select the +correct card type. If you get video but no sound you've very likely +specified the wrong (or no) card type. A list of supported cards is +in CARDLIST. + +If your card is'nt listed in CARDLIST, you should read the Sound-FAQ. + + +Still does'nt work? +------------------- + +I do NOT have a lab with 30+ different grabber boards and a +PAL/NTSC/SECAM test signal generator at home, so I often can't +reproduce your problems. This makes debugging very difficuilt for me. +If you have some knowledge and spare time, please try to fix this +yourself (patches very welcome of course...) You know: The linux +slogan is "Do it yourself". + +There is a mailing list: video4linux-list@redhat.com. If you have +trouble with some specific TV card, try to ask there instead of +mailing me directly. The chance that someone with the same card +listens there is much higher... + + +Finally: If you mail some patches for bttv around the world (to +linux-kernel/Alan/Linus/...), please Cc: me. + + +Have fun with bttv, + + Gerd + +-- +Gerd Knorr diff -u --recursive --new-file v2.3.33/linux/Documentation/video4linux/bttv/README.FIRST linux/Documentation/video4linux/bttv/README.FIRST --- v2.3.33/linux/Documentation/video4linux/bttv/README.FIRST Sat Feb 6 12:46:20 1999 +++ linux/Documentation/video4linux/bttv/README.FIRST Wed Dec 31 16:00:00 1969 @@ -1,4 +0,0 @@ -o Please direct queries about the in kernel version of this driver to - Alan Cox first not to Ralph, or better yet join the video4linux mailing - list (mail video4linux-list-request@redhat.com with "subscribe") - diff -u --recursive --new-file v2.3.33/linux/Documentation/video4linux/bttv/README.Hauppauge linux/Documentation/video4linux/bttv/README.Hauppauge --- v2.3.33/linux/Documentation/video4linux/bttv/README.Hauppauge Sun Aug 23 13:32:25 1998 +++ linux/Documentation/video4linux/bttv/README.Hauppauge Wed Dec 31 16:00:00 1969 @@ -1,29 +0,0 @@ -The current I2C-Code could by accident overwrite the configuration EEPROM on -Hauppauge boards!!! -(E.g. the videotext driver and the bt848 driver do not know about each other. -This might cause unwanted states on the I2C bus which overwrite the EEPROM) - -Back up this EEPROM before doing anything else by typing: -(do this AFTER installing bttv.o with "make ins" but BEFORE starting the -X application) - -make readee -readee > tvee.h - -If you encounter any problems in Windows95 (like "PNP component not found" ...) -go back into linux, load bttv and type: - -make writeee -writeee - -to write the backed up contents. -If you backed up your EEPROM as described above, this will restore it to its -original state. -A detailed description of the meaning of the EEPROM bytes by -Hauppauge would of course be even more helpful! - -If you have board type 405 and you did not make a backup, my tvee.h file in -mytvee.h might be of help. - -Forget about all of the above if you do not have a Hauppauge board. - diff -u --recursive --new-file v2.3.33/linux/Documentation/video4linux/bttv/README.MIRO linux/Documentation/video4linux/bttv/README.MIRO --- v2.3.33/linux/Documentation/video4linux/bttv/README.MIRO Sun Aug 23 13:32:25 1998 +++ linux/Documentation/video4linux/bttv/README.MIRO Wed Dec 31 16:00:00 1969 @@ -1,3 +0,0 @@ -The right tuner type should automatically be detected. -Look in your kernel log files to see which one bttv thinks it is. - diff -u --recursive --new-file v2.3.33/linux/Documentation/video4linux/bttv/README.PCI linux/Documentation/video4linux/bttv/README.PCI --- v2.3.33/linux/Documentation/video4linux/bttv/README.PCI Sun Aug 23 13:32:25 1998 +++ linux/Documentation/video4linux/bttv/README.PCI Wed Dec 31 16:00:00 1969 @@ -1,36 +0,0 @@ -Because some people were asking about the bandwidth the Bt848 might use up -on the PCI bus I did a little benchmark. - -"bonnie -s 200" with a Fireball TM 3.8 Gb using Busmaster DMA on an ASUS P6NP5 - -without capturing: - - -------Sequential Output-------- ---Sequential Input-- --Random-- - -Per Char- --Block--- -Rewrite-- -Per Char- --Block--- --Seeks--- -Machine MB K/sec %CPU K/sec %CPU K/sec %CPU K/sec %CPU K/sec %CPU /sec %CPU - 200 5353 76.6 5898 16.9 2363 12.1 5889 51.3 6416 10.2 37.8 0.9 - - -while capturing full screen PAL (786x576) with 24bpp: - - -------Sequential Output-------- ---Sequential Input-- --Random-- - -Per Char- --Block--- -Rewrite-- -Per Char- --Block--- --Seeks--- -Machine MB K/sec %CPU K/sec %CPU K/sec %CPU K/sec %CPU K/sec %CPU /sec %CPU - 200 5619 69.3 5939 16.9 2334 12.0 5859 50.9 6441 10.5 37.9 0.9 - -The differences are small and probably within the normal error margin of -bonnie. -So, one bt848 card does not have much(any?) impact on the normal operation -of a Linux system. -If you have several cards running this will look very differently! -The same is probably true if your Linux box is used as a file server -with 15 (or 30) SCSI drives. - -I tested having 2 Bt848 cards grabbing in 32 bit mode (That's almost 100MB/s!) -while running bonnie. -The xtvscreen windows showed severe pixel errors. -After a while the ide driver failed to use DMA and switched DMA off. -It continued running but the results where bad. - - - diff -u --recursive --new-file v2.3.33/linux/Documentation/video4linux/bttv/README.RADIO linux/Documentation/video4linux/bttv/README.RADIO --- v2.3.33/linux/Documentation/video4linux/bttv/README.RADIO Mon Jul 5 20:04:47 1999 +++ linux/Documentation/video4linux/bttv/README.RADIO Wed Dec 31 16:00:00 1969 @@ -1,15 +0,0 @@ -Support is in now: - - Turn on the "big red switch" of the sound processor. - - two ioctls to access (some) registers of the sound processor. - - a function in the TV-Widget which monitors the signal quality - and does the mono/stereo switching. - -So you should have TV with (stereo) sound now. Radio does _not_ work. -It probably does not work with sat receivers. I can't test this and -therefore have not added support for it yet. If someone needs this and -can help testing the sat stuff, drop me a note. - - Gerd - --- -Gerd Knorr diff -u --recursive --new-file v2.3.33/linux/Documentation/video4linux/bttv/Sound-FAQ linux/Documentation/video4linux/bttv/Sound-FAQ --- v2.3.33/linux/Documentation/video4linux/bttv/Sound-FAQ Wed Dec 31 16:00:00 1969 +++ linux/Documentation/video4linux/bttv/Sound-FAQ Thu Dec 16 13:59:38 1999 @@ -0,0 +1,90 @@ + +bttv and sound mini howto +========================= + +There are alot of different bt848/849/878/879 based boards available. +Making video work often is not a big deal, because this is handled +completely by the bt8xx chip, which is common on all boards. But +sound is handled in slightly different ways on each board. + +To handle the grabber boards correctly, there is a array tvcards[] in +bttv.c, which holds the informations required for each board. Sound +will work only, if the correct entry is used (for video it often makes +no difference). The bttv driver prints a line to the kernel log, +telling which card type is used. Like this one: + + bttv0: model: BT848(Hauppauge old) + +You should verify this is correct. If it is'nt, you have to pass the +correct board type as insmod argument, "insmod bttv card=2" for +example. The file MODULES in the driver directory has a list of valid +arguments. If your card is'nt listed there, you might check the +source code for new entries which are not listed yet. If there is'nt +one for your card, you can check if one of the existing entries does +work for you (just trial and error...). + +Some boards have an extra processor for sound to do stereo decoding +and other nice features. The msp34xx chips are used by Hauppauge for +example. If your board has one, you might have to load a helper +module like msp3400.o to make sound work. If there is'nt one for the +chip used on your board: Bad luck. Start writing a new one. Well, +you might want to check the video4linux mailing list archive first... + +Of course you need a correctly installed soundcard unless you have the +speakers connected directly to the grabber board. Hint: check the +mixer settings too... + + +How sound works in detail +========================= + +Still does'nt work? Looks like some driver hacking is required. +Below is a do-it-yourself description for you. + +The bt8xx chips have 32 general purpose pins, and registers to control +these pins. One register is the output enable register +(BT848_GPIO_OUT_EN), it says which pins are actively driven by the +bt848 chip. Another one is the data register (BT848_GPIO_DATA), where +you can get/set the status if these pins. They can be used for input +and output. + +All grabber board vendors use these pins to control an external chip +which does the sound routing. But every board is a little different. +These pins are also used by some companies to drive remote control +receiver chips. + +As mentioned above, there is a array which holds the required +informations for each known board. You basically have to create a new +line for your board. What is in there: + +struct tvcard +{ + char *name; + int inputs; /* number of video inputs */ + int tuner; /* which of them is the tuner */ + int svhs; /* which of them is the svhs input */ + u32 gpiomask; + u32 muxsel[8]; /* video mux */ + u32 audiomux[6]; /* audio mux: Tuner, Radio, external, internal, mute, stereo */ + u32 gpiomask2; /* GPIO MUX mask (this is video) */ +}; + +gpiomask has all bits set which are used to control the audio mux. +This value basically goes to the gpio output enable register. It is +also used to mask bits when switching the audio mux (which is done by +read-modify-write on the gpio data register). + +What you have to do is figure out the correct values for gpiomask and +the audiomux array. If you have Windows and the drivers four your +card installed, you might to check out if you can read these registers +values used by the windows driver. A tool to do this is available +from ftp://telepresence.dmem.strath.ac.uk/pub/bt848/winutil +If you hav'nt Windows installed, this is a trial and error game... + +Good luck, + + Gerd + + +PS: If you have a new working entry, mail it to Ralph. So it can be + included into next driver version... diff -u --recursive --new-file v2.3.33/linux/MAINTAINERS linux/MAINTAINERS --- v2.3.33/linux/MAINTAINERS Tue Dec 14 01:27:23 1999 +++ linux/MAINTAINERS Mon Dec 20 15:43:01 1999 @@ -122,13 +122,13 @@ AHA152X SCSI DRIVER P: Juergen E. Fischer -M: Juergen Fischer +M: Juergen Fischer L: linux-scsi@vger.rutgers.edu S: Maintained APM DRIVER P: Stephen Rothwell -M: sfr@linuxcare.com +M: apm@linuxcare.com.au L: linux-laptop@vger.rutgers.edu W: http://linuxcare.com.au/apm/ S: Supported @@ -733,6 +733,12 @@ L: linux-ppp@vger.rutgers.edu S: Maintained +PROMISE DC4030 CACHING DISK CONTROLLER DRIVER +P: Peter Denison +M: promise@pnd-pc.demon.co.uk +W: http://www.pnd-pc.demon.co.uk/promise/ +S: Maintained + RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER P: Corey Thomas M: corey@world.std.com @@ -938,7 +944,7 @@ W: http://www.linux-usb.org S: Supported -USB HUB AND UHCI DRIVERS +USB HUB P: Johannes Erdfelt M: jerdfelt@sventech.com L: linux-usb@suse.com @@ -952,6 +958,20 @@ S: Maintained (not yet usable) W: http://suitenine.com/usb/ +USB SERIAL DRIVER +P: Greg Kroah-Hartman +M: greg@kroah.com +L: linux-usb@suse.com +S: Maintained +W: http://www.kroah.com/linux-usb/ + +USB UHCI DRIVER +P: Georg Acher +M: usb@in.tum.de +L: linux-usb@suse.com +W: http://usb.in.tum.de +S: Maintained + VFAT FILESYSTEM: P: Gordon Chaffee M: chaffee@cs.berkeley.edu @@ -993,6 +1013,13 @@ P: Henner Eisen M: eis@baty.hanse.de L: linux-x25@vger.rutgers.edu +S: Maintained + +RTLINUX REALTIME LINUX +P: Victor Yodaiken +M: yodaiken@fsmlabs.com +L: rtl@rtlinux.org +W: www.rtlinux.org S: Maintained Z85230 SYNCHRONOUS DRIVER diff -u --recursive --new-file v2.3.33/linux/Makefile linux/Makefile --- v2.3.33/linux/Makefile Wed Dec 15 10:43:16 1999 +++ linux/Makefile Mon Dec 20 14:43:39 1999 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 3 -SUBLEVEL = 33 +SUBLEVEL = 34 EXTRAVERSION = ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) @@ -22,7 +22,7 @@ AS =$(CROSS_COMPILE)as LD =$(CROSS_COMPILE)ld -CC =$(CROSS_COMPILE)gcc -D__KERNEL__ -I$(HPATH) +CC =$(CROSS_COMPILE)gcc CPP =$(CC) -E AR =$(CROSS_COMPILE)ar NM =$(CROSS_COMPILE)nm @@ -86,16 +86,18 @@ # standard CFLAGS # -CFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer - -# use '-fno-strict-aliasing', but only if the compiler can take it -CFLAGS += $(shell if $(CC) -fno-strict-aliasing -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-fno-strict-aliasing"; fi) +CPPFLAGS := -D__KERNEL__ -I$(HPATH) ifdef CONFIG_SMP -CFLAGS += -D__SMP__ -AFLAGS += -D__SMP__ +CPPFLAGS += -D__SMP__ endif +CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +AFLAGS := $(CPPFLAGS) + +# use '-fno-strict-aliasing', but only if the compiler can take it +CFLAGS += $(shell if $(CC) -fno-strict-aliasing -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-fno-strict-aliasing"; fi) + # # if you want the RAM disk device, define this to be the # size in blocks. @@ -231,6 +233,10 @@ ifeq ($(CONFIG_IRDA),y) DRIVERS := $(DRIVERS) drivers/net/irda/irda_drivers.a +endif + +ifeq ($(CONFIG_I2C),y) +DRIVERS := $(DRIVERS) drivers/i2c/i2c.a endif include arch/$(ARCH)/Makefile diff -u --recursive --new-file v2.3.33/linux/arch/alpha/Makefile linux/arch/alpha/Makefile --- v2.3.33/linux/arch/alpha/Makefile Wed Dec 15 10:43:16 1999 +++ linux/arch/alpha/Makefile Wed Dec 15 10:32:47 1999 @@ -29,16 +29,16 @@ # the host compiler might have on by default. Given that EV4 and EV5 # have the same instruction set, prefer EV5 because an EV5 schedule is # more likely to keep an EV4 processor busy than vice-versa. - mcpu_done := + mcpu_done := n ifeq ($(CONFIG_ALPHA_GENERIC),y) CFLAGS := $(CFLAGS) -mcpu=ev5 mcpu_done := y endif - ifeq ($(mcpu_done)$(CONFIG_ALPHA_PYXIS),y) + ifeq ($(mcpu_done)$(CONFIG_ALPHA_PYXIS),ny) CFLAGS := $(CFLAGS) -mcpu=ev56 mcpu_done := y endif - ifeq ($(mcpu_done)$(CONFIG_ALPHA_POLARIS),y) + ifeq ($(mcpu_done)$(CONFIG_ALPHA_POLARIS),ny) ifeq ($(have_mcpu_pca56),y) CFLAGS := $(CFLAGS) -mcpu=pca56 else @@ -46,15 +46,15 @@ endif mcpu_done := y endif - ifeq ($(mcpu_done)$(CONFIG_ALPHA_NAUTILUS)$(have_mcpu_ev67),yy) + ifeq ($(mcpu_done)$(CONFIG_ALPHA_NAUTILUS)$(have_mcpu_ev67),nyy) CFLAGS := $(CFLAGS) -mcpu=ev67 mcpu_done := y endif - ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV4),y) + ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV4),ny) CFLAGS := $(CFLAGS) -mcpu=ev4 mcpu_done := y endif - ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV6),y) + ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV6),ny) ifeq ($(have_mcpu_ev6),y) CFLAGS := $(CFLAGS) -mcpu=ev6 else diff -u --recursive --new-file v2.3.33/linux/arch/alpha/boot/Makefile linux/arch/alpha/boot/Makefile --- v2.3.33/linux/arch/alpha/boot/Makefile Mon Oct 12 11:40:12 1998 +++ linux/arch/alpha/boot/Makefile Mon Dec 20 14:43:39 1999 @@ -11,9 +11,9 @@ LINKFLAGS = -static -T bootloader.lds #-N -relax .S.s: - $(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $< + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -E -o $*.o $< .S.o: - $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $< OBJECTS = head.o main.o BPOBJECTS = head.o bootp.o diff -u --recursive --new-file v2.3.33/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S --- v2.3.33/linux/arch/alpha/kernel/entry.S Tue Aug 31 17:29:12 1999 +++ linux/arch/alpha/kernel/entry.S Mon Dec 20 14:41:47 1999 @@ -8,7 +8,7 @@ #define SIGCHLD 20 -#define NR_SYSCALLS 373 +#define NR_SYSCALLS 374 /* * These offsets must match with alpha_mv in . @@ -84,8 +84,8 @@ ldq $1,8($30); \ ldq $2,16($30); \ ldq $3,24($30); \ - ldq $20,152($30); \ - ldq $21,HAE_CACHE($19); \ + ldq $21,152($30); \ + ldq $20,HAE_CACHE($19); \ ldq $4,32($30); \ ldq $5,40($30); \ ldq $6,48($30); \ @@ -1149,3 +1149,4 @@ .quad sys_sendfile /* 370 */ .quad sys_setresgid .quad sys_getresgid + .quad sys_ni_syscall /* sys_dipc */ diff -u --recursive --new-file v2.3.33/linux/arch/alpha/lib/Makefile linux/arch/alpha/lib/Makefile --- v2.3.33/linux/arch/alpha/lib/Makefile Tue Dec 7 09:32:40 1999 +++ linux/arch/alpha/lib/Makefile Mon Dec 20 14:43:39 1999 @@ -14,16 +14,16 @@ $(AR) rcs lib.a $(OBJS) __divqu.o: divide.S - $(CC) -DDIV -c -o __divqu.o divide.S + $(CC) $(AFLAGS) -DDIV -c -o __divqu.o divide.S __remqu.o: divide.S - $(CC) -DREM -c -o __remqu.o divide.S + $(CC) $(AFLAGS) -DREM -c -o __remqu.o divide.S __divlu.o: divide.S - $(CC) -DDIV -DINTSIZE -c -o __divlu.o divide.S + $(CC) $(AFLAGS) -DDIV -DINTSIZE -c -o __divlu.o divide.S __remlu.o: divide.S - $(CC) -DREM -DINTSIZE -c -o __remlu.o divide.S + $(CC) $(AFLAGS) -DREM -DINTSIZE -c -o __remlu.o divide.S dep: diff -u --recursive --new-file v2.3.33/linux/arch/arm/boot/compressed/Makefile linux/arch/arm/boot/compressed/Makefile --- v2.3.33/linux/arch/arm/boot/compressed/Makefile Mon Nov 1 13:56:26 1999 +++ linux/arch/arm/boot/compressed/Makefile Mon Dec 20 14:43:39 1999 @@ -6,7 +6,7 @@ HEAD = head.o OBJS = misc.o SYSTEM = $(TOPDIR)/vmlinux -CFLAGS = -O2 -DSTDC_HEADERS $(CFLAGS_PROC) +CFLAGS = $(CPPFLAGS) -O2 -DSTDC_HEADERS $(CFLAGS_PROC) FONTC = $(TOPDIR)/drivers/video/font_acorn_8x8.c ZLDFLAGS = -p -X -T vmlinux.lds @@ -77,7 +77,7 @@ $(LD) $(ZLDFLAGS) $(HEAD) $(OBJS) piggy.o $(GCCLIB) -o vmlinux $(HEAD): $(HEAD:.o=.S) - $(CC) -traditional -c $(HEAD:.o=.S) + $(CC) $(AFLAGS) -traditional -c $(HEAD:.o=.S) piggy.o: $(SYSTEM) $(OBJCOPY) $(SYSTEM) piggy @@ -86,7 +86,7 @@ rm -f piggy piggy.gz font.o: $(FONTC) - $(CC) -Dstatic= -c -o $@ $(FONTC) + $(CC) $(CFLAGS) -Dstatic= -c -o $@ $(FONTC) vmlinux.lds: vmlinux.lds.in @sed "$(SEDFLAGS)" < vmlinux.lds.in > $@ diff -u --recursive --new-file v2.3.33/linux/arch/arm/kernel/Makefile linux/arch/arm/kernel/Makefile --- v2.3.33/linux/arch/arm/kernel/Makefile Fri Oct 22 13:21:44 1999 +++ linux/arch/arm/kernel/Makefile Mon Dec 20 14:43:39 1999 @@ -75,7 +75,7 @@ endif $(HEAD_OBJ): $(HEAD_OBJ:.o=.S) - $(CC) -D__ASSEMBLY__ -DTEXTADDR=$(TEXTADDR) -traditional -c $(HEAD_OBJ:.o=.S) -o $@ + $(CC) -D__ASSEMBLY__ $(AFLAGS) -DTEXTADDR=$(TEXTADDR) -traditional -c $(HEAD_OBJ:.o=.S) -o $@ include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.3.33/linux/arch/arm/kernel/calls.S linux/arch/arm/kernel/calls.S --- v2.3.33/linux/arch/arm/kernel/calls.S Tue Aug 31 17:29:12 1999 +++ linux/arch/arm/kernel/calls.S Thu Dec 16 16:25:37 1999 @@ -85,7 +85,7 @@ .long SYMBOL_NAME(sys_sigpending) .long SYMBOL_NAME(sys_sethostname) /* 75 */ .long SYMBOL_NAME(sys_setrlimit) - .long SYMBOL_NAME(sys_getrlimit) + .long SYMBOL_NAME(sys_old_getrlimit) .long SYMBOL_NAME(sys_getrusage) .long SYMBOL_NAME(sys_gettimeofday) .long SYMBOL_NAME(sys_settimeofday) @@ -200,8 +200,15 @@ .long SYMBOL_NAME(sys_ni_syscall) .long SYMBOL_NAME(sys_ni_syscall) /* 190 */ .long SYMBOL_NAME(sys_vfork_wrapper) + .long SYMBOL_NAME(sys_getrlimit) + .long SYMBOL_NAME(sys_mmap2) + .long SYMBOL_NAME(sys_truncate64) + .long SYMBOL_NAME(sys_ftruncate64) +/* 195 */ .long SYMBOL_NAME(sys_stat64) + .long SYMBOL_NAME(sys_lstat64) + .long SYMBOL_NAME(sys_fstat64) - .rept NR_syscalls-186 + .rept NR_syscalls-197 .long SYMBOL_NAME(sys_ni_syscall) .endr #endif diff -u --recursive --new-file v2.3.33/linux/arch/i386/boot/Makefile linux/arch/i386/boot/Makefile --- v2.3.33/linux/arch/i386/boot/Makefile Tue Nov 23 22:42:20 1999 +++ linux/arch/i386/boot/Makefile Mon Dec 20 14:43:39 1999 @@ -49,7 +49,7 @@ $(AS) -o $@ $< bootsect.s: bootsect.S Makefile $(BOOT_INCL) - $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + $(CPP) $(CPPFLAGS) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ bbootsect: bbootsect.o $(LD) -Ttext 0x0 -s -oformat binary $< -o $@ @@ -58,7 +58,7 @@ $(AS) -o $@ $< bbootsect.s: bootsect.S Makefile $(BOOT_INCL) - $(CPP) -D__BIG_KERNEL__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + $(CPP) $(CPPFLAGS) -D__BIG_KERNEL__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ setup: setup.o $(LD) -Ttext 0x0 -s -oformat binary -e begtext -o $@ $< @@ -67,7 +67,7 @@ $(AS) -o $@ $< setup.s: setup.S video.S Makefile $(BOOT_INCL) $(TOPDIR)/include/linux/version.h $(TOPDIR)/include/linux/compile.h - $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + $(CPP) $(CPPFLAGS) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ bsetup: bsetup.o $(LD) -Ttext 0x0 -s -oformat binary -e begtext -o $@ $< @@ -76,7 +76,7 @@ $(AS) -o $@ $< bsetup.s: setup.S video.S Makefile $(BOOT_INCL) $(TOPDIR)/include/linux/version.h $(TOPDIR)/include/linux/compile.h - $(CPP) -D__BIG_KERNEL__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + $(CPP) $(CPPFLAGS) -D__BIG_KERNEL__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ dep: diff -u --recursive --new-file v2.3.33/linux/arch/i386/boot/compressed/Makefile linux/arch/i386/boot/compressed/Makefile --- v2.3.33/linux/arch/i386/boot/compressed/Makefile Mon Oct 11 15:38:14 1999 +++ linux/arch/i386/boot/compressed/Makefile Mon Dec 20 14:43:39 1999 @@ -9,7 +9,7 @@ OBJECTS = $(HEAD) misc.o -CFLAGS = -O2 -DSTDC_HEADERS +CFLAGS = $(CPPFLAGS) -O2 -DSTDC_HEADERS ZLDFLAGS = -e startup_32 # diff -u --recursive --new-file v2.3.33/linux/arch/i386/config.in linux/arch/i386/config.in --- v2.3.33/linux/arch/i386/config.in Wed Dec 8 14:11:25 1999 +++ linux/arch/i386/config.in Mon Dec 20 15:43:01 1999 @@ -129,6 +129,8 @@ bool ' Ignore multiple suspend/resume cycles' CONFIG_APM_IGNORE_SUSPEND_BOUNCE bool ' RTC stores time in GMT' CONFIG_APM_RTC_IS_GMT bool ' Allow interrupts during APM BIOS calls' CONFIG_APM_ALLOW_INTS + bool ' Entry point offset fix (some Acer laptops)' CONFIG_APM_BAD_ENTRY_OFFSET + bool ' Use real mode APM BIOS call to power off' CONFIG_APM_REAL_MODE_POWER_OFF fi endmenu diff -u --recursive --new-file v2.3.33/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v2.3.33/linux/arch/i386/defconfig Tue Dec 14 01:27:23 1999 +++ linux/arch/i386/defconfig Mon Dec 20 16:00:37 1999 @@ -105,8 +105,6 @@ # CONFIG_BLK_DEV_IDEDMA_PCI is not set # CONFIG_BLK_DEV_OFFBOARD is not set # CONFIG_BLK_DEV_AEC6210 is not set -CONFIG_BLK_DEV_PIIX=y -# CONFIG_BLK_DEV_PIIX_TUNING is not set # CONFIG_IDE_CHIPSETS is not set # CONFIG_BLK_CPQ_DA is not set @@ -311,6 +309,7 @@ # CONFIG_PCMCIA_3C575 is not set # CONFIG_PCMCIA_TULIP is not set # CONFIG_PCMCIA_EPIC100 is not set +CONFIG_NET_PCMCIA_RADIO=y CONFIG_PCMCIA_RAYCS=y # CONFIG_PCMCIA_NETWAVE is not set # CONFIG_PCMCIA_WAVELAN is not set @@ -349,6 +348,11 @@ CONFIG_UNIX98_PTY_COUNT=256 # +# I2C support +# +# CONFIG_I2C is not set + +# # Mice # # CONFIG_BUSMOUSE is not set @@ -393,7 +397,7 @@ # CONFIG_PCMCIA_SERIAL_CB is not set # -# Support for USB +# USB support # # CONFIG_USB is not set diff -u --recursive --new-file v2.3.33/linux/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c --- v2.3.33/linux/arch/i386/kernel/apm.c Tue Dec 14 01:27:23 1999 +++ linux/arch/i386/kernel/apm.c Mon Dec 20 15:43:01 1999 @@ -34,6 +34,7 @@ * Jan 1999, Version 1.8 * Jan 1999, Version 1.9 * Oct 1999, Version 1.10 + * Nov 1999, Version 1.11 * * History: * 0.6b: first version in official kernel, Linux 1.3.46 @@ -101,6 +102,14 @@ * configurable (default on). * Make debug only a boot time parameter (remove APM_DEBUG). * Try to blank all devices on any error. + * 1.11: Remove APM dependencies in drivers/char/console.c + * Check nr_running to detect if we are idle (from + * Borislav Deianov ) + * Fix for bioses that don't zero the top part of the + * entrypoint offset (Mario Sitta ) + * (reported by Panos Katsaloulis ). + * Real mode power off patch (Walter Hofmann + * ). * * APM 1.1 Reference: * @@ -135,6 +144,7 @@ #include #include #include +#include #include #include @@ -149,8 +159,14 @@ EXPORT_SYMBOL(apm_unregister_callback); extern unsigned long get_cmos_time(void); +extern void machine_real_restart(unsigned char *, int); +#ifdef CONFIG_MAGIC_SYSRQ extern void (*sysrq_power_off)(void); +#endif +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) +extern int (*console_blank_hook)(int); +#endif /* * The apm_bios device is one of the misc char devices. @@ -269,7 +285,7 @@ static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); static struct apm_bios_struct * user_list = NULL; -static char driver_version[] = "1.10"; /* no spaces */ +static char driver_version[] = "1.11"; /* no spaces */ static char * apm_event_name[] = { "system standby", @@ -380,7 +396,7 @@ __asm__ __volatile__(APM_DO_ZERO_SEGS "pushl %%edi\n\t" "pushl %%ebp\n\t" - "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "; cld\n\t" + "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" "setc %%al\n\t" "popl %%ebp\n\t" "popl %%edi\n\t" @@ -413,7 +429,7 @@ __asm__ __volatile__(APM_DO_ZERO_SEGS "pushl %%edi\n\t" "pushl %%ebp\n\t" - "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry)"; cld\n\t" + "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry)"\n\t" "setc %%bl\n\t" "popl %%ebp\n\t" "popl %%edi\n\t" @@ -551,24 +567,24 @@ * they are doing because they booted with the smp-power-off * kernel option. */ - if (apm_enabled || (smp_hack == 2)) - (void) apm_set_power_state(APM_STATE_OFF); -} - -#ifdef CONFIG_APM_DISPLAY_BLANK -/* Called by apm_display_blank and apm_display_unblank when apm_enabled. */ -static int apm_set_display_power_state(u_short state) -{ - int error; + if (apm_enabled || (smp_hack == 2)) { +#ifdef CONFIG_APM_REAL_MODE_POWER_OFF + unsigned char po_bios_call[] = { + 0xb8, 0x00, 0x10, /* movw $0x1000,ax */ + 0x8e, 0xd0, /* movw ax,ss */ + 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */ + 0xb8, 0x07, 0x53, /* movw $0x5307,ax */ + 0xbb, 0x01, 0x00, /* movw $0x0001,bx */ + 0xb9, 0x03, 0x00, /* movw $0x0003,cx */ + 0xcd, 0x15 /* int $0x15 */ + }; - /* Blank the first display device */ - error = set_power_state(0x0100, state); - if (error != APM_SUCCESS) - /* try to blank them all instead */ - error = set_power_state(0x01ff, state); - return error; -} + machine_real_restart(po_bios_call, sizeof(po_bios_call)); +#else + (void) apm_set_power_state(APM_STATE_OFF); #endif + } +} #ifdef CONFIG_APM_DO_ENABLE static int __init apm_enable_power_management(void) @@ -651,33 +667,25 @@ str, err); } +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) /* Called from console driver -- must make sure apm_enabled. */ -int apm_display_blank(void) +static int apm_console_blank(int blank) { -#ifdef CONFIG_APM_DISPLAY_BLANK - if (apm_enabled) { - int error = apm_set_display_power_state(APM_STATE_STANDBY); - if ((error == APM_SUCCESS) || (error == APM_NO_ERROR)) - return 1; - apm_error("set display standby", error); - } -#endif - return 0; -} + int error; + u_short state; -/* Called from console driver -- must make sure apm_enabled. */ -int apm_display_unblank(void) -{ -#ifdef CONFIG_APM_DISPLAY_BLANK - if (apm_enabled) { - int error = apm_set_display_power_state(APM_STATE_READY); - if ((error == APM_SUCCESS) || (error == APM_NO_ERROR)) - return 1; - apm_error("set display ready", error); - } -#endif + state = blank ? APM_STATE_STANDBY : APM_STATE_READY; + /* Blank the first display device */ + error = set_power_state(0x100, state); + if (error != APM_SUCCESS) + /* try to blank them all instead */ + error = set_power_state(0x1ff, state); + if ((error == APM_SUCCESS) || (error == APM_NO_ERROR)) + return 1; + apm_error("set display", error); return 0; } +#endif int apm_register_callback(int (*callback)(apm_event_t)) { @@ -884,12 +892,15 @@ case APM_USER_STANDBY: #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND if (waiting_for_resume) - return; - waiting_for_resume = 1; + break; #endif - if (send_event(event, APM_STANDBY_RESUME, NULL) - && (standbys_pending <= 0)) - standby(); + if (send_event(event, APM_STANDBY_RESUME, NULL)) { +#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND + waiting_for_resume = 1; +#endif + if (standbys_pending <= 0) + standby(); + } break; case APM_USER_SUSPEND: @@ -905,12 +916,15 @@ #endif #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND if (waiting_for_resume) - return; - waiting_for_resume = 1; + break; +#endif + if (send_event(event, APM_NORMAL_RESUME, NULL)) { +#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND + waiting_for_resume = 1; #endif - if (send_event(event, APM_NORMAL_RESUME, NULL) - && (suspends_pending <= 0)) - suspend(); + if (suspends_pending <= 0) + suspend(); + } break; case APM_NORMAL_RESUME: @@ -947,21 +961,18 @@ static void apm_event_handler(void) { static int pending_count = 0; + int err; - if (((standbys_pending > 0) || (suspends_pending > 0)) - && (apm_bios_info.version > 0x100)) { - if (pending_count-- <= 0) { - int err; - + if ((standbys_pending > 0) || (suspends_pending > 0)) { + if ((apm_bios_info.version > 0x100) && (pending_count-- <= 0)) { pending_count = 4; err = apm_set_power_state(APM_STATE_BUSY); if (err) apm_error("busy", err); } - } else { + } else pending_count = 0; - check_events(); - } + check_events(); } /* @@ -970,12 +981,8 @@ * Check whether we're the only running process to * decide if we should just power down. * - * Do this by checking the runqueue: if we're the - * only one, then the current process run_list will - * have both prev and next pointing to the same - * entry (the true idle process) */ -#define system_idle() (current->run_list.next == current->run_list.prev) +#define system_idle() (nr_running == 1) static void apm_mainloop(void) { @@ -1367,10 +1374,13 @@ /* Install our power off handler.. */ if (power_off_enabled) acpi_power_off = apm_power_off; - #ifdef CONFIG_MAGIC_SYSRQ sysrq_power_off = apm_power_off; #endif +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) + console_blank_hook = apm_console_blank; +#endif + apm_mainloop(); return 0; } @@ -1492,6 +1502,9 @@ _set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4)); apm_bios_entry.offset = apm_bios_info.offset; +#ifdef CONFIG_APM_BAD_ENTRY_OFFSET + apm_bios_entry.offset &= 0xffff; +#endif apm_bios_entry.segment = APM_CS; set_base(gdt[APM_CS >> 3], __va((unsigned long)apm_bios_info.cseg << 4)); diff -u --recursive --new-file v2.3.33/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S --- v2.3.33/linux/arch/i386/kernel/entry.S Wed Dec 8 14:11:25 1999 +++ linux/arch/i386/kernel/entry.S Mon Dec 20 15:35:25 1999 @@ -92,8 +92,8 @@ pushl %ecx; \ pushl %ebx; \ movl $(__KERNEL_DS),%edx; \ - movl %dx,%ds; \ - movl %dx,%es; + movl %edx,%ds; \ + movl %edx,%es; #define RESTORE_ALL \ popl %ebx; \ @@ -288,15 +288,15 @@ pushl %ecx pushl %ebx cld - movl %es,%cx + movl %es,%ecx xchgl %eax, ORIG_EAX(%esp) # orig_eax (get the error code. ) movl %esp,%edx xchgl %ecx, ES(%esp) # get the address and save es. pushl %eax # push the error code pushl %edx movl $(__KERNEL_DS),%edx - movl %dx,%ds - movl %dx,%es + movl %edx,%ds + movl %edx,%es GET_CURRENT(%ebx) call *%ecx addl $8,%esp @@ -595,7 +595,10 @@ .long SYMBOL_NAME(sys_mmap2) .long SYMBOL_NAME(sys_truncate64) .long SYMBOL_NAME(sys_ftruncate64) - /* 195 */ + .long SYMBOL_NAME(sys_stat64) /* 195 */ + .long SYMBOL_NAME(sys_lstat64) + .long SYMBOL_NAME(sys_fstat64) + /* * NOTE!! This doesn't have to be exact - we just have @@ -603,6 +606,6 @@ * entries. Don't panic if you notice that this hasn't * been shrunk every time we add a new system call. */ - .rept NR_syscalls-194 + .rept NR_syscalls-197 .long SYMBOL_NAME(sys_ni_syscall) .endr diff -u --recursive --new-file v2.3.33/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S --- v2.3.33/linux/arch/i386/kernel/head.S Thu Nov 11 20:11:31 1999 +++ linux/arch/i386/kernel/head.S Mon Dec 20 15:35:25 1999 @@ -47,10 +47,10 @@ */ cld movl $(__KERNEL_DS),%eax - movl %ax,%ds - movl %ax,%es - movl %ax,%fs - movl %ax,%gs + movl %eax,%ds + movl %eax,%es + movl %eax,%fs + movl %eax,%gs #ifdef __SMP__ orw %bx,%bx jz 1f @@ -231,13 +231,13 @@ lidt idt_descr ljmp $(__KERNEL_CS),$1f 1: movl $(__KERNEL_DS),%eax # reload all the segment registers - movl %ax,%ds # after changing gdt. - movl %ax,%es - movl %ax,%fs - movl %ax,%gs + movl %eax,%ds # after changing gdt. + movl %eax,%es + movl %eax,%fs + movl %eax,%gs #ifdef __SMP__ movl $(__KERNEL_DS), %eax - mov %ax,%ss # Reload the stack pointer (segment only) + movl %eax,%ss # Reload the stack pointer (segment only) #else lss stack_start,%esp # Load processor stack #endif @@ -323,8 +323,8 @@ pushl %es pushl %ds movl $(__KERNEL_DS),%eax - movl %ax,%ds - movl %ax,%es + movl %eax,%ds + movl %eax,%es pushl $int_msg call SYMBOL_NAME(printk) popl %eax diff -u --recursive --new-file v2.3.33/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c --- v2.3.33/linux/arch/i386/kernel/process.c Tue Dec 14 01:27:23 1999 +++ linux/arch/i386/kernel/process.c Mon Dec 20 15:43:01 1999 @@ -69,6 +69,16 @@ void (*acpi_power_off)(void) = NULL; /* + * We use this if we don't have any better + * idle routine.. + */ +static void default_idle(void) +{ + if (current_cpu_data.hlt_works_ok && !hlt_counter) + asm volatile("sti ; hlt" : : : "memory"); +} + +/* * The idle thread. There's no useful work to be * done, so just try to conserve power and have a * low exit latency (ie sit in a loop waiting for @@ -82,26 +92,16 @@ current->counter = -100; while (1) { - while (!current->need_resched) { - if (!current_cpu_data.hlt_works_ok) - continue; - if (hlt_counter) - continue; - asm volatile("sti ; hlt" : : : "memory"); - } + void (*idle)(void) = acpi_idle; + if (!idle) + idle = default_idle; + while (!current->need_resched) + idle(); schedule(); check_pgt_cache(); - if (acpi_idle) - acpi_idle(); } } -/* - * This routine reboots the machine by asking the keyboard - * controller to pulse the reset-line low. We try that for a while, - * and if it doesn't work, we do some other stupid things. - */ - static long no_idt[2] = {0, 0}; static int reboot_mode = 0; static int reboot_thru_bios = 0; @@ -187,7 +187,10 @@ 0x74, 0x02, /* jz f */ 0x0f, 0x08, /* invd */ 0x24, 0x10, /* f: andb $0x10,al */ - 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */ + 0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */ +}; +static unsigned char jump_to_bios [] = +{ 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */ }; @@ -200,34 +203,13 @@ break; } -void machine_restart(char * __unused) +/* + * Switch to real mode and then execute the code + * specified by the code and length parameters. + * We assume that length will aways be less that 100! + */ +void machine_real_restart(unsigned char *code, int length) { -#if __SMP__ - /* - * Stop all CPUs and turn off local APICs and the IO-APIC, so - * other OSs see a clean IRQ state. - */ - smp_send_stop(); - disable_IO_APIC(); -#endif - - if(!reboot_thru_bios) { - /* rebooting needs to touch the page at absolute addr 0 */ - *((unsigned short *)__va(0x472)) = reboot_mode; - for (;;) { - int i; - for (i=0; i<100; i++) { - kb_wait(); - udelay(50); - outb(0xfe,0x64); /* pulse reset low */ - udelay(50); - } - /* That didn't work - force a triple fault.. */ - __asm__ __volatile__("lidt %0": :"m" (no_idt)); - __asm__ __volatile__("int3"); - } - } - cli(); /* Write zero to CMOS register number 0x0f, which the BIOS POST @@ -273,8 +255,9 @@ off paging. Copy it near the end of the first page, out of the way of BIOS variables. */ - memcpy ((void *) (0x1000 - sizeof (real_mode_switch)), + memcpy ((void *) (0x1000 - sizeof (real_mode_switch) - 100), real_mode_switch, sizeof (real_mode_switch)); + memcpy ((void *) (0x1000 - 100), code, length); /* Set up the IDT for real mode. */ @@ -293,11 +276,11 @@ the values are consistent for real mode operation already. */ __asm__ __volatile__ ("movl $0x0010,%%eax\n" - "\tmovl %%ax,%%ds\n" - "\tmovl %%ax,%%es\n" - "\tmovl %%ax,%%fs\n" - "\tmovl %%ax,%%gs\n" - "\tmovl %%ax,%%ss" : : : "eax"); + "\tmovl %%eax,%%ds\n" + "\tmovl %%eax,%%es\n" + "\tmovl %%eax,%%fs\n" + "\tmovl %%eax,%%gs\n" + "\tmovl %%eax,%%ss" : : : "eax"); /* Jump to the 16-bit code that we copied earlier. It disables paging and the cache, switches to real mode, and jumps to the BIOS reset @@ -305,7 +288,38 @@ __asm__ __volatile__ ("ljmp $0x0008,%0" : - : "i" ((void *) (0x1000 - sizeof (real_mode_switch)))); + : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100))); +} + +void machine_restart(char * __unused) +{ +#if __SMP__ + /* + * Stop all CPUs and turn off local APICs and the IO-APIC, so + * other OSs see a clean IRQ state. + */ + smp_send_stop(); + disable_IO_APIC(); +#endif + + if(!reboot_thru_bios) { + /* rebooting needs to touch the page at absolute addr 0 */ + *((unsigned short *)__va(0x472)) = reboot_mode; + for (;;) { + int i; + for (i=0; i<100; i++) { + kb_wait(); + udelay(50); + outb(0xfe,0x64); /* pulse reset low */ + udelay(50); + } + /* That didn't work - force a triple fault.. */ + __asm__ __volatile__("lidt %0": :"m" (no_idt)); + __asm__ __volatile__("int3"); + } + } + + machine_real_restart(jump_to_bios, sizeof(jump_to_bios)); } void machine_halt(void) diff -u --recursive --new-file v2.3.33/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c --- v2.3.33/linux/arch/i386/kernel/setup.c Wed Dec 8 14:11:25 1999 +++ linux/arch/i386/kernel/setup.c Wed Dec 15 08:44:20 1999 @@ -592,13 +592,16 @@ */ max_pfn = 0; for (i = 0; i < e820.nr_map; i++) { - unsigned long curr_pfn; + unsigned long start, end; /* RAM? */ if (e820.map[i].type != E820_RAM) continue; - curr_pfn = PFN_DOWN(e820.map[i].addr + e820.map[i].size); - if (curr_pfn > max_pfn) - max_pfn = curr_pfn; + start = PFN_UP(e820.map[i].addr); + end = PFN_DOWN(e820.map[i].addr + e820.map[i].size); + if (start >= end) + continue; + if (end > max_pfn) + max_pfn = end; } /* diff -u --recursive --new-file v2.3.33/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- v2.3.33/linux/arch/i386/kernel/smp.c Wed Dec 8 14:11:25 1999 +++ linux/arch/i386/kernel/smp.c Wed Dec 15 08:57:57 1999 @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -638,11 +639,18 @@ */ static unsigned int __init get_8254_timer_count(void) { + extern rwlock_t xtime_lock; + unsigned long flags; + unsigned int count; + write_lock_irqsave(&xtime_lock, flags); + outb_p(0x00, 0x43); count = inb_p(0x40); count |= inb_p(0x40) << 8; + + write_unlock_irqrestore(&xtime_lock, flags); return count; } diff -u --recursive --new-file v2.3.33/linux/arch/i386/kernel/vm86.c linux/arch/i386/kernel/vm86.c --- v2.3.33/linux/arch/i386/kernel/vm86.c Tue Nov 23 22:42:20 1999 +++ linux/arch/i386/kernel/vm86.c Mon Dec 20 15:35:25 1999 @@ -260,7 +260,7 @@ mark_screen_rdonly(tsk); unlock_kernel(); __asm__ __volatile__( - "xorl %%eax,%%eax; movl %%ax,%%fs; movl %%ax,%%gs\n\t" + "xorl %%eax,%%eax; movl %%eax,%%fs; movl %%eax,%%gs\n\t" "movl %0,%%esp\n\t" "jmp ret_from_sys_call" : /* no outputs */ diff -u --recursive --new-file v2.3.33/linux/arch/i386/math-emu/Makefile linux/arch/i386/math-emu/Makefile --- v2.3.33/linux/arch/i386/math-emu/Makefile Tue Dec 9 17:57:09 1997 +++ linux/arch/i386/math-emu/Makefile Mon Dec 20 14:43:39 1999 @@ -10,7 +10,7 @@ CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin $(MATH_EMULATION) .S.o: - $(CC) -D__ASSEMBLY__ $(PARANOID) -c $< + $(CC) -D__ASSEMBLY__ $(AFLAGS) $(PARANOID) -c $< # From 'C' language sources: C_OBJS =fpu_entry.o errors.o \ diff -u --recursive --new-file v2.3.33/linux/arch/m68k/fpsp040/Makefile linux/arch/m68k/fpsp040/Makefile --- v2.3.33/linux/arch/m68k/fpsp040/Makefile Fri Dec 20 01:19:58 1996 +++ linux/arch/m68k/fpsp040/Makefile Mon Dec 20 14:43:39 1999 @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile... .S.o: - $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $< OS_TARGET := fpsp.o diff -u --recursive --new-file v2.3.33/linux/arch/m68k/ifpsp060/Makefile linux/arch/m68k/ifpsp060/Makefile --- v2.3.33/linux/arch/m68k/ifpsp060/Makefile Fri Dec 20 01:19:58 1996 +++ linux/arch/m68k/ifpsp060/Makefile Mon Dec 20 14:43:39 1999 @@ -5,7 +5,7 @@ # for more details. .S.o: - $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $< OS_TARGET := ifpsp.o diff -u --recursive --new-file v2.3.33/linux/arch/m68k/kernel/Makefile linux/arch/m68k/kernel/Makefile --- v2.3.33/linux/arch/m68k/kernel/Makefile Tue Sep 7 12:14:06 1999 +++ linux/arch/m68k/kernel/Makefile Mon Dec 20 14:43:39 1999 @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile... .S.o: - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o ifndef CONFIG_SUN3 all: head.o kernel.o diff -u --recursive --new-file v2.3.33/linux/arch/m68k/lib/Makefile linux/arch/m68k/lib/Makefile --- v2.3.33/linux/arch/m68k/lib/Makefile Wed Sep 25 00:47:40 1996 +++ linux/arch/m68k/lib/Makefile Mon Dec 20 14:43:39 1999 @@ -3,7 +3,7 @@ # .S.o: - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $@ + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $@ L_TARGET = lib.a L_OBJS = ashrdi3.o checksum.o memcpy.o memcmp.o memset.o semaphore.o diff -u --recursive --new-file v2.3.33/linux/arch/m68k/math-emu/Makefile linux/arch/m68k/math-emu/Makefile --- v2.3.33/linux/arch/m68k/math-emu/Makefile Sun Aug 15 11:47:29 1999 +++ linux/arch/m68k/math-emu/Makefile Mon Dec 20 14:43:39 1999 @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile... .S.o: - $(CC) $(EXTRA_CFLAGS) -D__ASSEMBLY__ -traditional -c $< -o $*.o + $(CC) $(EXTRA_CFLAGS) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o #EXTRA_CFLAGS=-DFPU_EMU_DEBUG diff -u --recursive --new-file v2.3.33/linux/arch/m68k/sun3/Makefile linux/arch/m68k/sun3/Makefile --- v2.3.33/linux/arch/m68k/sun3/Makefile Tue Sep 7 12:14:06 1999 +++ linux/arch/m68k/sun3/Makefile Mon Dec 20 14:43:39 1999 @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile... .S.o: - $(CC) -D__ASSEMBLY__ -traditional -Wa,-m68020 -c $< -o $*.o + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -Wa,-m68020 -c $< -o $*.o O_TARGET := sun3.o O_OBJS := config.o idprom.o mmu_emu.o sun3ints.o leds.o dvma.o sbus.o diff -u --recursive --new-file v2.3.33/linux/arch/m68k/sun3/prom/Makefile linux/arch/m68k/sun3/prom/Makefile --- v2.3.33/linux/arch/m68k/sun3/prom/Makefile Tue Sep 7 12:14:06 1999 +++ linux/arch/m68k/sun3/prom/Makefile Mon Dec 20 14:43:40 1999 @@ -17,6 +17,6 @@ sync dep: - $(CPP) -M *.c > .depend + $(CPP) $(CPPFLAGS) -M *.c > .depend include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.3.33/linux/arch/mips/arc/Makefile linux/arch/mips/arc/Makefile --- v2.3.33/linux/arch/mips/arc/Makefile Fri Jun 25 17:40:12 1999 +++ linux/arch/mips/arc/Makefile Mon Dec 20 14:43:40 1999 @@ -18,6 +18,6 @@ sync dep: - $(CPP) -M *.c > .depend + $(CPP) $(CPPFLAGS) -M *.c > .depend include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.3.33/linux/arch/mips/dec/prom/Makefile linux/arch/mips/dec/prom/Makefile --- v2.3.33/linux/arch/mips/dec/prom/Makefile Fri Jun 25 17:40:12 1999 +++ linux/arch/mips/dec/prom/Makefile Mon Dec 20 14:43:40 1999 @@ -24,6 +24,6 @@ locore.o: locore.S dep: - $(CPP) -M *.c > .depend + $(CPP) $(CPPFLAGS) -M *.c > .depend include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.3.33/linux/arch/mips/sgi/kernel/Makefile linux/arch/mips/sgi/kernel/Makefile --- v2.3.33/linux/arch/mips/sgi/kernel/Makefile Fri Jun 25 17:40:13 1999 +++ linux/arch/mips/sgi/kernel/Makefile Mon Dec 20 14:43:40 1999 @@ -28,6 +28,6 @@ indyIRQ.o: indyIRQ.S dep: - $(CPP) -M *.c > .depend + $(CPP) $(CPPFLAGS) -M *.c > .depend include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.3.33/linux/arch/ppc/Makefile linux/arch/ppc/Makefile --- v2.3.33/linux/arch/ppc/Makefile Tue Dec 7 09:32:41 1999 +++ linux/arch/ppc/Makefile Mon Dec 20 14:43:40 1999 @@ -20,8 +20,8 @@ ASFLAGS = LINKFLAGS = -T arch/ppc/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic -CFLAGSINC = -D__KERNEL__ -I$(TOPDIR)/include -D__powerpc__ -CFLAGS := $(CFLAGS) -I$(HPATH) -D__powerpc__ -fsigned-char \ +CPPFLAGS := $(CPPFLAGS) -D__powerpc__ +CFLAGS := $(CFLAGS) -D__powerpc__ -fsigned-char \ -msoft-float -pipe -fno-builtin -ffixed-r2 -Wno-uninitialized \ -mmultiple -mstring CPP = $(CC) -E $(CFLAGS) diff -u --recursive --new-file v2.3.33/linux/arch/ppc/boot/Makefile linux/arch/ppc/boot/Makefile --- v2.3.33/linux/arch/ppc/boot/Makefile Tue Dec 7 09:32:41 1999 +++ linux/arch/ppc/boot/Makefile Mon Dec 20 14:43:40 1999 @@ -16,9 +16,9 @@ .c.o: $(CC) $(CFLAGS) -DINITRD_OFFSET=$(IOFF) -DINITRD_SIZE=$(ISZ) -DZIMAGE_OFFSET=$(ZOFF) -DZIMAGE_SIZE=$(ZSZ) -c -o $*.o $< .S.s: - $(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $< + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -E -o $*.o $< .S.o: - $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $< ZOFF = 0 ZSZ = 0 @@ -52,7 +52,7 @@ GZIP_FLAGS = -v9 OBJECTS := head.o misc.o ../coffboot/zlib.o -CFLAGS = -O2 -DSTDC_HEADERS -fno-builtin -I$(TOPDIR)/include +CFLAGS = $(CPPFLAGS) -O2 -DSTDC_HEADERS -fno-builtin OBJCOPY = $(CROSS_COMPILE)objcopy OBJCOPY_ARGS = -O elf32-powerpc @@ -140,7 +140,7 @@ $(TOPDIR)/scripts/mkdep *.[Sch] > .depend dep: - $(CPP) -M *.S *.c > .depend + $(CPP) $(CPPFLAGS) -M *.S *.c > .depend # just here to match coffboot/Makefile vmlinux.coff: diff -u --recursive --new-file v2.3.33/linux/arch/ppc/chrpboot/Makefile linux/arch/ppc/chrpboot/Makefile --- v2.3.33/linux/arch/ppc/chrpboot/Makefile Tue Aug 31 17:29:12 1999 +++ linux/arch/ppc/chrpboot/Makefile Mon Dec 20 14:43:40 1999 @@ -12,11 +12,11 @@ .c.o: $(CC) $(CFLAGS) -DKERNELBASE=$(KERNELBASE) -c -o $*.o $< .S.s: - $(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $< + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -E -o $*.o $< .S.o: - $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $< -CFLAGS = -O -fno-builtin -DSTDC_HEADERS -I$(TOPDIR)/include +CFLAGS = $(CPPFLAGS) -O -fno-builtin -DSTDC_HEADERS LD_ARGS = -Ttext 0x00400000 OBJCOPY = $(CROSS_COMPILE)objcopy @@ -102,5 +102,5 @@ $(TOPDIR)/scripts/mkdep *.[Sch] > .depend dep: - $(CPP) -M *.S *.c > .depend + $(CPP) $(CPPFLAGS) -M *.S *.c > .depend diff -u --recursive --new-file v2.3.33/linux/arch/ppc/coffboot/Makefile linux/arch/ppc/coffboot/Makefile --- v2.3.33/linux/arch/ppc/coffboot/Makefile Tue Dec 7 09:32:41 1999 +++ linux/arch/ppc/coffboot/Makefile Mon Dec 20 14:43:40 1999 @@ -7,7 +7,7 @@ CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld -CFLAGS = -O -fno-builtin -I$(TOPDIR)/include +CFLAGS = $(CPPFLAGS) -O -fno-builtin OBJCOPY = $(CROSS_COMPILE)objcopy OBJCOPY_ARGS = -O aixcoff-rs6000 -R .stab -R .stabstr -R .comment COFF_LD_ARGS = -e _start -T ld.script -Ttext 500000 -Tdata 510000 -Bstatic diff -u --recursive --new-file v2.3.33/linux/arch/ppc/kernel/gemini_setup.c linux/arch/ppc/kernel/gemini_setup.c --- v2.3.33/linux/arch/ppc/kernel/gemini_setup.c Tue Dec 7 09:32:41 1999 +++ linux/arch/ppc/kernel/gemini_setup.c Mon Dec 20 14:27:34 1999 @@ -335,7 +335,14 @@ irq_desc[i].ctl = &open_pic; openpic_init(1); #ifdef __SMP__ - request_irq(OPENPIC_VEC_IPI, openpic_ipi_action, 0, "IPI0", 0); + request_irq(OPENPIC_VEC_IPI, openpic_ipi_action, + 0, "IPI0", 0); + request_irq(OPENPIC_VEC_IPI+1, openpic_ipi_action, + 0, "IPI1 (invalidate TLB)", 0); + request_irq(OPENPIC_VEC_IPI+2, openpic_ipi_action, + 0, "IPI2 (stop CPU)", 0); + request_irq(OPENPIC_VEC_IPI+3, openpic_ipi_action, + 0, "IPI3 (reschedule)", 0); #endif /* __SMP__ */ } diff -u --recursive --new-file v2.3.33/linux/arch/ppc/kernel/open_pic.c linux/arch/ppc/kernel/open_pic.c --- v2.3.33/linux/arch/ppc/kernel/open_pic.c Tue Dec 7 09:32:41 1999 +++ linux/arch/ppc/kernel/open_pic.c Mon Dec 20 14:27:34 1999 @@ -64,14 +64,13 @@ if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ printk("openpic.c:%d: illegal priority %d\n", __LINE__, pri); /* - * Turned this check off since the IPI's are treated as irqs - * but they're above NumSources -- Cort - */ -#define check_arg_irq(irq) -#if 0 - if (irq < 0 || irq >= (NumSources+open_pic.irq_offset)) \ - printk("openpic.c:%d: illegal irq %d\n", __LINE__, irq); -#endif + * I changed this to return to keep us from from trying to use irq #'s + * that we're using for IPI's. + * -- Cort + */ +#define check_arg_irq(irq) \ + /*if (irq < 0 || irq >= (NumSources+open_pic.irq_offset)) \ + printk("openpic.c:%d: illegal irq %d\n", __LINE__, irq);*/ #define check_arg_cpu(cpu) \ if (cpu < 0 || cpu >= NumProcessors) \ printk("openpic.c:%d: illegal cpu %d\n", __LINE__, cpu); @@ -91,8 +90,7 @@ #ifdef __SMP__ void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) { -printk("openpic_ipi_action\n"); - smp_message_recv(); + smp_message_recv(cpl-OPENPIC_VEC_IPI); } #endif /* __SMP__ */ @@ -213,6 +211,9 @@ /* Initialize external interrupts */ if ( ppc_md.progress ) ppc_md.progress("openpic ext",0x3bc); + /* SIOint (8259 cascade) is special */ + openpic_initirq(0, 8, open_pic.irq_offset, 1, 1); + openpic_mapirq(0, 1<<0); for (i = 1; i < NumSources; i++) { /* Enabled, Priority 8 */ openpic_initirq(i, 8, open_pic.irq_offset+i, 0, @@ -226,9 +227,6 @@ openpic_set_spurious(OPENPIC_VEC_SPURIOUS); if ( _machine != _MACH_gemini ) { - /* SIOint (8259 cascade) is special */ - openpic_initirq(0, 8, open_pic.irq_offset, 1, 1); - openpic_mapirq(0, 1<<0); if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT, "82c59 cascade", NULL)) printk("Unable to get OpenPIC IRQ 0 for cascade\n"); @@ -363,6 +361,27 @@ check_arg_ipi(ipi); openpic_clearfield(&OpenPIC->Global.IPI_Vector_Priority(ipi), OPENPIC_MASK); +} + +/* + * Do per-cpu setup for SMP systems. + * + * Get IPI's working and start taking interrupts. + * -- Cort + */ +void do_openpic_setup_cpu(void) +{ + int i; + + for ( i = 0; i < OPENPIC_NUM_IPI ; i++ ) + openpic_enable_IPI(i); +#if 0 + /* let the openpic know we want intrs */ + for ( i = 0; i < NumSources ; i++ ) + openpic_mapirq(i, openpic_read(&OpenPIC->Source[i].Destination) + | (1< #include #include +#include #include #include @@ -269,3 +270,4 @@ EXPORT_SYMBOL(ppc_irq_dispatch_handler); EXPORT_SYMBOL(decrementer_count); EXPORT_SYMBOL(get_wchan); +EXPORT_SYMBOL(console_drivers); diff -u --recursive --new-file v2.3.33/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c --- v2.3.33/linux/arch/ppc/kernel/process.c Tue Dec 7 09:32:42 1999 +++ linux/arch/ppc/kernel/process.c Mon Dec 20 14:27:34 1999 @@ -204,15 +204,6 @@ if ( (prev->thread.regs && (prev->thread.regs->msr & MSR_VEC)) && prev->thread.vrsave ) giveup_altivec(prev); - /* - * The 750 doesn't broadcast invalidates with tlbie's - * so flush every processor switch. - * -- Cort - */ - if ( ((_get_PVR()>>16) == 8) && - (new->last_processor != NO_PROC_ID) && - (new->last_processor != new->processor) && new->mm ) - flush_tlb_mm(new->mm); prev->last_processor = prev->processor; current_set[smp_processor_id()] = new; #endif /* __SMP__ */ diff -u --recursive --new-file v2.3.33/linux/arch/ppc/kernel/smp.c linux/arch/ppc/kernel/smp.c --- v2.3.33/linux/arch/ppc/kernel/smp.c Tue Dec 7 09:32:42 1999 +++ linux/arch/ppc/kernel/smp.c Mon Dec 20 14:27:34 1999 @@ -108,34 +108,8 @@ } } -/* - * Dirty hack to get smp message passing working. - * - * As it is now, if we're sending two message at the same time - * we have race conditions. The PowerSurge doesn't easily - * allow us to send IPI messages so we put the messages in - * smp_message[]. - * - * This is because don't have several IPI's on the PowerSurge even though - * we do on the chrp. It would be nice to use the actual IPI's on the chrp - * rather than this but having two methods of doing IPI isn't a good idea - * right now. - * -- Cort - */ -int smp_message[NR_CPUS]; -void smp_message_recv(void) +void smp_message_recv(int msg) { - int msg = smp_message[smp_processor_id()]; - - if ( _machine == _MACH_Pmac ) - { - /* clear interrupt */ - out_be32(PSURGE_INTR, ~0); - } - - /* make sure msg is for us */ - if ( msg == -1 ) return; - ipi_count++; switch( msg ) @@ -147,15 +121,54 @@ case MSG_RESCHEDULE: current->need_resched = 1; break; - case 0xf0f0: /* syncing time bases - just return */ + case MSG_INVALIDATE_TLB: + _tlbia(); + case 0xf0f0: /* pmac syncing time bases - just return */ break; default: printk("SMP %d: smp_message_recv(): unknown msg %d\n", smp_processor_id(), msg); break; } - /* reset message */ - smp_message[smp_processor_id()] = -1; +} + +/* + * As it is now, if we're sending two message at the same time + * we have race conditions on Pmac. The PowerSurge doesn't easily + * allow us to send IPI messages so we put the messages in + * smp_message[]. + * + * This is because don't have several IPI's on the PowerSurge even though + * we do on the chrp. It would be nice to use actual IPI's such as with openpic + * rather than this. + * -- Cort + */ +int pmac_smp_message[NR_CPUS]; +void pmac_smp_message_recv(void) +{ + int msg = pmac_smp_message[smp_processor_id()]; + + /* clear interrupt */ + out_be32(PSURGE_INTR, ~0); + + /* make sure msg is for us */ + if ( msg == -1 ) return; + + smp_message_recv(msg); + + /* reset message */ + pmac_smp_message[smp_processor_id()] = -1; +} + +/* + * 750's don't broadcast tlb invalidates so + * we have to emulate that behavior. + * -- Cort + */ +void smp_send_tlb_invalidate(int cpu) +{ + if ( (_get_PVR()>>16) == 8 ) + smp_message_pass(MSG_ALL_BUT_SELF, MSG_INVALIDATE_TLB, 0, 0); } void smp_send_reschedule(int cpu) @@ -169,6 +182,8 @@ * as the timer). * -- Cort */ + /* This is only used if `cpu' is running an idle task, + so it will reschedule itself anyway... */ smp_message_pass(cpu, MSG_RESCHEDULE, 0, 0); } @@ -177,38 +192,39 @@ smp_message_pass(MSG_ALL_BUT_SELF, MSG_STOP_CPU, 0, 0); } -spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED; void smp_message_pass(int target, int msg, unsigned long data, int wait) { int i; - if ( !(_machine & (_MACH_Pmac|_MACH_chrp|_MACH_gemini)) ) + + if ( !(_machine & (_MACH_Pmac|_MACH_chrp|_MACH_prep|_MACH_gemini)) ) return; - spin_lock(&mesg_pass_lock); - - /* - * We assume here that the msg is not -1. If it is, - * the recipient won't know the message was destined - * for it. -- Cort - */ - - switch( target ) - { - case MSG_ALL: - smp_message[smp_processor_id()] = msg; - /* fall through */ - case MSG_ALL_BUT_SELF: - for ( i = 0 ; i < smp_num_cpus ; i++ ) - if ( i != smp_processor_id () ) - smp_message[i] = msg; - break; - default: - smp_message[target] = msg; - break; - } - - if ( _machine == _MACH_Pmac ) - { + switch (_machine) { + case _MACH_Pmac: + /* + * IPI's on the Pmac are a hack but without reasonable + * IPI hardware SMP on Pmac is a hack. + * + * We assume here that the msg is not -1. If it is, + * the recipient won't know the message was destined + * for it. -- Cort + */ + for ( i = 0; i <= smp_num_cpus ; i++ ) + pmac_smp_message[i] = -1; + switch( target ) + { + case MSG_ALL: + pmac_smp_message[smp_processor_id()] = msg; + /* fall through */ + case MSG_ALL_BUT_SELF: + for ( i = 0 ; i < smp_num_cpus ; i++ ) + if ( i != smp_processor_id () ) + pmac_smp_message[i] = msg; + break; + default: + pmac_smp_message[target] = msg; + break; + } /* interrupt secondary processor */ out_be32(PSURGE_INTR, ~0); out_be32(PSURGE_INTR, 0); @@ -218,40 +234,28 @@ */ /* interrupt primary */ /**(volatile unsigned long *)(0xf3019000);*/ - } - - if ( _machine == _MACH_chrp ) - { - /* - * There has to be some way of doing this better - - * perhaps a send-to-all or send-to-all-but-self - * in the openpic. This gets us going for now, though. - * -- Cort - */ + break; + case _MACH_chrp: + case _MACH_prep: + case _MACH_gemini: + /* make sure we're sending something that translates to an IPI */ + if ( msg > 0x3 ) + break; switch ( target ) { case MSG_ALL: - openpic_cause_IPI(smp_processor_id(), 0, 0x0 ); - openpic_cause_IPI(smp_processor_id(), 0, 0xffffffff ); + openpic_cause_IPI(smp_processor_id(), msg, 0xffffffff); break; case MSG_ALL_BUT_SELF: - for ( i = 0 ; i < smp_num_cpus ; i++ ) - if ( i != smp_processor_id () ) - { - openpic_cause_IPI(smp_processor_id(), 0, - 0x0 ); - openpic_cause_IPI(smp_processor_id(), 0, + openpic_cause_IPI(smp_processor_id(), msg, 0xffffffff & ~(1 << smp_processor_id())); - } break; default: - openpic_cause_IPI(smp_processor_id(), 0, 0x0 ); - openpic_cause_IPI(target, 0, 1U << target ); + openpic_cause_IPI(smp_processor_id(), msg, 1<mm->mmap->vm_end = init_mm.mmap->vm_end; #endif cpu_callin_map[current->processor] = 1; + /* + * Each processor has to do this and this is the best + * place to stick it for now. + * -- Cort + */ + if ( _machine & (_MACH_gemini|_MACH_chrp|_MACH_prep) ) + do_openpic_setup_cpu(); while(!smp_commenced) barrier(); __sti(); diff -u --recursive --new-file v2.3.33/linux/arch/ppc/lib/Makefile linux/arch/ppc/lib/Makefile --- v2.3.33/linux/arch/ppc/lib/Makefile Wed Dec 23 07:34:11 1998 +++ linux/arch/ppc/lib/Makefile Mon Dec 20 14:43:40 1999 @@ -3,7 +3,7 @@ # .S.o: - $(CC) -D__ASSEMBLY__ -c $< -o $*.o + $(CC) -D__ASSEMBLY__ $(AFLAGS) -c $< -o $*.o O_TARGET = lib.o O_OBJS = checksum.o string.o strcase.o diff -u --recursive --new-file v2.3.33/linux/arch/ppc/mbxboot/Makefile linux/arch/ppc/mbxboot/Makefile --- v2.3.33/linux/arch/ppc/mbxboot/Makefile Sat Oct 9 11:47:50 1999 +++ linux/arch/ppc/mbxboot/Makefile Mon Dec 20 14:43:40 1999 @@ -16,9 +16,9 @@ .c.o: $(CC) $(CFLAGS) -DINITRD_OFFSET=$(IOFF) -DINITRD_SIZE=$(ISZ) -DZIMAGE_OFFSET=$(ZOFF) -DZIMAGE_SIZE=$(ZSZ) -c -o $*.o $< .S.s: - $(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $< + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -E -o $*.o $< .S.o: - $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $< ZOFF = 0 ZSZ = 0 @@ -30,7 +30,7 @@ GZIP_FLAGS = -v9 OBJECTS := head.o misc.o ../coffboot/zlib.o m8xx_tty.o -CFLAGS = -O2 -DSTDC_HEADERS -fno-builtin -I$(TOPDIR)/include -DCONFIG_8xx +CFLAGS = $(CPPFLAGS) -O2 -DSTDC_HEADERS -fno-builtin -DCONFIG_8xx OBJCOPY = $(CROSS_COMPILE)objcopy OBJCOPY_ARGS = -O elf32-powerpc @@ -109,7 +109,7 @@ $(TOPDIR)/scripts/mkdep *.[Sch] > .depend dep: - $(CPP) -M *.S *.c > .depend + $(CPP) $(CPPFLAGS) -M *.S *.c > .depend # just here to match coffboot/Makefile vmlinux.coff: diff -u --recursive --new-file v2.3.33/linux/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c --- v2.3.33/linux/arch/ppc/mm/init.c Tue Dec 7 09:32:42 1999 +++ linux/arch/ppc/mm/init.c Mon Dec 20 14:27:34 1999 @@ -324,23 +324,24 @@ void si_meminfo(struct sysinfo *val) { - int i, c; + int i; i = max_mapnr; - val->totalram = totalram_pages; + val->totalram = 0; + val->sharedram = 0; val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); - val->sharedram = 0; while (i-- > 0) { if (PageReserved(mem_map+i)) continue; - c = atomic_read(&mem_map[i].count); - if (c > 1) - val->sharedram += c - 1; - } - val->totalhigh = 0; - val->freehigh = 0; - val->mem_unit = PAGE_SIZE; + val->totalram++; + if (!atomic_read(&mem_map[i].count)) + continue; + val->sharedram += atomic_read(&mem_map[i].count) - 1; + } + val->totalram <<= PAGE_SHIFT; + val->sharedram <<= PAGE_SHIFT; + return; } void * @@ -498,6 +499,9 @@ { __clear_user(Hash, Hash_size); _tlbia(); +#ifdef __SMP__ + smp_send_tlb_invalidate(0); +#endif } /* @@ -511,6 +515,9 @@ mm->context = NO_CONTEXT; if (mm == current->mm) activate_mm(mm, mm); +#ifdef __SMP__ + smp_send_tlb_invalidate(0); +#endif } void @@ -520,6 +527,9 @@ flush_hash_page(vma->vm_mm->context, vmaddr); else flush_hash_page(0, vmaddr); +#ifdef __SMP__ + smp_send_tlb_invalidate(0); +#endif } @@ -545,6 +555,9 @@ { flush_hash_page(mm->context, start); } +#ifdef __SMP__ + smp_send_tlb_invalidate(0); +#endif } /* @@ -566,6 +579,9 @@ } read_unlock(&tasklist_lock); flush_hash_segments(0x10, 0xffffff); +#ifdef __SMP__ + smp_send_tlb_invalidate(0); +#endif atomic_set(&next_mmu_context, 0); /* make sure current always has a context */ current->mm->context = MUNGE_CONTEXT(atomic_inc_return(&next_mmu_context)); @@ -715,7 +731,7 @@ f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; #ifndef CONFIG_8xx else - /* On the powerpc, denying user access + /* On the powerpc (not 8xx), no user access forces R/W kernel access */ f |= _PAGE_USER; #endif /* CONFIG_8xx */ @@ -874,7 +890,7 @@ setbat(3, 0x90000000, 0x90000000, 0x10000000, IO_PAGE); break; case _MACH_Pmac: -#if 1 +#if 0 { unsigned long base = 0xf3000000; struct device_node *macio = find_devices("mac-io"); @@ -967,7 +983,6 @@ /* remove the bootmem bitmap from the available memory */ mem_pieces_remove(&phys_avail, start, boot_mapsize, 1); - /* add everything in phys_avail into the bootmem map */ for (i = 0; i < phys_avail.n_regions; ++i) free_bootmem(phys_avail.regions[i].address, @@ -1053,9 +1068,9 @@ int codepages = 0; int datapages = 0; int initpages = 0; -#if defined(CONFIG_CHRP) || defined(CONFIG_PMAC) || defined(CONFIG_ALL_PPC) +#if defined(CONFIG_CHRP) || defined(CONFIG_ALL_PPC) extern unsigned int rtas_data, rtas_size; -#endif /* CONFIG_CHRP || CONFIG_PMAC || CONFIG_ALL_PPC */ +#endif /* defined(CONFIG_CHRP) || defined(CONFIG_ALL_PPC) */ max_mapnr = max_low_pfn; high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); num_physpages = max_mapnr; /* RAM is assumed contiguous */ @@ -1071,13 +1086,13 @@ } #endif /* CONFIG_BLK_DEV_INITRD */ -#if defined(CONFIG_CHRP) || defined(CONFIG_PMAC) || defined(CONFIG_ALL_PPC) +#if defined(CONFIG_CHRP) || defined(CONFIG_ALL_PPC) /* mark the RTAS pages as reserved */ if ( rtas_data ) for (addr = rtas_data; addr < PAGE_ALIGN(rtas_data+rtas_size) ; addr += PAGE_SIZE) SetPageReserved(mem_map + MAP_NR(addr)); -#endif /* CONFIG_CHRP || CONFIG_PMAC || CONFIG_ALL_PPC */ +#endif /* defined(CONFIG_CHRP) || defined(CONFIG_ALL_PPC) */ for (addr = PAGE_OFFSET; addr < (unsigned long)end_of_DRAM; addr += PAGE_SIZE) { @@ -1088,12 +1103,12 @@ else if (addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end) initpages++; - else + else if (addr < (ulong) klimit) datapages++; } printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08x,%08lx]\n", - (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), + (unsigned long) nr_free_pages << (PAGE_SHIFT-10), codepages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10), initpages << (PAGE_SHIFT-10), diff -u --recursive --new-file v2.3.33/linux/arch/sparc/ap1000/Makefile linux/arch/sparc/ap1000/Makefile --- v2.3.33/linux/arch/sparc/ap1000/Makefile Sun Jan 26 02:07:06 1997 +++ linux/arch/sparc/ap1000/Makefile Mon Dec 20 14:43:40 1999 @@ -6,9 +6,9 @@ # .S.s: - $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s + $(CPP) -D__ASSEMBLY__ $(AFLAGS) -ansi $< -o $*.s .S.o: - $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c $< -o $*.o all: ap1000lib.o diff -u --recursive --new-file v2.3.33/linux/arch/sparc/kernel/Makefile linux/arch/sparc/kernel/Makefile --- v2.3.33/linux/arch/sparc/kernel/Makefile Tue Aug 31 17:29:13 1999 +++ linux/arch/sparc/kernel/Makefile Mon Dec 20 14:43:40 1999 @@ -55,11 +55,13 @@ @echo "#ifndef CONFIG_SMP" >> asm_offsets.h @echo "" >> asm_offsets.h @echo "#include " > tmp.c + @echo "#undef __SMP__" >> tmp.c @echo "#undef CONFIG_SMP" >> tmp.c @echo "#include " >> tmp.c - $(CC) -E tmp.c -o tmp.i + $(CC) $(CPPFLAGS) -E tmp.c -o tmp.i @echo "/* Automatically generated. Do not edit. */" > check_asm.c @echo "#include " >> check_asm.c + @echo "#undef __SMP__" >> check_asm.c @echo "#undef CONFIG_SMP" >> check_asm.c @echo "#include " >> check_asm.c @echo 'struct task_struct _task;' >> check_asm.c @@ -71,7 +73,7 @@ $(SH) ./check_asm.sh thread tmp.i check_asm.c @echo 'return 0; }' >> check_asm.c @rm -f tmp.[ci] - $(CC) -o check_asm check_asm.c + $(CC) $(CFLAGS) -o check_asm check_asm.c ./check_asm >> asm_offsets.h @rm -f check_asm check_asm.c @echo "" >> asm_offsets.h @@ -81,7 +83,7 @@ @echo "#undef CONFIG_SMP" >> tmp.c @echo "#define CONFIG_SMP 1" >> tmp.c @echo "#include " >> tmp.c - $(CC) -D__SMP__ -E tmp.c -o tmp.i + $(CC) $(CPPFLAGS) -D__SMP__ -E tmp.c -o tmp.i @echo "/* Automatically generated. Do not edit. */" > check_asm.c @echo "#include " >> check_asm.c @echo "#undef CONFIG_SMP" >> check_asm.c @@ -96,7 +98,7 @@ $(SH) ./check_asm.sh thread tmp.i check_asm.c @echo 'return 0; }' >> check_asm.c @rm -f tmp.[ci] - $(CC) -D__SMP__ -o check_asm check_asm.c + $(CC) $(CFLAGS) -D__SMP__ -o check_asm check_asm.c ./check_asm >> asm_offsets.h @rm -f check_asm check_asm.c @echo "" >> asm_offsets.h diff -u --recursive --new-file v2.3.33/linux/arch/sparc/lib/Makefile linux/arch/sparc/lib/Makefile --- v2.3.33/linux/arch/sparc/lib/Makefile Sun Mar 21 07:23:38 1999 +++ linux/arch/sparc/lib/Makefile Mon Dec 20 14:43:40 1999 @@ -16,34 +16,34 @@ sync checksum.o: checksum.S - $(CC) -D__ASSEMBLY__ -ansi -c -o checksum.o checksum.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o checksum.o checksum.S memcpy.o: memcpy.S - $(CC) -D__ASSEMBLY__ -ansi -c -o memcpy.o memcpy.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o memcpy.o memcpy.S memcmp.o: memcmp.S - $(CC) -D__ASSEMBLY__ -ansi -c -o memcmp.o memcmp.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o memcmp.o memcmp.S memscan.o: memscan.S - $(CC) -D__ASSEMBLY__ -ansi -c -o memscan.o memscan.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o memscan.o memscan.S strncmp.o: strncmp.S - $(CC) -D__ASSEMBLY__ -ansi -c -o strncmp.o strncmp.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o strncmp.o strncmp.S strncpy_from_user.o: strncpy_from_user.S - $(CC) -D__ASSEMBLY__ -ansi -c -o strncpy_from_user.o strncpy_from_user.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o strncpy_from_user.o strncpy_from_user.S strlen_user.o: strlen_user.S - $(CC) -D__ASSEMBLY__ -ansi -c -o strlen_user.o strlen_user.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o strlen_user.o strlen_user.S copy_user.o: copy_user.S - $(CC) -D__ASSEMBLY__ -ansi -c -o copy_user.o copy_user.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o copy_user.o copy_user.S blockops.o: blockops.S - $(CC) -D__ASSEMBLY__ -ansi -c -o blockops.o blockops.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o blockops.o blockops.S memset.o: memset.S - $(CC) -D__ASSEMBLY__ -ansi -c -o memset.o memset.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o memset.o memset.S locks.o: locks.S $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o locks.o locks.S @@ -60,37 +60,37 @@ endif strlen.o: strlen.S - $(CC) -D__ASSEMBLY__ -ansi -c -o strlen.o strlen.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o strlen.o strlen.S divdi3.o: divdi3.S - $(CC) -D__ASSEMBLY__ -ansi -c -o divdi3.o divdi3.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o divdi3.o divdi3.S udivdi3.o: udivdi3.S - $(CC) -D__ASSEMBLY__ -ansi -c -o udivdi3.o udivdi3.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o udivdi3.o udivdi3.S mul.o: mul.S - $(CC) -D__ASSEMBLY__ -c -o mul.o mul.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -c -o mul.o mul.S rem.o: rem.S - $(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o rem.o rem.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -DST_DIV0=0x2 -c -o rem.o rem.S sdiv.o: sdiv.S - $(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o sdiv.o sdiv.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -DST_DIV0=0x2 -c -o sdiv.o sdiv.S udiv.o: udiv.S - $(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o udiv.o udiv.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -DST_DIV0=0x2 -c -o udiv.o udiv.S umul.o: umul.S - $(CC) -D__ASSEMBLY__ -c -o umul.o umul.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -c -o umul.o umul.S urem.o: urem.S - $(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o urem.o urem.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -DST_DIV0=0x2 -c -o urem.o urem.S ashrdi3.o: ashrdi3.S - $(CC) -D__ASSEMBLY__ -c -o ashrdi3.o ashrdi3.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -c -o ashrdi3.o ashrdi3.S lshrdi3.o: lshrdi3.S - $(CC) -D__ASSEMBLY__ -c -o lshrdi3.o lshrdi3.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -c -o lshrdi3.o lshrdi3.S dep: diff -u --recursive --new-file v2.3.33/linux/arch/sparc/math-emu/Makefile linux/arch/sparc/math-emu/Makefile --- v2.3.33/linux/arch/sparc/math-emu/Makefile Tue Aug 31 17:29:13 1999 +++ linux/arch/sparc/math-emu/Makefile Mon Dec 20 14:43:40 1999 @@ -11,10 +11,10 @@ O_OBJS := math.o ashldi3.o .S.s: - $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s + $(CPP) -D__ASSEMBLY__ $(AFLAGS) -ansi $< -o $*.s .S.o: - $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c $< -o $*.o CFLAGS += -I. -I$(TOPDIR)/include/math-emu -w diff -u --recursive --new-file v2.3.33/linux/arch/sparc/prom/Makefile linux/arch/sparc/prom/Makefile --- v2.3.33/linux/arch/sparc/prom/Makefile Tue Apr 14 17:44:20 1998 +++ linux/arch/sparc/prom/Makefile Mon Dec 20 14:43:40 1999 @@ -22,6 +22,6 @@ sync dep: - $(CPP) -M *.c > .depend + $(CPP) $(CPPFLAGS) -M *.c > .depend include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.3.33/linux/arch/sparc64/Makefile linux/arch/sparc64/Makefile --- v2.3.33/linux/arch/sparc64/Makefile Tue Aug 31 17:29:13 1999 +++ linux/arch/sparc64/Makefile Mon Dec 20 14:43:40 1999 @@ -12,16 +12,10 @@ # line... SHELL =/bin/bash -CC := sparc64-linux-gcc -D__KERNEL__ -I$(TOPDIR)/include +CC := sparc64-linux-gcc -CC_HAS_ARGS := $(shell if echo "$(CC)" | grep '\(__KERNEL__\| \)' > /dev/null; then echo y; else echo n; fi) IS_EGCS := $(shell if $(CC) -m64 -mcmodel=medlow -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo y; else echo n; fi; ) NEW_GAS := $(shell if $(LD) --version 2>&1 | grep 'elf64_sparc' > /dev/null; then echo y; else echo n; fi) - -ifneq ($(CC_HAS_ARGS),y) -MAKEOVERRIDES := $(shell echo "$(MAKEOVERRIDES)" | sed 's CC=$(CC) CC=$(CC)\\\ -D__KERNEL__\\\ -I$(TOPDIR)/include ') -override CC := $(CC) -D__KERNEL__ -I$(TOPDIR)/include -endif ifneq ($(NEW_GAS),y) AS = sparc64-linux-as diff -u --recursive --new-file v2.3.33/linux/arch/sparc64/kernel/Makefile linux/arch/sparc64/kernel/Makefile --- v2.3.33/linux/arch/sparc64/kernel/Makefile Tue Aug 31 17:29:13 1999 +++ linux/arch/sparc64/kernel/Makefile Mon Dec 20 14:43:40 1999 @@ -74,11 +74,13 @@ @echo -e "# error Please issue 'make check_asm' in linux top-level directory first\n# endif\n#endif\n" >> asm_offsets.h @echo -e "#ifndef CONFIG_SMP\n" >> asm_offsets.h @echo "#include " > tmp.c + @echo "#undef __SMP__" >> tmp.c @echo "#undef CONFIG_SMP" >> tmp.c @echo "#include " >> tmp.c - $(CC) -E tmp.c -o tmp.i + $(CC) $(CPPFLAGS) -E tmp.c -o tmp.i @echo "/* Automatically generated. Do not edit. */" > check_asm.c @echo "#include " >> check_asm.c + @echo "#undef __SMP__" >> check_asm.c @echo "#undef CONFIG_SMP" >> check_asm.c @echo "#include " >> check_asm.c @echo 'struct task_struct _task;' >> check_asm.c @@ -92,7 +94,7 @@ @rm -f tmp.[ci] #$(CC) -o check_asm check_asm.c # Until we can do this natively, a hack has to take place - $(CC) $(CMODEL_CFLAG) -ffixed-g4 -S -o check_asm.s check_asm.c + $(CC) $(CPPFLAGS) $(CMODEL_CFLAG) -ffixed-g4 -S -o check_asm.s check_asm.c $(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s @rm -f check_asm.s # @@ -104,7 +106,7 @@ @echo "#undef CONFIG_SMP" >> tmp.c @echo "#define CONFIG_SMP 1" >> tmp.c @echo "#include " >> tmp.c - $(CC) -D__SMP__ -E tmp.c -o tmp.i + $(CC) $(CPPFLAGS) -D__SMP__ -E tmp.c -o tmp.i @echo "/* Automatically generated. Do not edit. */" > check_asm.c @echo "#include " >> check_asm.c @echo "#undef CONFIG_SMP" >> check_asm.c @@ -121,7 +123,7 @@ @rm -f tmp.[ci] #$(CC) -D__SMP__ -o check_asm check_asm.c # Until we can do this natively, a hack has to take place - $(CC) -D__SMP__ $(CMODEL_CFLAG) -ffixed-g4 -S -o check_asm.s check_asm.c + $(CC) $(CPPFLAGS) -D__SMP__ $(CMODEL_CFLAG) -ffixed-g4 -S -o check_asm.s check_asm.c $(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s @rm -f check_asm.s # @@ -129,7 +131,7 @@ @rm -f check_asm check_asm.c @echo -e "\n#else /* SPIN_LOCK_DEBUG */\n" >> asm_offsets.h @echo "#include " > tmp.c - $(CC) -D__SMP__ -DSPIN_LOCK_DEBUG -E tmp.c -o tmp.i + $(CC) $(CPPFLAGS) -D__SMP__ -DSPIN_LOCK_DEBUG -E tmp.c -o tmp.i @echo "/* Automatically generated. Do not edit. */" > check_asm.c @echo "#include " >> check_asm.c @echo "#undef CONFIG_SMP" >> check_asm.c @@ -146,7 +148,7 @@ @rm -f tmp.[ci] #$(CC) -D__SMP__ -DSPIN_LOCK_DEBUG -o check_asm check_asm.c # Until we can do this natively, a hack has to take place - $(CC) -D__SMP__ -DSPIN_LOCK_DEBUG $(CMODEL_CFLAG) -ffixed-g4 -S -o check_asm.s check_asm.c + $(CC) $(CPPFLAGS) -D__SMP__ -DSPIN_LOCK_DEBUG $(CMODEL_CFLAG) -ffixed-g4 -S -o check_asm.s check_asm.c $(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s @rm -f check_asm.s # diff -u --recursive --new-file v2.3.33/linux/arch/sparc64/prom/Makefile linux/arch/sparc64/prom/Makefile --- v2.3.33/linux/arch/sparc64/prom/Makefile Mon Mar 17 14:54:23 1997 +++ linux/arch/sparc64/prom/Makefile Mon Dec 20 14:43:40 1999 @@ -18,6 +18,6 @@ sync dep: - $(CPP) -M *.c > .depend + $(CPP) $(CPPFLAGS) -M *.c > .depend include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.3.33/linux/drivers/Makefile linux/drivers/Makefile --- v2.3.33/linux/drivers/Makefile Sat Oct 9 11:47:50 1999 +++ linux/drivers/Makefile Thu Dec 16 13:59:37 1999 @@ -11,7 +11,7 @@ MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) pci sgi scsi sbus cdrom isdn pnp i2o \ macintosh video dio zorro fc4 usb \ - nubus tc ap1000 atm pcmcia + nubus tc ap1000 atm pcmcia i2c ifdef CONFIG_DIO SUB_DIRS += dio @@ -139,6 +139,15 @@ ifeq ($(CONFIG_HAMRADIO),y) SUB_DIRS += net/hamradio MOD_SUB_DIRS += net/hamradio +endif + +ifeq ($(CONFIG_I2C),y) +SUB_DIRS += i2c +MOD_SUB_DIRS += i2c +else + ifeq ($(CONFIG_I2C),m) + MOD_SUB_DIRS += i2c + endif endif include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.3.33/linux/drivers/block/Config.in linux/drivers/block/Config.in --- v2.3.33/linux/drivers/block/Config.in Wed Dec 15 10:43:16 1999 +++ linux/drivers/block/Config.in Mon Dec 20 15:33:46 1999 @@ -70,11 +70,11 @@ if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" -a "$CONFIG_BLK_DEV_HPT366" = "y" ]; then bool ' HPT366 Fast Interrupt support (EXPERIMENTAL)' HPT366_FAST_IRQ_PREDICTION fi - fi - if [ "$CONFIG_X86" = "y" ]; then - bool ' Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX - if [ "$CONFIG_BLK_DEV_PIIX" = "y" ]; then - bool ' PIIXn Tuning support' CONFIG_BLK_DEV_PIIX_TUNING + if [ "$CONFIG_X86" = "y" ]; then + bool ' Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX + if [ "$CONFIG_BLK_DEV_PIIX" = "y" -a "$CONFIG_IDEDMA_PCI_AUTO" = "y" ]; then + bool ' PIIXn Tuning support' CONFIG_BLK_DEV_PIIX_TUNING + fi fi fi if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then @@ -98,7 +98,7 @@ if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 if [ "$CONFIG_X86" = "y" ]; then - bool ' VIA82CXXX chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82CXXX + bool ' VIA82CXXX chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82CXXX fi fi fi diff -u --recursive --new-file v2.3.33/linux/drivers/block/README.fd linux/drivers/block/README.fd --- v2.3.33/linux/drivers/block/README.fd Mon Oct 12 14:13:54 1998 +++ linux/drivers/block/README.fd Thu Dec 16 13:57:04 1999 @@ -4,7 +4,7 @@ ========= A FAQ list may be found in the fdutils package (see below), and also -at http://poboxes.com/Alain.Knaff/floppy/FAQ.html +at http://fdutils.linux.lu/FAQ.html LILO configuration options (Thinkpad users, read this) @@ -13,15 +13,15 @@ The floppy driver is configured using the 'floppy=' option in lilo. This option can be typed at the boot prompt, or entered in the lilo configuration file. - Example: If your kernel is called linux-pre2.0.9, type the following line + Example: If your kernel is called linux-2.2.13, type the following line at the lilo boot prompt (if you have a thinkpad): - linux-pre2.0.9 floppy=thinkpad + linux-2.2.13 floppy=thinkpad You may also enter the following line in /etc/lilo.conf, in the description -of linux-pre2.0.9: +of linux-2.2.13: append = "floppy=thinkpad" Several floppy related options may be given, example: - linux-pre2.0.9 floppy=daring floppy=two_fdc + linux-2.2.13 floppy=daring floppy=two_fdc append = "floppy=daring floppy=two_fdc" If you give options both in the lilo config file and on the boot @@ -30,28 +30,19 @@ restore the default behavior. If you use the floppy driver as a module, use the following syntax: - insmod floppy 'floppy=""' + insmod floppy Example: - insmod floppy 'floppy="daring two_fdc"' - - Note that in this case 'floppy=' should only be typed out once, and -not once for each option. You need at least modules-1.3.57 for this -method. However, the older environment variable based syntax is still -available: -(sh syntax): floppy="daring two_fdc" insmod floppy -(csh syntax): setenv floppy "daring two_fdc" ; insmod floppy + insmod floppy daring two_fdc Some versions of insmod are buggy in one way or another. If you have any problems (options not being passed correctly, segfaults during -insmod), first check whether there is a more recent version. If there -isn't, use the old method using environment variables. +insmod), first check whether there is a more recent version. The floppy related options include: floppy=asus_pci - Sets the bit mask to allow only units 0 and 1. Obsolete, as - this is the default setting anyways + Sets the bit mask to allow only units 0 and 1. (default) floppy=daring Tells the floppy driver that you have a well behaved floppy controller. @@ -63,14 +54,15 @@ with caution. floppy=one_fdc - Tells the floppy driver that you have only floppy controller (default) + Tells the floppy driver that you have only one floppy controller. + (default) floppy=two_fdc floppy=
,two_fdc - Tells the floppy driver that you have two floppy controllers. The - second floppy controller is assumed to be at
. This - option is not needed if the second controller is at address - 0x370, and if you use the 'cmos' option + Tells the floppy driver that you have two floppy controllers. + The second floppy controller is assumed to be at
. + This option is not needed if the second controller is at address + 0x370, and if you use the 'cmos' option. floppy=thinkpad Tells the floppy driver that you have a Thinkpad. Thinkpads use an @@ -89,7 +81,7 @@ and is thus harder to find, whereas non-dma buffers may be allocated in virtual memory. However, I advise against this if you have an FDC without a FIFO (8272A or 82072). 82072A and - later are OK). You also need at least a 486 to use nodma. + later are OK. You also need at least a 486 to use nodma. If you use nodma mode, I suggest you also set the FIFO threshold to 10 or lower, in order to limit the number of data transfer interrupts. @@ -99,8 +91,8 @@ If you want to avoid this, explicitely ask for 'yesdma'. floppy=yesdma - Tells the floppy driver that a workable DMA channel is available - (the default). + Tells the floppy driver that a workable DMA channel is available. + (default) floppy=nofifo Disables the FIFO entirely. This is needed if you get "Bus @@ -108,7 +100,7 @@ from other devices) while accessing the floppy. floppy=fifo - Enables the FIFO (default) + Enables the FIFO. (default) floppy=,fifo_depth Sets the FIFO threshold. This is mostly relevant in DMA @@ -147,18 +139,18 @@ (Note: there are two valid types for ED drives. This is because 5 was initially chosen to represent floppy *tapes*, and 6 for ED drives. AMI ignored this, and used 5 for ED drives. That's why the floppy - driver handles both) + driver handles both.) floppy=unexpected_interrupts - Print a warning message when an unexpected interrupt is received - (default behavior) + Print a warning message when an unexpected interrupt is received. + (default) floppy=no_unexpected_interrupts floppy=L40SX Don't print a message when an unexpected interrupt is received. This is needed on IBM L40SX laptops in certain video modes. (There seems - to be an interaction between video and floppy. The unexpected interrupts - only affect performance, and can safely be ignored.) + to be an interaction between video and floppy. The unexpected + interrupts affect only performance, and can be safely ignored.) floppy=broken_dcl Don't use the disk change line, but assume that the disk was @@ -168,53 +160,52 @@ floppy operation less efficient due to unneeded cache flushings, and slightly more unreliable. Please verify your cable, connection and jumper settings if you have any DCL - problems. However, some older drives, and also some Laptops + problems. However, some older drives, and also some laptops are known not to have a DCL. floppy=debug - Print debugging messages + Print debugging messages. floppy=messages Print informational messages for some operations (disk change notifications, warnings about over and underruns, and about - autodetection) + autodetection). floppy=silent_dcl_clear Uses a less noisy way to clear the disk change line (which - doesn't involve seeks). Implied by daring. + doesn't involve seeks). Implied by 'daring' option. floppy=,irq - Sets the floppy IRQ to instead of 6 + Sets the floppy IRQ to instead of 6. floppy=,dma - Sets the floppy DMA channel to instead of 2 + Sets the floppy DMA channel to instead of 2. floppy=slow Use PS/2 stepping rate: " PS/2 floppies have much slower step rates than regular floppies. It's been recommended that take about 1/4 of the default speed in some more extreme cases." - + Supporting utilities and additional documentation: ================================================== - Additional parameters of the floppy driver can be configured at run -time. Utilities which do this can be found in the fdutils -package. This package also contains a new version of mtools which -allows to access high capacity disks (up to 1992K on a high density 3 -1/2 disk!). It also contains additional documentation about the floppy -driver. It can be found at: - linux.wauug.org:/pub/knaff/fdutils/fdutils-4.3.src.tar.gz - sunsite.unc.edu:/pub/Linux/system/Misc/fdutils-4.3.src.tar.gz - tsx-11.mit.edu:/pub/linux/sources/sbin/fdutils-4.3.src.tar.gz - - Alpha patches to these utilities are at: - http://www.club.innet.lu/~year3160/fdutils/ALPHA - All patches contained in this directory are directly against the base -version, i.e. DON'T APPLY THEM ON TOP OF EACH OTHER. Only apply the -most recent one. + Additional parameters of the floppy driver can be configured at +runtime. Utilities which do this can be found in the fdutils package. +This package also contains a new version of mtools which allows to +access high capacity disks (up to 1992K on a high density 3 1/2 disk!). +It also contains additional documentation about the floppy driver. + +The latest version can be found at fdutils homepage: + http://fdutils.linux.lu + +The fdutils-5.3 release can be found at: + http://fdutils.linux.lu/fdutils-5.3.src.tar.gz + http://www.tux.org/pub/knaff/fdutils/fdutils-5.3.src.tar.gz + ftp://tsx-11.mit.edu/pub/linux/sources/sbin/fdutils-5.3.src.tar.gz + ftp://metalab.unc.edu/pub/Linux/utils/disk-management/fdutils-5.3.src.tar.gz Reporting problems about the floppy driver ========================================== @@ -228,4 +219,4 @@ Be sure to read the FAQ before mailing/posting any bug reports! - Alain + Alain diff -u --recursive --new-file v2.3.33/linux/drivers/block/aec6210.c linux/drivers/block/aec6210.c --- v2.3.33/linux/drivers/block/aec6210.c Tue Nov 23 22:42:20 1999 +++ linux/drivers/block/aec6210.c Tue Dec 14 23:03:49 1999 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/aec6210.c Version 0.03 Nov. 12, 1999 + * linux/drivers/block/aec6210.c Version 0.04 Dec. 13, 1999 * * Copyright (C) 1998-99 Andre Hedrick (andre@suse.com) * May be copied or modified under the terms of the GNU General Public License diff -u --recursive --new-file v2.3.33/linux/drivers/block/alim15x3.c linux/drivers/block/alim15x3.c --- v2.3.33/linux/drivers/block/alim15x3.c Tue Nov 23 22:42:20 1999 +++ linux/drivers/block/alim15x3.c Tue Dec 14 23:03:49 1999 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/alim15x3.c Version 0.06 Sept. 3, 1999 + * linux/drivers/block/alim15x3.c Version 0.07 Dec. 13, 1999 * * Copyright (C) 1998-99 Michel Aubry, Maintainer * Copyright (C) 1998-99 Andrzej Krzysztofowicz, Maintainer @@ -251,6 +251,8 @@ unsigned long flags; int bus_speed = ide_system_bus_speed(); int port = hwif->index ? 0x5c : 0x58; + int portFIFO = hwif->channel ? 0x55 : 0x54; + byte cd_dma_fifo = 0; pio = ide_get_best_pio_mode(drive, pio, 5, &d); s_time = ide_pio_timings[pio].setup_time; @@ -270,10 +272,29 @@ r_clc = 1; } else { if (r_clc >= 16) - r_clc = 0; + r_clc = 0; } save_flags(flags); cli(); + + /* + * PIO mode => ATA FIFO on, ATAPI FIFO off + */ + pci_read_config_byte(dev, portFIFO, &cd_dma_fifo); + if (drive->media==ide_disk) { + if (hwif->index) { + pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0x0F) | 0x50); + } else { + pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0xF0) | 0x05); + } + } else { + if (hwif->index) { + pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0x0F); + } else { + pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0xF0); + } + } + pci_write_config_byte(dev, port, s_clc); pci_write_config_byte(dev, port+drive->select.b.unit+2, (a_clc << 4) | r_clc); restore_flags(flags); @@ -374,6 +395,7 @@ { byte unit = (drive->select.b.unit & 0x01); byte bits = (drive->id->dma_mword | drive->id->dma_1word) & 0x07; + byte ultra = (unit) ? 0x7f : 0xf7; byte tmpbyte; ide_hwif_t *hwif = HWIF(drive); unsigned long flags; @@ -385,11 +407,15 @@ * clear "ultra enable" bit */ pci_read_config_byte(hwif->pci_dev, m5229_udma_setting_index, &tmpbyte); +#if 0 if (unit) { tmpbyte &= 0x7f; } else { tmpbyte &= 0xf7; } +#else + tmpbyte &= ultra; +#endif save_flags(flags); cli(); pci_write_config_byte(hwif->pci_dev, m5229_udma_setting_index, tmpbyte); @@ -586,6 +612,7 @@ struct pci_dev *isa; unsigned long fixdma_base = dev->resource[4].start; byte tmpbyte; + unsigned long flags; pci_read_config_byte(dev, PCI_REVISION_ID, &m5229_revision); @@ -717,17 +744,123 @@ m5229_revision = 0xC2; } } + + /* + * CD_ROM DMA on (m5229, 0x53, bit0) + * Enable this bit even if we want to use PIO + * PIO FIFO off (m5229, 0x53, bit1) + * The hardware will use 0x54h and 0x55h to control PIO FIFO + */ + pci_read_config_byte(dev, 0x53, &tmpbyte); + tmpbyte = (tmpbyte & (~0x02)) | 0x01; + save_flags(flags); + cli(); + pci_write_config_byte(dev, 0x53, tmpbyte); + restore_flags(flags); return 0; } unsigned int __init ata66_ali15x3 (ide_hwif_t *hwif) { +#if 0 /* - * FIXME !!!! - * {0x4a,0x01,0x01}, {0x4a,0x02,0x02} + * FIXME !!! This detection needs to be in "ata66_ali15x3()" + * below as a standard detection return. */ + + if (m5229_revision >= 0xC2) { + unsigned long flags; + /* + * 1543C-B?, 1535, 1535D, 1553 + * Note 1: not all "motherboard" support this detection + * Note 2: if no udma 66 device, the detection may "error". + * but in this case, we will not set the device to + * ultra 66, the detection result is not important + */ + save_flags(flags); + cli(); + + /* + * enable "Cable Detection", m5229, 0x4b, bit3 + */ + pci_read_config_byte(dev, 0x4b, &tmpbyte); + pci_write_config_byte(dev, 0x4b, tmpbyte | 0x08); + + /* + * set south-bridge's enable bit, m1533, 0x79 + */ + pci_read_config_byte(isa_dev, 0x79, &tmpbyte); + if (m5229_revision == 0xC2) { + /* + * 1543C-B0 (m1533, 0x79, bit 2) + */ + pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x04); + } else if (m5229_revision == 0xC3) { + /* + * 1553/1535 (m1533, 0x79, bit 1) + */ + pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x02); + } + restore_flags(flags); + /* + * Ultra66 cable detection (from Host View) + * m5229, 0x4a, bit0: primary, bit1: secondary 80 pin + */ + pci_read_config_byte(dev, 0x4a, &tmpbyte); + /* + * 0x4a, bit0 is 0 => primary channel + * has 80-pin (from host view) + */ + if (!(tmpbyte & 0x01)) + cable_80_pin[0] = 1; + /* + * 0x4a, bit1 is 0 => secondary channel + * has 80-pin (from host view) + */ + if (!(tmpbyte & 0x02)) + cable_80_pin[1] = 1; + } else { + unsigned long flags; + /* + * revision 0x20 (1543-E, 1543-F) + * revision 0xC0, 0xC1 (1543C-C, 1543C-D, 1543C-E) + * clear CD-ROM DMA write bit, m5229, 0x4b, bit 7 + */ + pci_read_config_byte(dev, 0x4b, &tmpbyte); + save_flags(flags); + cli(); + /* + * clear bit 7 + */ + pci_write_config_byte(dev, 0x4b, tmpbyte & 0x7F); + restore_flags(flags); + + /* + * check m1533, 0x5e, bit 1~4 == 1001 => & 00011110 = 00010010 + */ + pci_read_config_byte(isa_dev, 0x5e, &tmpbyte); + chip_is_1543c_e = ((tmpbyte & 0x1e) == 0x12) ? 1: 0; + } + + byte ata66mask = hwif->channel ? 0x02 : 0x01; + unsigned int ata66 = 0; + /* + * Ultra66 cable detection (from Host View) + * m5229, 0x4a, bit0: primary, bit1: secondary 80 pin + * + * 0x4a, bit0 is 0 => primary channel + * has 80-pin (from host view) + * + * 0x4a, bit1 is 0 => secondary channel + * has 80-pin (from host view) + */ + pci_read_config_byte(hwif->pci_dev, 0x4a, &tmpbyte); + ata66 = (!(tmpbyte & ata66mask)) ? 0 : 1; + return(ata66); +#else return 0; +#endif } void __init ide_init_ali15x3 (ide_hwif_t *hwif) @@ -773,12 +906,14 @@ */ hwif->dmaproc = &ali15x3_dmaproc; hwif->autodma = 1; + hwif->drives[0].autotune = 0; + hwif->drives[1].autotune = 0; } else { hwif->autodma = 0; hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; } - + #if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) ali_proc = 1; bmide_dev = hwif->pci_dev; diff -u --recursive --new-file v2.3.33/linux/drivers/block/cy82c693.c linux/drivers/block/cy82c693.c --- v2.3.33/linux/drivers/block/cy82c693.c Fri Oct 22 13:21:47 1999 +++ linux/drivers/block/cy82c693.c Tue Dec 14 23:03:49 1999 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/cy82c693.c Version 0.34 Sept 3, 1999 + * linux/drivers/block/cy82c693.c Version 0.34 Dec. 13, 1999 * * Copyright (C) 1998-99 Andreas S. Krebs (akrebs@altavista.net), Maintainer * Copyright (C) 1998-99 Andre Hedrick, Integrater @@ -426,8 +426,14 @@ void __init ide_init_cy82c693(ide_hwif_t *hwif) { hwif->chipset = ide_cy82c693; - if (hwif->dma_base) - hwif->dmaproc = &cy82c693_dmaproc; hwif->tuneproc = &cy82c693_tune_drive; + if (hwif->dma_base) { + hwif->dmaproc = &cy82c693_dmaproc; + hwif->drives[0].autotune = 0; + hwif->drives[1].autotune = 0; + } else { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + } } diff -u --recursive --new-file v2.3.33/linux/drivers/block/hpt34x.c linux/drivers/block/hpt34x.c --- v2.3.33/linux/drivers/block/hpt34x.c Sun Nov 7 16:37:34 1999 +++ linux/drivers/block/hpt34x.c Tue Dec 14 23:03:49 1999 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/hpt34x.c Version 0.27 Sept 03, 1999 + * linux/drivers/block/hpt34x.c Version 0.28 Dec. 13, 1999 * * Copyright (C) 1998-99 Andre Hedrick (andre@suse.com) * May be copied or modified under the terms of the GNU General Public License @@ -377,6 +377,8 @@ #endif #endif /* CONFIG_BLK_DEV_HPT34X_DMA */ hwif->dmaproc = &hpt34x_dmaproc; + hwif->drives[0].autotune = 0; + hwif->drives[1].autotune = 0; } else { hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; diff -u --recursive --new-file v2.3.33/linux/drivers/block/hpt366.c linux/drivers/block/hpt366.c --- v2.3.33/linux/drivers/block/hpt366.c Thu Nov 18 20:25:37 1999 +++ linux/drivers/block/hpt366.c Tue Dec 14 23:03:49 1999 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/hpt366.c Version 0.13 Sept. 3, 1999 + * linux/drivers/block/hpt366.c Version 0.14 Dec. 13, 1999 * * Copyright (C) 1999 Andre Hedrick * May be copied or modified under the terms of the GNU General Public License @@ -507,6 +507,10 @@ hwif->tuneproc = &hpt366_tune_drive; if (hwif->dma_base) { hwif->dmaproc = &hpt366_dmaproc; +#if 0 + hwif->drives[0].autotune = 0; + hwif->drives[1].autotune = 0; +#endif } else { hwif->autodma = 0; hwif->drives[0].autotune = 1; diff -u --recursive --new-file v2.3.33/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c --- v2.3.33/linux/drivers/block/ide-cd.c Tue Dec 14 01:27:23 1999 +++ linux/drivers/block/ide-cd.c Tue Dec 14 23:03:50 1999 @@ -2613,6 +2613,10 @@ continue; } } + if (drive->scsi) { + printk("ide-cd: passing drive %s to ide-scsi emulation.\n", drive->name); + continue; + } info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL); if (info == NULL) { printk ("%s: Can't allocate a cdrom structure\n", drive->name); diff -u --recursive --new-file v2.3.33/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c --- v2.3.33/linux/drivers/block/ide-disk.c Tue Dec 14 01:27:23 1999 +++ linux/drivers/block/ide-disk.c Tue Dec 14 23:03:50 1999 @@ -241,9 +241,10 @@ #ifdef CONFIG_BLK_DEV_PDC4030 rq->sector += nsect; #endif - if ((rq->nr_sectors -= nsect) <= 0) - { - spin_unlock_irqrestore(&io_request_lock, flags); + if ((rq->nr_sectors -= nsect) <= 0) { + printk("%s: multwrite: count=%d, current=%ld\n", + drive->name, nsect, rq->nr_sectors); + spin_unlock_irqrestore(&io_request_lock, flags); break; } if ((rq->current_nr_sectors -= nsect) == 0) { diff -u --recursive --new-file v2.3.33/linux/drivers/block/ide-features.c linux/drivers/block/ide-features.c --- v2.3.33/linux/drivers/block/ide-features.c Tue Dec 7 09:32:43 1999 +++ linux/drivers/block/ide-features.c Tue Dec 14 23:03:50 1999 @@ -42,6 +42,8 @@ char *ide_xfer_verbose (byte xfer_rate) { switch(xfer_rate) { + case XFER_UDMA_6: return("UDMA 6"); + case XFER_UDMA_5: return("UDMA 5"); case XFER_UDMA_4: return("UDMA 4"); case XFER_UDMA_3: return("UDMA 3"); case XFER_UDMA_2: return("UDMA 2"); @@ -251,6 +253,10 @@ drive->id->dma_1word &= ~0x0F00; switch(speed) { +#if 0 + case XFER_UDMA_6: drive->id->dma_ultra |= 0x1010; break; + case XFER_UDMA_5: drive->id->dma_ultra |= 0x1010; break; +#endif case XFER_UDMA_4: drive->id->dma_ultra |= 0x1010; break; case XFER_UDMA_3: drive->id->dma_ultra |= 0x0808; break; case XFER_UDMA_2: drive->id->dma_ultra |= 0x0404; break; diff -u --recursive --new-file v2.3.33/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c --- v2.3.33/linux/drivers/block/ide-floppy.c Thu Nov 18 20:25:37 1999 +++ linux/drivers/block/ide-floppy.c Tue Dec 14 23:03:50 1999 @@ -1614,6 +1614,10 @@ printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name); continue; } + if (drive->scsi) { + printk("ide-floppy: passing drive %s to ide-scsi emulation.\n", drive->name); + continue; + } if ((floppy = (idefloppy_floppy_t *) kmalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL) { printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name); continue; diff -u --recursive --new-file v2.3.33/linux/drivers/block/ide-pci.c linux/drivers/block/ide-pci.c --- v2.3.33/linux/drivers/block/ide-pci.c Thu Nov 18 20:25:37 1999 +++ linux/drivers/block/ide-pci.c Tue Dec 14 23:03:50 1999 @@ -29,6 +29,8 @@ #define DEVID_PIIXb ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1}) #define DEVID_PIIX3 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1}) #define DEVID_PIIX4 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB}) +#define DEVID_PIIX4E ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_1}) +#define DEVID_PIIX4U ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_1}) #define DEVID_VIA_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561}) #define DEVID_VP_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1}) #define DEVID_PDC20246 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246}) @@ -171,11 +173,14 @@ #ifdef CONFIG_BLK_DEV_PIIX extern unsigned int pci_init_piix(struct pci_dev *, const char *); +extern unsigned int ata66_piix(ide_hwif_t *); extern void ide_init_piix(ide_hwif_t *); #define PCI_PIIX &pci_init_piix +#define ATA66_PIIX &ata66_piix #define INIT_PIIX &ide_init_piix #else #define PCI_PIIX NULL +#define ATA66_PIIX NULL #define INIT_PIIX NULL #endif @@ -254,6 +259,8 @@ {DEVID_PIIXb, "PIIX", NULL, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_PIIX3, "PIIX3", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_PIIX4, "PIIX4", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4E, "PIIX4", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4U, "PIIX4", PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_VIA_IDE, "VIA_IDE", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, {DEVID_VP_IDE, "VP_IDE", PCI_VIA82CXXX, ATA66_VIA82CXXX,INIT_VIA82CXXX, DMA_VIA82CXXX, {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, ON_BOARD, 0 }, {DEVID_PDC20246,"PDC20246", PCI_PDC202XX, NULL, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 16 }, @@ -566,7 +573,11 @@ hwif->irq = hwif->channel ? 15 : 14; goto bypass_umc_dma; } - hwif->udma_four = (d->ata66_check) ? d->ata66_check(hwif) : 0; + if (hwif->udma_four) { + printk("%s: ATA-66 forced bit set (WARNING)!!\n", d->name); + } else { + hwif->udma_four = (d->ata66_check) ? d->ata66_check(hwif) : 0; + } #ifdef CONFIG_BLK_DEV_IDEDMA if (IDE_PCI_DEVID_EQ(d->devid, DEVID_SIS5513) || IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X)) diff -u --recursive --new-file v2.3.33/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c --- v2.3.33/linux/drivers/block/ide-tape.c Thu Nov 18 20:25:37 1999 +++ linux/drivers/block/ide-tape.c Tue Dec 14 23:03:50 1999 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide-tape.c Version 1.15 Jul 4, 1999 + * linux/drivers/block/ide-tape.c Version 1.16e Oct 3, 1999 * * Copyright (C) 1995 - 1999 Gadi Oxman * @@ -216,7 +216,14 @@ * Replace cli()/sti() with hwgroup spinlocks. * Ver 1.15 Mar 25 99 Fix SMP race condition by replacing hwgroup * spinlock with private per-tape spinlock. - * Fix use of freed memory. + * Ver 1.16 Sep 1 99 Add OnStream tape support. + * Abort read pipeline on EOD. + * Wait for the tape to become ready in case it returns + * "in the process of becoming ready" on open(). + * Fix zero padding of the last written block in + * case the tape block size is larger than PAGE_SIZE. + * Decrease the default disconnection time to tn. + * Ver 1.16e Oct 3 99 Minor fixes. * * Here are some words from the first releases of hd.c, which are quoted * in ide.c and apply here as well: @@ -326,7 +333,7 @@ * sharing a (fast) ATA-2 disk with any (slow) new ATAPI device. */ -#define IDETAPE_VERSION "1.13" +#define IDETAPE_VERSION "1.16e" #include #include @@ -352,12 +359,114 @@ #include /* - * For general magnetic tape device compatibility. + * OnStream support */ +#define ONSTREAM_DEBUG (0) +#define OS_CONFIG_PARTITION (0xff) +#define OS_DATA_PARTITION (0) +#define OS_PARTITION_VERSION (1) + +/* + * partition + */ +typedef struct os_partition_s { + __u8 partition_num; + __u8 par_desc_ver; + __u16 wrt_pass_cntr; + __u32 first_frame_addr; + __u32 last_frame_addr; + __u32 eod_frame_addr; +} os_partition_t; + +/* + * DAT entry + */ +typedef struct os_dat_entry_s { + __u32 blk_sz; + __u16 blk_cnt; + __u8 flags; + __u8 reserved; +} os_dat_entry_t; + +/* + * DAT + */ +#define OS_DAT_FLAGS_DATA (0xc) +#define OS_DAT_FLAGS_MARK (0x1) + +typedef struct os_dat_s { + __u8 dat_sz; + __u8 reserved1; + __u8 entry_cnt; + __u8 reserved3; + os_dat_entry_t dat_list[16]; +} os_dat_t; + +/* + * Frame types + */ +#define OS_FRAME_TYPE_FILL (0) +#define OS_FRAME_TYPE_EOD (1 << 0) +#define OS_FRAME_TYPE_MARKER (1 << 1) +#define OS_FRAME_TYPE_HEADER (1 << 3) +#define OS_FRAME_TYPE_DATA (1 << 7) + +/* + * AUX + */ +typedef struct os_aux_s { + __u32 format_id; /* hardware compability AUX is based on */ + char application_sig[4]; /* driver used to write this media */ + __u32 hdwr; /* reserved */ + __u32 update_frame_cntr; /* for configuration frame */ + __u8 frame_type; + __u8 frame_type_reserved; + __u8 reserved_18_19[2]; + os_partition_t partition; + __u8 reserved_36_43[8]; + __u32 frame_seq_num; + __u32 logical_blk_num_high; + __u32 logical_blk_num; + os_dat_t dat; + __u8 reserved188_191[4]; + __u32 filemark_cnt; + __u32 phys_fm; + __u32 last_mark_addr; + __u8 reserved204_223[20]; + + /* + * __u8 app_specific[32]; + * + * Linux specific fields: + */ + __u32 next_mark_addr; /* when known, points to next marker */ + __u8 linux_specific[28]; + + __u8 reserved_256_511[256]; +} os_aux_t; + +typedef struct os_header_s { + char ident_str[8]; + __u8 major_rev; + __u8 minor_rev; + __u8 reserved10_15[6]; + __u8 par_num; + __u8 reserved1_3[3]; + os_partition_t partition; +} os_header_t; + +/* + * OnStream ADRL frame + */ +#define OS_FRAME_SIZE (32 * 1024 + 512) +#define OS_DATA_SIZE (32 * 1024) +#define OS_AUX_SIZE (512) + #include /**************************** Tunable parameters *****************************/ + /* * Pipelined mode parameters. * @@ -372,36 +481,13 @@ * * Setting the following parameter to 0 will disable the pipelined mode. */ -#define IDETAPE_MIN_PIPELINE_STAGES 100 -#define IDETAPE_MAX_PIPELINE_STAGES 200 +#define IDETAPE_MIN_PIPELINE_STAGES 200 +#define IDETAPE_MAX_PIPELINE_STAGES 400 #define IDETAPE_INCREASE_STAGES_RATE 20 /* - * Assuming the tape shares an interface with another device, the default - * behavior is to service our pending pipeline requests as soon as - * possible, but to gracefully postpone them in favor of the other device - * when the tape is busy. This has the potential to maximize our - * throughput and in the same time, to make efficient use of the IDE bus. - * - * Note that when we transfer data to / from the tape, we co-operate with - * the relatively fast tape buffers and the tape will perform the - * actual media access in the background, without blocking the IDE - * bus. This means that as long as the maximum IDE bus throughput is much - * higher than the sum of our maximum throughput and the maximum - * throughput of the other device, we should probably leave the default - * behavior. - * - * However, if it is still desired to give the other device a share even - * in our own (small) bus bandwidth, you can set IDETAPE_LOW_TAPE_PRIORITY - * to 1. This will let the other device finish *all* its pending requests - * before we even check if we can service our next pending request. - */ -#define IDETAPE_LOW_TAPE_PRIORITY 0 - -/* * The following are used to debug the driver: * - * Setting IDETAPE_INFO_LOG to 1 will log driver vender information. * Setting IDETAPE_DEBUG_LOG to 1 will log driver flow control. * Setting IDETAPE_DEBUG_BUGS to 1 will enable self-sanity checks in * some places. @@ -416,15 +502,9 @@ * is verified to be stable enough. This will make it much more * esthetic. */ -#define IDETAPE_INFO_LOG 0 #define IDETAPE_DEBUG_LOG 0 #define IDETAPE_DEBUG_BUGS 1 -#if IDETAPE_DEBUG_LOG -#undef IDETAPE_INFO_LOG -#define IDETAPE_INFO_LOG IDETAPE_DEBUG_LOG -#endif - /* * After each failed packet command we issue a request sense command * and retry the packet command IDETAPE_MAX_PC_RETRIES times. @@ -448,6 +528,20 @@ #define IDETAPE_PC_STACK (10 + IDETAPE_MAX_PC_RETRIES) /* + * Some tape drives require a long irq timeout + */ +#define IDETAPE_WAIT_CMD (60*HZ) + +/* + * The following parameter is used to select the point in the internal + * tape fifo in which we will start to refill the buffer. Decreasing + * the following parameter will improve the system's latency and + * interactive response, while using a high value might improve sytem + * throughput. + */ +#define IDETAPE_FIFO_THRESHOLD 2 + +/* * DSC polling parameters. * * Polling for DSC (a single bit in the status register) is a very @@ -479,20 +573,6 @@ */ /* - * The following parameter is used to select the point in the internal - * tape fifo in which we will start to refill the buffer. Decreasing - * the following parameter will improve the system's latency and - * interactive response, while using a high value might improve sytem - * throughput. - */ -#define IDETAPE_FIFO_THRESHOLD 2 - -/* - * Some tape drives require a long irq timeout - */ -#define IDETAPE_WAIT_CMD (60*HZ) - -/* * DSC timings. */ #define IDETAPE_DSC_RW_MIN 5*HZ/100 /* 50 msec */ @@ -505,6 +585,25 @@ /*************************** End of tunable parameters ***********************/ +/* + * Debugging/Performance analysis + * + * I/O trace support + */ +#define USE_IOTRACE 0 +#if USE_IOTRACE +#include +#define IO_IDETAPE_FIFO 500 +#endif + +/* + * Read/Write error simulation + */ +#define SIMULATE_ERRORS 0 + +/* + * For general magnetic tape device compatibility. + */ typedef enum { idetape_direction_none, idetape_direction_read, @@ -569,8 +668,9 @@ unsigned blk512 :1; /* Supports 512 bytes block size */ unsigned blk1024 :1; /* Supports 1024 bytes block size */ unsigned reserved7_3_6 :4; - unsigned slowb :1; /* The device restricts the byte count for PIO */ + unsigned blk32768 :1; /* slowb - the device restricts the byte count for PIO */ /* transfers for slow buffer memory ??? */ + /* Also 32768 block size in some cases */ u16 max_speed; /* Maximum speed supported in KBps */ u8 reserved10, reserved11; u16 ctl; /* Continuous Transfer Limit in blocks */ @@ -580,15 +680,60 @@ } idetape_capabilities_page_t; /* + * Block Size Page + */ +typedef struct { + unsigned page_code :6; /* Page code - Should be 0x30 */ + unsigned reserved1_6 :1; + unsigned ps :1; + u8 page_length; /* Page Length - Should be 2 */ + u8 reserved2; + unsigned play32 :1; + unsigned play32_5 :1; + unsigned reserved2_23 :2; + unsigned record32 :1; + unsigned record32_5 :1; + unsigned reserved2_6 :1; + unsigned one :1; +} idetape_block_size_page_t; + +/* * A pipeline stage. */ typedef struct idetape_stage_s { struct request rq; /* The corresponding request */ struct buffer_head *bh; /* The data buffers */ struct idetape_stage_s *next; /* Pointer to the next stage */ + os_aux_t *aux; /* OnStream aux ptr */ } idetape_stage_t; /* + * REQUEST SENSE packet command result - Data Format. + */ +typedef struct { + unsigned error_code :7; /* Current of deferred errors */ + unsigned valid :1; /* The information field conforms to QIC-157C */ + u8 reserved1 :8; /* Segment Number - Reserved */ + unsigned sense_key :4; /* Sense Key */ + unsigned reserved2_4 :1; /* Reserved */ + unsigned ili :1; /* Incorrect Length Indicator */ + unsigned eom :1; /* End Of Medium */ + unsigned filemark :1; /* Filemark */ + u32 information __attribute__ ((packed)); + u8 asl; /* Additional sense length (n-7) */ + u32 command_specific; /* Additional command specific information */ + u8 asc; /* Additional Sense Code */ + u8 ascq; /* Additional Sense Code Qualifier */ + u8 replaceable_unit_code; /* Field Replaceable Unit Code */ + unsigned sk_specific1 :7; /* Sense Key Specific */ + unsigned sksv :1; /* Sense Key Specific information is valid */ + u8 sk_specific2; /* Sense Key Specific */ + u8 sk_specific3; /* Sense Key Specific */ + u8 pad[2]; /* Padding to 20 bytes */ +} idetape_request_sense_result_t; + + +/* * Most of our global data which we need to save even as we leave the * driver due to an interrupt or a timer event is stored in a variable * of type idetape_tape_t, defined below. @@ -637,10 +782,12 @@ unsigned long dsc_timeout; /* Maximum waiting time */ /* - * Position information + * Read position information */ byte partition; - unsigned int block_address; /* Current block */ + unsigned int first_frame_position; /* Current block */ + unsigned int last_frame_position; + unsigned int blocks_in_buffer; /* * Last error information @@ -701,9 +848,109 @@ unsigned int flags; /* Status/Action flags */ spinlock_t spinlock; /* protects the ide-tape queue */ + + /* + * Measures average tape speed + */ + unsigned long avg_time; + int avg_size; + int avg_speed; + + idetape_request_sense_result_t sense; /* last sense information */ + + char vendor_id[10]; + char product_id[18]; + char firmware_revision[6]; + int firmware_revision_num; + + int door_locked; /* the door is currently locked */ + + /* + * OnStream flags + */ + int onstream; /* the tape is an OnStream tape */ + int raw; /* OnStream raw access (32.5KB block size) */ + int cur_frames; /* current number of frames in internal buffer */ + int max_frames; /* max number of frames in internal buffer */ + int logical_blk_num; /* logical block number */ + __u16 wrt_pass_cntr; /* write pass counter */ + __u32 update_frame_cntr; /* update frame counter */ + struct semaphore *sem; + int onstream_write_error; /* write error recovery active */ + int header_ok; /* header frame verified ok */ + int linux_media; /* reading linux-specifc media */ + int linux_media_version; + char application_sig[5]; /* application signature */ + int filemark_cnt; + int first_mark_addr; + int last_mark_addr; + int eod_frame_addr; + unsigned long cmd_start_time; + unsigned long max_cmd_time; + + /* + * Optimize the number of "buffer filling" + * mode sense commands. + */ + unsigned long last_buffer_fill; /* last time in which we issued fill cmd */ + int req_buffer_fill; /* buffer fill command requested */ + int writes_since_buffer_fill; + int reads_since_buffer_fill; + + /* + * Limit the number of times a request can + * be postponed, to avoid an infinite postpone + * deadlock. + */ + int postpone_cnt; /* request postpone count limit */ + + /* + * Measures number of frames: + * + * 1. written/read to/from the driver pipeline (pipeline_head). + * 2. written/read to/from the tape buffers (buffer_head). + * 3. written/read by the tape to/from the media (tape_head). + */ + int pipeline_head; + int buffer_head; + int tape_head; + int last_tape_head; + + /* + * Speed control at the tape buffers input/output + */ + unsigned long insert_time; + int insert_size; + int insert_speed; + int max_insert_speed; + int measure_insert_time; + + /* + * Measure tape still time, in milliseconds + */ + unsigned long tape_still_time_begin; + int tape_still_time; + + /* + * Speed regulation negative feedback loop + */ + int speed_control; + int pipeline_head_speed, controlled_pipeline_head_speed, uncontrolled_pipeline_head_speed; + int controlled_last_pipeline_head, uncontrolled_last_pipeline_head; + unsigned long uncontrolled_pipeline_head_time, controlled_pipeline_head_time; + int controlled_previous_pipeline_head, uncontrolled_previous_pipeline_head; + unsigned long controlled_previous_head_time, uncontrolled_previous_head_time; + int restart_speed_control_req; } idetape_tape_t; /* + * Tape door status + */ +#define DOOR_UNLOCKED 0 +#define DOOR_LOCKED 1 +#define DOOR_EXPLICITLY_LOCKED 2 + +/* * Tape flag bits values. */ #define IDETAPE_IGNORE_DSC 0 @@ -713,6 +960,8 @@ #define IDETAPE_DETECT_BS 4 /* Attempt to auto-detect the current user block size */ #define IDETAPE_FILEMARK 5 /* Currently on a filemark */ #define IDETAPE_DRQ_INTERRUPT 6 /* DRQ interrupt device */ +#define IDETAPE_READ_ERROR 7 +#define IDETAPE_PIPELINE_ACTIVE 8 /* pipeline active */ /* * Supported ATAPI tape drives packet commands @@ -727,9 +976,18 @@ #define IDETAPE_INQUIRY_CMD 0x12 #define IDETAPE_ERASE_CMD 0x19 #define IDETAPE_MODE_SENSE_CMD 0x1a +#define IDETAPE_MODE_SELECT_CMD 0x15 #define IDETAPE_LOAD_UNLOAD_CMD 0x1b +#define IDETAPE_PREVENT_CMD 0x1e #define IDETAPE_LOCATE_CMD 0x2b #define IDETAPE_READ_POSITION_CMD 0x34 +#define IDETAPE_READ_BUFFER_CMD 0x3c +#define IDETAPE_SET_SPEED_CMD 0xbb + +/* + * Some defines for the READ BUFFER command + */ +#define IDETAPE_RETRIEVE_FAULTY_BLOCK 6 /* * Some defines for the SPACE command @@ -768,8 +1026,10 @@ #define IDETAPE_READ_RQ 92 #define IDETAPE_WRITE_RQ 93 #define IDETAPE_ABORTED_WRITE_RQ 94 +#define IDETAPE_ABORTED_READ_RQ 95 +#define IDETAPE_READ_BUFFER_RQ 96 -#define IDETAPE_LAST_RQ 94 +#define IDETAPE_LAST_RQ 96 /* * A macro which can be used to check if a we support a given @@ -947,36 +1207,12 @@ } idetape_read_position_result_t; /* - * REQUEST SENSE packet command result - Data Format. - */ -typedef struct { - unsigned error_code :7; /* Current of deferred errors */ - unsigned valid :1; /* The information field conforms to QIC-157C */ - u8 reserved1 :8; /* Segment Number - Reserved */ - unsigned sense_key :4; /* Sense Key */ - unsigned reserved2_4 :1; /* Reserved */ - unsigned ili :1; /* Incorrect Length Indicator */ - unsigned eom :1; /* End Of Medium */ - unsigned filemark :1; /* Filemark */ - u32 information __attribute__ ((packed)); - u8 asl; /* Additional sense length (n-7) */ - u32 command_specific; /* Additional command specific information */ - u8 asc; /* Additional Sense Code */ - u8 ascq; /* Additional Sense Code Qualifier */ - u8 replaceable_unit_code; /* Field Replaceable Unit Code */ - unsigned sk_specific1 :7; /* Sense Key Specific */ - unsigned sksv :1; /* Sense Key Specific information is valid */ - u8 sk_specific2; /* Sense Key Specific */ - u8 sk_specific3; /* Sense Key Specific */ - u8 pad[2]; /* Padding to 20 bytes */ -} idetape_request_sense_result_t; - -/* * Follows structures which are related to the SELECT SENSE / MODE SENSE * packet commands. Those packet commands are still not supported * by ide-tape. */ #define IDETAPE_CAPABILITIES_PAGE 0x2a +#define IDETAPE_BLOCK_SIZE_PAGE 0x30 /* * Mode Parameter Header for the MODE SENSE packet command @@ -1140,46 +1376,6 @@ #endif /* CONFIG_BLK_DEV_IDEDMA */ /* - * idetape_postpone_request postpones the current request so that - * ide.c will be able to service requests from another device on - * the same hwgroup while we are polling for DSC. - */ -static void idetape_postpone_request (ide_drive_t *drive) -{ - idetape_tape_t *tape = drive->driver_data; - - tape->postponed_rq = HWGROUP(drive)->rq; - ide_stall_queue(drive, tape->dsc_polling_frequency); -} - -/* - * idetape_queue_pc_head generates a new packet command request in front - * of the request queue, before the current request, so that it will be - * processed immediately, on the next pass through the driver. - * - * idetape_queue_pc_head is called from the request handling part of - * the driver (the "bottom" part). Safe storage for the request should - * be allocated with idetape_next_pc_storage and idetape_next_rq_storage - * before calling idetape_queue_pc_head. - * - * Memory for those requests is pre-allocated at initialization time, and - * is limited to IDETAPE_PC_STACK requests. We assume that we have enough - * space for the maximum possible number of inter-dependent packet commands. - * - * The higher level of the driver - The ioctl handler and the character - * device handling functions should queue request to the lower level part - * and wait for their completion using idetape_queue_pc_tail or - * idetape_queue_rw_tail. - */ -static void idetape_queue_pc_head (ide_drive_t *drive,idetape_pc_t *pc,struct request *rq) -{ - ide_init_drive_cmd (rq); - rq->buffer = (char *) pc; - rq->cmd = IDETAPE_PC_RQ1; - (void) ide_do_drive_cmd (drive, rq, ide_preempt); -} - -/* * idetape_next_pc_storage returns a pointer to a place in which we can * safely store a packet command, even though we intend to leave the * driver. A storage space for a maximum of IDETAPE_PC_STACK packet @@ -1223,196 +1419,116 @@ } /* - * Pipeline related functions + * idetape_init_pc initializes a packet command. */ - -static inline int idetape_pipeline_active (idetape_tape_t *tape) +static void idetape_init_pc (idetape_pc_t *pc) { - return tape->active_data_request != NULL; + memset (pc->c, 0, 12); + pc->retries = 0; + pc->flags = 0; + pc->request_transfer = 0; + pc->buffer = pc->pc_buffer; + pc->buffer_size = IDETAPE_PC_BUFFER_SIZE; + pc->bh = NULL; + pc->b_data = NULL; } /* - * idetape_kfree_stage calls kfree to completely free a stage, along with - * its related buffers. + * idetape_analyze_error is called on each failed packet command retry + * to analyze the request sense. We currently do not utilize this + * information. */ -static void __idetape_kfree_stage (idetape_stage_t *stage) +static void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result) { - struct buffer_head *prev_bh, *bh = stage->bh; - int size; + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t *pc = tape->failed_pc; - while (bh != NULL) { - if (bh->b_data != NULL) { - size = (int) bh->b_size; - while (size > 0) { - free_page ((unsigned long) bh->b_data); - size -= PAGE_SIZE; - bh->b_data += PAGE_SIZE; - } + tape->sense = *result; + tape->sense_key = result->sense_key; tape->asc = result->asc; tape->ascq = result->ascq; +#if IDETAPE_DEBUG_LOG + /* + * Without debugging, we only log an error if we decided to + * give up retrying. + */ + printk (KERN_INFO "ide-tape: pc = %x, sense key = %x, asc = %x, ascq = %x\n",pc->c[0],result->sense_key,result->asc,result->ascq); +#endif /* IDETAPE_DEBUG_LOG */ + + if (tape->onstream && result->sense_key == 2 && result->asc == 0x53 && result->ascq == 2) { + clear_bit(PC_DMA_ERROR, &pc->flags); + ide_stall_queue(drive, HZ / 2); + return; + } +#ifdef CONFIG_BLK_DEV_IDEDMA + + /* + * Correct pc->actually_transferred by asking the tape. + */ + if (test_bit (PC_DMA_ERROR, &pc->flags)) { + pc->actually_transferred = pc->request_transfer - tape->tape_block_size * ntohl (get_unaligned (&result->information)); + idetape_update_buffers (pc); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + if (pc->c[0] == IDETAPE_READ_CMD && result->filemark) { + pc->error = IDETAPE_ERROR_FILEMARK; + set_bit (PC_ABORT, &pc->flags); + } + if (pc->c[0] == IDETAPE_WRITE_CMD) { + if (result->eom || (result->sense_key == 0xd && result->asc == 0x0 && result->ascq == 0x2)) { + pc->error = IDETAPE_ERROR_EOD; + set_bit (PC_ABORT, &pc->flags); } - prev_bh = bh; - bh = bh->b_reqnext; - kfree (prev_bh); } - kfree (stage); + if (pc->c[0] == IDETAPE_READ_CMD || pc->c[0] == IDETAPE_WRITE_CMD) { + if (result->sense_key == 8) { + pc->error = IDETAPE_ERROR_EOD; + set_bit (PC_ABORT, &pc->flags); + } + if (!test_bit (PC_ABORT, &pc->flags) && (tape->onstream || pc->actually_transferred)) + pc->retries = IDETAPE_MAX_PC_RETRIES + 1; + } } -static void idetape_kfree_stage (idetape_tape_t *tape, idetape_stage_t *stage) +static void idetape_abort_pipeline (ide_drive_t *drive) { - if (tape->cache_stage == NULL) - tape->cache_stage = stage; - else - __idetape_kfree_stage (stage); -} - -/* - * idetape_kmalloc_stage uses __get_free_page to allocate a pipeline - * stage, along with all the necessary small buffers which together make - * a buffer of size tape->stage_size (or a bit more). We attempt to - * combine sequential pages as much as possible. - * - * Returns a pointer to the new allocated stage, or NULL if we - * can't (or don't want to) allocate a stage. - * - * Pipeline stages are optional and are used to increase performance. - * If we can't allocate them, we'll manage without them. - */ -static idetape_stage_t *__idetape_kmalloc_stage (idetape_tape_t *tape) -{ - idetape_stage_t *stage; - struct buffer_head *prev_bh, *bh; - int pages = tape->pages_per_stage; - char *b_data; - - if ((stage = (idetape_stage_t *) kmalloc (sizeof (idetape_stage_t),GFP_KERNEL)) == NULL) - return NULL; - stage->next = NULL; - - bh = stage->bh = (struct buffer_head *) kmalloc (sizeof (struct buffer_head), GFP_KERNEL); - if (bh == NULL) - goto abort; - bh->b_reqnext = NULL; - if ((bh->b_data = (char *) __get_free_page (GFP_KERNEL)) == NULL) - goto abort; - bh->b_size = PAGE_SIZE; - set_bit (BH_Lock, &bh->b_state); + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage = tape->next_stage; - while (--pages) { - if ((b_data = (char *) __get_free_page (GFP_KERNEL)) == NULL) - goto abort; - if (bh->b_data == b_data + PAGE_SIZE && virt_to_bus (bh->b_data) == virt_to_bus (b_data) + PAGE_SIZE) { - bh->b_size += PAGE_SIZE; - bh->b_data -= PAGE_SIZE; - continue; - } - if (b_data == bh->b_data + bh->b_size && virt_to_bus (b_data) == virt_to_bus (bh->b_data) + bh->b_size) { - bh->b_size += PAGE_SIZE; - continue; - } - prev_bh = bh; - if ((bh = (struct buffer_head *) kmalloc (sizeof (struct buffer_head), GFP_KERNEL)) == NULL) { - free_page ((unsigned long) b_data); - goto abort; - } - bh->b_reqnext = NULL; - bh->b_data = b_data; - bh->b_size = PAGE_SIZE; - set_bit (BH_Lock, &bh->b_state); - prev_bh->b_reqnext = bh; +#if IDETAPE_DEBUG_LOG + printk(KERN_INFO "ide-tape: %s: idetape_abort_pipeline called\n", tape->name); +#endif + while (stage) { + if (stage->rq.cmd == IDETAPE_WRITE_RQ) + stage->rq.cmd = IDETAPE_ABORTED_WRITE_RQ; + else if (stage->rq.cmd == IDETAPE_READ_RQ) + stage->rq.cmd = IDETAPE_ABORTED_READ_RQ; + stage = stage->next; } - bh->b_size -= tape->excess_bh_size; - return stage; -abort: - __idetape_kfree_stage (stage); - return NULL; } -static idetape_stage_t *idetape_kmalloc_stage (idetape_tape_t *tape) +/* + * idetape_active_next_stage will declare the next stage as "active". + */ +static void idetape_active_next_stage (ide_drive_t *drive) { - idetape_stage_t *cache_stage = tape->cache_stage; + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage=tape->next_stage; + struct request *rq = &stage->rq; #if IDETAPE_DEBUG_LOG - printk (KERN_INFO "Reached idetape_kmalloc_stage\n"); + printk (KERN_INFO "Reached idetape_active_next_stage\n"); #endif /* IDETAPE_DEBUG_LOG */ - - if (tape->nr_stages >= tape->max_stages) - return NULL; - if (cache_stage != NULL) { - tape->cache_stage = NULL; - return cache_stage; - } - return __idetape_kmalloc_stage (tape); -} - -static void idetape_copy_stage_from_user (idetape_tape_t *tape, idetape_stage_t *stage, const char *buf, int n) -{ - struct buffer_head *bh = tape->bh; - int count; - - while (n) { -#if IDETAPE_DEBUG_BUGS - if (bh == NULL) { - printk (KERN_ERR "ide-tape: bh == NULL in idetape_copy_stage_from_user\n"); - return; - } -#endif /* IDETAPE_DEBUG_BUGS */ - count = IDE_MIN (bh->b_size - atomic_read(&bh->b_count), n); - copy_from_user (bh->b_data + atomic_read(&bh->b_count), buf, count); - n -= count; atomic_add(count, &bh->b_count); buf += count; - if (atomic_read(&bh->b_count) == bh->b_size) { - bh = bh->b_reqnext; - if (bh) - atomic_set(&bh->b_count, 0); - } - } - tape->bh = bh; -} - -static void idetape_copy_stage_to_user (idetape_tape_t *tape, char *buf, idetape_stage_t *stage, int n) -{ - struct buffer_head *bh = tape->bh; - int count; - - while (n) { #if IDETAPE_DEBUG_BUGS - if (bh == NULL) { - printk (KERN_ERR "ide-tape: bh == NULL in idetape_copy_stage_to_user\n"); - return; - } -#endif /* IDETAPE_DEBUG_BUGS */ - count = IDE_MIN (tape->b_count, n); - copy_to_user (buf, tape->b_data, count); - n -= count; tape->b_data += count; tape->b_count -= count; buf += count; - if (!tape->b_count) { - tape->bh = bh = bh->b_reqnext; - if (bh) { - tape->b_data = bh->b_data; - tape->b_count = atomic_read(&bh->b_count); - } - } - } -} - -static void idetape_init_merge_stage (idetape_tape_t *tape) -{ - struct buffer_head *bh = tape->merge_stage->bh; - - tape->bh = bh; - if (tape->chrdev_direction == idetape_direction_write) - atomic_set(&bh->b_count, 0); - else { - tape->b_data = bh->b_data; - tape->b_count = atomic_read(&bh->b_count); + if (stage == NULL) { + printk (KERN_ERR "ide-tape: bug: Trying to activate a non existing stage\n"); + return; } -} - -static void idetape_switch_buffers (idetape_tape_t *tape, idetape_stage_t *stage) -{ - struct buffer_head *tmp; +#endif /* IDETAPE_DEBUG_BUGS */ - tmp = stage->bh; - stage->bh = tape->merge_stage->bh; - tape->merge_stage->bh = tmp; - idetape_init_merge_stage (tape); + rq->buffer = NULL; + rq->bh = stage->bh; + tape->active_data_request=rq; + tape->active_stage=stage; + tape->next_stage=stage->next; } /* @@ -1437,28 +1553,33 @@ } /* - * idetape_add_stage_tail adds a new stage at the end of the pipeline. + * idetape_kfree_stage calls kfree to completely free a stage, along with + * its related buffers. */ -static void idetape_add_stage_tail (ide_drive_t *drive,idetape_stage_t *stage) +static void __idetape_kfree_stage (idetape_stage_t *stage) { - idetape_tape_t *tape = drive->driver_data; - unsigned long flags; - -#if IDETAPE_DEBUG_LOG - printk (KERN_INFO "Reached idetape_add_stage_tail\n"); -#endif /* IDETAPE_DEBUG_LOG */ - spin_lock_irqsave(&tape->spinlock, flags); - stage->next=NULL; - if (tape->last_stage != NULL) - tape->last_stage->next=stage; - else - tape->first_stage=tape->next_stage=stage; - tape->last_stage=stage; - if (tape->next_stage == NULL) - tape->next_stage=tape->last_stage; - tape->nr_stages++; - tape->nr_pending_stages++; - spin_unlock_irqrestore(&tape->spinlock, flags); + struct buffer_head *prev_bh, *bh = stage->bh; + int size; + + while (bh != NULL) { + if (bh->b_data != NULL) { + size = (int) bh->b_size; + while (size > 0) { + free_page ((unsigned long) bh->b_data); + size -= PAGE_SIZE; + bh->b_data += PAGE_SIZE; + } + } + prev_bh = bh; + bh = bh->b_reqnext; + kfree (prev_bh); + } + kfree (stage); +} + +static void idetape_kfree_stage (idetape_tape_t *tape, idetape_stage_t *stage) +{ + __idetape_kfree_stage (stage); } /* @@ -1499,83 +1620,35 @@ } /* - * idetape_active_next_stage will declare the next stage as "active". + * idetape_end_request is used to finish servicing a request, and to + * insert a pending pipeline request into the main device queue. */ -static void idetape_active_next_stage (ide_drive_t *drive) +static void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup) { + ide_drive_t *drive = hwgroup->drive; + struct request *rq = hwgroup->rq; idetape_tape_t *tape = drive->driver_data; - idetape_stage_t *stage=tape->next_stage; - struct request *rq = &stage->rq; + unsigned long flags; + int error; + int remove_stage = 0; +#if ONSTREAM_DEBUG + idetape_stage_t *stage; + os_aux_t *aux; + unsigned char *p; +#endif #if IDETAPE_DEBUG_LOG - printk (KERN_INFO "Reached idetape_active_next_stage\n"); + printk (KERN_INFO "Reached idetape_end_request\n"); #endif /* IDETAPE_DEBUG_LOG */ -#if IDETAPE_DEBUG_BUGS - if (stage == NULL) { - printk (KERN_ERR "ide-tape: bug: Trying to activate a non existing stage\n"); - return; - } -#endif /* IDETAPE_DEBUG_BUGS */ - - rq->buffer = NULL; - rq->bh = stage->bh; - tape->active_data_request=rq; - tape->active_stage=stage; - tape->next_stage=stage->next; -} - -/* - * idetape_insert_pipeline_into_queue is used to start servicing the - * pipeline stages, starting from tape->next_stage. - */ -static void idetape_insert_pipeline_into_queue (ide_drive_t *drive) -{ - idetape_tape_t *tape = drive->driver_data; - if (tape->next_stage == NULL) - return; - if (!idetape_pipeline_active (tape)) { - idetape_active_next_stage (drive); - (void) ide_do_drive_cmd (drive, tape->active_data_request, ide_end); + switch (uptodate) { + case 0: error = IDETAPE_ERROR_GENERAL; break; + case 1: error = 0; break; + default: error = uptodate; } -} - -static void idetape_abort_pipeline (ide_drive_t *drive) -{ - idetape_tape_t *tape = drive->driver_data; - idetape_stage_t *stage = tape->next_stage; - - while (stage) { - stage->rq.cmd = IDETAPE_ABORTED_WRITE_RQ; - stage = stage->next; - } -} - -/* - * idetape_end_request is used to finish servicing a request, and to - * insert a pending pipeline request into the main device queue. - */ -static void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup) -{ - ide_drive_t *drive = hwgroup->drive; - struct request *rq = hwgroup->rq; - idetape_tape_t *tape = drive->driver_data; - unsigned long flags; - int error; - int remove_stage = 0; - -#if IDETAPE_DEBUG_LOG - printk (KERN_INFO "Reached idetape_end_request\n"); -#endif /* IDETAPE_DEBUG_LOG */ - - switch (uptodate) { - case 0: error = IDETAPE_ERROR_GENERAL; break; - case 1: error = 0; break; - default: error = uptodate; - } - rq->errors = error; - if (error) - tape->failed_pc = NULL; + rq->errors = error; + if (error) + tape->failed_pc = NULL; spin_lock_irqsave(&tape->spinlock, flags); if (tape->active_data_request == rq) { /* The request was a pipelined data transfer request */ @@ -1583,83 +1656,68 @@ tape->active_data_request = NULL; tape->nr_pending_stages--; if (rq->cmd == IDETAPE_WRITE_RQ) { +#if ONSTREAM_DEBUG + if (tape->onstream) { + stage = tape->first_stage; + aux = stage->aux; + p = stage->bh->b_data; + if (ntohl(aux->logical_blk_num) < 11300 && ntohl(aux->logical_blk_num) > 11100) + printk(KERN_INFO "finished writing logical blk %lu (data %x %x %x %x)\n", ntohl(aux->logical_blk_num), *p++, *p++, *p++, *p++); + } +#endif + if (tape->onstream && !tape->raw) { + if (tape->first_frame_position == 0xba4) { +#if ONSTREAM_DEBUG + printk("ide-tape: %s: skipping over config parition..\n", tape->name); +#endif + tape->onstream_write_error = 2; + if (tape->sem) + up(tape->sem); + } + } + remove_stage = 1; if (error) { set_bit (IDETAPE_PIPELINE_ERROR, &tape->flags); if (error == IDETAPE_ERROR_EOD) idetape_abort_pipeline (drive); + if (tape->onstream && !tape->raw && error == IDETAPE_ERROR_GENERAL && tape->sense.sense_key == 3) { + clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags); + printk(KERN_ERR "ide-tape: %s: write error, enabling error recovery\n", tape->name); + tape->onstream_write_error = 1; + remove_stage = 0; + tape->nr_pending_stages++; + tape->next_stage = tape->first_stage; + rq->current_nr_sectors = rq->nr_sectors; + if (tape->sem) + up(tape->sem); + } + } + } else if (rq->cmd == IDETAPE_READ_RQ) { + if (error == IDETAPE_ERROR_EOD) { + set_bit (IDETAPE_PIPELINE_ERROR, &tape->flags); + idetape_abort_pipeline(drive); } - remove_stage = 1; } - if (tape->next_stage != NULL) { + if (tape->next_stage != NULL && !tape->onstream_write_error) { idetape_active_next_stage (drive); /* * Insert the next request into the request queue. - * The choice of using ide_next or ide_end is now left to the user. */ -#if IDETAPE_LOW_TAPE_PRIORITY (void) ide_do_drive_cmd (drive, tape->active_data_request, ide_end); -#else - (void) ide_do_drive_cmd (drive, tape->active_data_request, ide_next); -#endif /* IDETAPE_LOW_TAPE_PRIORITY */ - } else if (!error) - idetape_increase_max_pipeline_stages (drive); + } else if (!error) { + if (!tape->onstream) + idetape_increase_max_pipeline_stages (drive); + } } ide_end_drive_cmd (drive, 0, 0); if (remove_stage) idetape_remove_stage_head (drive); + if (tape->active_data_request == NULL) + clear_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags); spin_unlock_irqrestore(&tape->spinlock, flags); } -/* - * idetape_analyze_error is called on each failed packet command retry - * to analyze the request sense. We currently do not utilize this - * information. - */ -static void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result) -{ - idetape_tape_t *tape = drive->driver_data; - idetape_pc_t *pc = tape->failed_pc; - - tape->sense_key = result->sense_key; tape->asc = result->asc; tape->ascq = result->ascq; -#if IDETAPE_DEBUG_LOG - /* - * Without debugging, we only log an error if we decided to - * give up retrying. - */ - printk (KERN_INFO "ide-tape: pc = %x, sense key = %x, asc = %x, ascq = %x\n",pc->c[0],result->sense_key,result->asc,result->ascq); -#endif /* IDETAPE_DEBUG_LOG */ - -#ifdef CONFIG_BLK_DEV_IDEDMA - - /* - * Correct pc->actually_transferred by asking the tape. - */ - if (test_bit (PC_DMA_ERROR, &pc->flags)) { - pc->actually_transferred = pc->request_transfer - tape->tape_block_size * ntohl (get_unaligned (&result->information)); - idetape_update_buffers (pc); - } -#endif /* CONFIG_BLK_DEV_IDEDMA */ - if (pc->c[0] == IDETAPE_READ_CMD && result->filemark) { - pc->error = IDETAPE_ERROR_FILEMARK; - set_bit (PC_ABORT, &pc->flags); - } - if (pc->c[0] == IDETAPE_WRITE_CMD) { - if (result->eom || (result->sense_key == 0xd && result->asc == 0x0 && result->ascq == 0x2)) { - pc->error = IDETAPE_ERROR_EOD; - set_bit (PC_ABORT, &pc->flags); - } - } - if (pc->c[0] == IDETAPE_READ_CMD || pc->c[0] == IDETAPE_WRITE_CMD) { - if (result->sense_key == 8) { - pc->error = IDETAPE_ERROR_EOD; - set_bit (PC_ABORT, &pc->flags); - } - if (!test_bit (PC_ABORT, &pc->flags) && pc->actually_transferred) - pc->retries = IDETAPE_MAX_PC_RETRIES + 1; - } -} - static ide_startstop_t idetape_request_sense_callback (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; @@ -1677,31 +1735,43 @@ return ide_stopped; } -/* - * idetape_init_pc initializes a packet command. - */ -static void idetape_init_pc (idetape_pc_t *pc) -{ - memset (pc->c, 0, 12); - pc->retries = 0; - pc->flags = 0; - pc->request_transfer = 0; - pc->buffer = pc->pc_buffer; - pc->buffer_size = IDETAPE_PC_BUFFER_SIZE; - pc->bh = NULL; - pc->b_data = NULL; -} - static void idetape_create_request_sense_cmd (idetape_pc_t *pc) { idetape_init_pc (pc); pc->c[0] = IDETAPE_REQUEST_SENSE_CMD; - pc->c[4] = 255; + pc->c[4] = 20; pc->request_transfer = 18; pc->callback = &idetape_request_sense_callback; } /* + * idetape_queue_pc_head generates a new packet command request in front + * of the request queue, before the current request, so that it will be + * processed immediately, on the next pass through the driver. + * + * idetape_queue_pc_head is called from the request handling part of + * the driver (the "bottom" part). Safe storage for the request should + * be allocated with idetape_next_pc_storage and idetape_next_rq_storage + * before calling idetape_queue_pc_head. + * + * Memory for those requests is pre-allocated at initialization time, and + * is limited to IDETAPE_PC_STACK requests. We assume that we have enough + * space for the maximum possible number of inter-dependent packet commands. + * + * The higher level of the driver - The ioctl handler and the character + * device handling functions should queue request to the lower level part + * and wait for their completion using idetape_queue_pc_tail or + * idetape_queue_rw_tail. + */ +static void idetape_queue_pc_head (ide_drive_t *drive,idetape_pc_t *pc,struct request *rq) +{ + ide_init_drive_cmd (rq); + rq->buffer = (char *) pc; + rq->cmd = IDETAPE_PC_RQ1; + (void) ide_do_drive_cmd (drive, rq, ide_preempt); +} + +/* * idetape_retry_pc is called when an error was detected during the * last packet command. We queue a request sense packet command in * the head of the request list. @@ -1723,6 +1793,22 @@ } /* + * idetape_postpone_request postpones the current request so that + * ide.c will be able to service requests from another device on + * the same hwgroup while we are polling for DSC. + */ +static void idetape_postpone_request (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + +#if IDETAPE_DEBUG_LOG + printk(KERN_INFO "idetape_postpone_request\n"); +#endif + tape->postponed_rq = HWGROUP(drive)->rq; + ide_stall_queue(drive, tape->dsc_polling_frequency); +} + +/* * idetape_pc_intr is the usual interrupt handler which will be called * during a packet command. We will transfer some of the data (as * requested by the drive) and will re-point interrupt handler to us. @@ -1737,12 +1823,19 @@ idetape_bcount_reg_t bcount; idetape_ireason_reg_t ireason; idetape_pc_t *pc=tape->pc; + unsigned int temp; + unsigned long cmd_time; +#if SIMULATE_ERRORS + static int error_sim_count = 0; +#endif #if IDETAPE_DEBUG_LOG printk (KERN_INFO "ide-tape: Reached idetape_pc_intr interrupt handler\n"); #endif /* IDETAPE_DEBUG_LOG */ + status.all = GET_STAT(); /* Clear the interrupt */ + #ifdef CONFIG_BLK_DEV_IDEDMA if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { if (HWIF(drive)->dmaproc(ide_dma_end, drive)) { @@ -1758,7 +1851,7 @@ * information from the DMA engine on most chipsets). */ set_bit (PC_DMA_ERROR, &pc->flags); - } else { + } else if (!status.b.check) { pc->actually_transferred=pc->request_transfer; idetape_update_buffers (pc); } @@ -1768,9 +1861,9 @@ } #endif /* CONFIG_BLK_DEV_IDEDMA */ - status.all = GET_STAT(); /* Clear the interrupt */ - if (!status.b.drq) { /* No more interrupts */ + cmd_time = (jiffies - tape->cmd_start_time) * 1000 / HZ; + tape->max_cmd_time = IDE_MAX(cmd_time, tape->max_cmd_time); #if IDETAPE_DEBUG_LOG printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred); #endif /* IDETAPE_DEBUG_LOG */ @@ -1778,6 +1871,12 @@ ide__sti(); /* local CPU only */ +#if SIMULATE_ERRORS + if ((pc->c[0] == IDETAPE_WRITE_CMD || pc->c[0] == IDETAPE_READ_CMD) && (++error_sim_count % 100) == 0) { + printk(KERN_INFO "ide-tape: %s: simulating error\n", tape->name); + status.b.check = 1; + } +#endif if (status.b.check && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) status.b.check = 0; if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) { /* Error detected */ @@ -1788,10 +1887,13 @@ printk (KERN_ERR "ide-tape: I/O error in request sense command\n"); return ide_do_reset (drive); } - return idetape_retry_pc (drive); /* Retry operation */ +#if IDETAPE_DEBUG_LOG + printk(KERN_INFO "[cmd %x]: check condition\n", pc->c[0]); +#endif + return idetape_retry_pc (drive); /* Retry operation */ } pc->error = 0; - if (test_bit (PC_WAIT_FOR_DSC, &pc->flags) && !status.b.dsc) { /* Media access command */ + if (!tape->onstream && test_bit (PC_WAIT_FOR_DSC, &pc->flags) && !status.b.dsc) { /* Media access command */ tape->dsc_polling_start = jiffies; tape->dsc_polling_frequency = IDETAPE_DSC_MA_FAST; tape->dsc_timeout = jiffies + IDETAPE_DSC_MA_TIMEOUT; @@ -1850,7 +1952,9 @@ } pc->actually_transferred+=bcount.all; /* Update the current position */ pc->current_position+=bcount.all; - +#if IDETAPE_DEBUG_LOG + printk(KERN_INFO "[cmd %x] transferred %d bytes on that interrupt\n", pc->c[0], bcount.all); +#endif ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD,NULL); /* And set the interrupt handler again */ return ide_started; } @@ -1897,7 +2001,6 @@ * we will handle the next request. * */ - static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; @@ -1906,7 +2009,7 @@ int retries = 100; ide_startstop_t startstop; - if (ide_wait_stat(&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n"); return startstop; } @@ -1925,6 +2028,7 @@ printk (KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n"); return ide_do_reset (drive); } + tape->cmd_start_time = jiffies; ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL); /* Set the interrupt routine */ atapi_output_bytes (drive,pc->c,12); /* Send the actual packet */ return ide_started; @@ -1954,8 +2058,12 @@ * example). */ if (!test_bit (PC_ABORT, &pc->flags)) { - printk (KERN_ERR "ide-tape: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n", - tape->name, pc->c[0], tape->sense_key, tape->asc, tape->ascq); + if (!(pc->c[0] == 0 && tape->sense_key == 2 && tape->asc == 4 && (tape->ascq == 1 || tape->ascq == 8))) { + printk (KERN_ERR "ide-tape: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n", + tape->name, pc->c[0], tape->sense_key, tape->asc, tape->ascq); + if (tape->onstream && pc->c[0] == 8 && tape->sense_key == 3 && tape->asc == 11) + printk(KERN_ERR "ide-tape: %s: enabling read error recovery\n", tape->name); + } pc->error = IDETAPE_ERROR_GENERAL; /* Giving up */ } tape->failed_pc=NULL; @@ -2001,17 +2109,139 @@ } } +/* + * General packet command callback function. + */ +static ide_startstop_t idetape_pc_callback (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + +#if IDETAPE_DEBUG_LOG + printk (KERN_INFO "ide-tape: Reached idetape_pc_callback\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive)); + return ide_stopped; +} + +/* + * A mode sense command is used to "sense" tape parameters. + */ +static void idetape_create_mode_sense_cmd (idetape_pc_t *pc, byte page_code) +{ + idetape_init_pc (pc); + pc->c[0] = IDETAPE_MODE_SENSE_CMD; + pc->c[1] = 8; /* DBD = 1 - Don't return block descriptors for now */ + pc->c[2] = page_code; + pc->c[3] = 255; /* Don't limit the returned information */ + pc->c[4] = 255; /* (We will just discard data in that case) */ + if (page_code == IDETAPE_CAPABILITIES_PAGE) + pc->request_transfer = 24; + else + pc->request_transfer = 50; + pc->callback = &idetape_pc_callback; +} + +static ide_startstop_t idetape_onstream_buffer_fill_callback (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + tape->max_frames = tape->pc->buffer[4 + 2]; + tape->cur_frames = tape->pc->buffer[4 + 3]; + if (tape->chrdev_direction == idetape_direction_write) + tape->tape_head = tape->buffer_head - tape->cur_frames; + else + tape->tape_head = tape->buffer_head + tape->cur_frames; + if (tape->tape_head != tape->last_tape_head) { + tape->last_tape_head = tape->tape_head; + tape->tape_still_time_begin = jiffies; + if (tape->tape_still_time > 200) + tape->measure_insert_time = 1; + } + tape->tape_still_time = (jiffies - tape->tape_still_time_begin) * 1000 / HZ; +#if USE_IOTRACE + IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor); +#endif +#if IDETAPE_DEBUG_LOG + printk(KERN_INFO "buffer fill callback, %d/%d\n", tape->cur_frames, tape->max_frames); +#endif + idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive)); + return ide_stopped; +} + +static void idetape_queue_onstream_buffer_fill (ide_drive_t *drive) +{ + idetape_pc_t *pc; + struct request *rq; + + pc = idetape_next_pc_storage (drive); + rq = idetape_next_rq_storage (drive); + idetape_create_mode_sense_cmd (pc, 0x33); + pc->callback = idetape_onstream_buffer_fill_callback; + idetape_queue_pc_head (drive, pc, rq); +} + +static void calculate_speeds(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + int full = 125, empty = 75; + + if (jiffies > tape->controlled_pipeline_head_time + 120 * HZ) { + tape->controlled_previous_pipeline_head = tape->controlled_last_pipeline_head; + tape->controlled_previous_head_time = tape->controlled_pipeline_head_time; + tape->controlled_last_pipeline_head = tape->pipeline_head; + tape->controlled_pipeline_head_time = jiffies; + } + if (jiffies > tape->controlled_pipeline_head_time + 60 * HZ) + tape->controlled_pipeline_head_speed = (tape->pipeline_head - tape->controlled_last_pipeline_head) * 32 * HZ / (jiffies - tape->controlled_pipeline_head_time); + else if (jiffies > tape->controlled_previous_head_time) + tape->controlled_pipeline_head_speed = (tape->pipeline_head - tape->controlled_previous_pipeline_head) * 32 * HZ / (jiffies - tape->controlled_previous_head_time); + + if (tape->nr_pending_stages < tape->max_stages /*- 1 */) { /* -1 for read mode error recovery */ + if (jiffies > tape->uncontrolled_previous_head_time + 10 * HZ) { + tape->uncontrolled_pipeline_head_time = jiffies; + tape->uncontrolled_pipeline_head_speed = (tape->pipeline_head - tape->uncontrolled_previous_pipeline_head) * 32 * HZ / (jiffies - tape->uncontrolled_previous_head_time); + } + } else { + tape->uncontrolled_previous_head_time = jiffies; + tape->uncontrolled_previous_pipeline_head = tape->pipeline_head; + if (jiffies > tape->uncontrolled_pipeline_head_time + 30 * HZ) { + tape->uncontrolled_pipeline_head_time = jiffies; + } + } + tape->pipeline_head_speed = IDE_MAX(tape->uncontrolled_pipeline_head_speed, tape->controlled_pipeline_head_speed); + if (tape->speed_control == 0) { + tape->max_insert_speed = 5000; + } else if (tape->speed_control == 1) { + if (tape->nr_pending_stages >= tape->max_stages / 2) + tape->max_insert_speed = tape->pipeline_head_speed + + (1100 - tape->pipeline_head_speed) * 2 * (tape->nr_pending_stages - tape->max_stages / 2) / tape->max_stages; + else + tape->max_insert_speed = 500 + + (tape->pipeline_head_speed - 500) * 2 * tape->nr_pending_stages / tape->max_stages; + if (tape->nr_pending_stages >= tape->max_stages * 99 / 100) + tape->max_insert_speed = 5000; + } else if (tape->speed_control == 2) { + tape->max_insert_speed = tape->pipeline_head_speed * empty / 100 + + (tape->pipeline_head_speed * full / 100 - tape->pipeline_head_speed * empty / 100) * tape->nr_pending_stages / tape->max_stages; + } else + tape->max_insert_speed = tape->speed_control; + tape->max_insert_speed = IDE_MAX(tape->max_insert_speed, 500); +} + static ide_startstop_t idetape_media_access_finished (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; idetape_pc_t *pc = tape->pc; idetape_status_reg_t status; + if (tape->onstream) + printk(KERN_INFO "ide-tape: bug: onstream, media_access_finished\n"); status.all = GET_STAT(); if (status.b.dsc) { if (status.b.check) { /* Error detected */ printk (KERN_ERR "ide-tape: %s: I/O error, ",tape->name); - return idetape_retry_pc (drive); /* Retry operation */ + return idetape_retry_pc (drive); /* Retry operation */ } pc->error = 0; if (tape->failed_pc == pc) @@ -2023,32 +2253,34 @@ return pc->callback (drive); } -/* - * General packet command callback function. - */ -static ide_startstop_t idetape_pc_callback (ide_drive_t *drive) -{ - idetape_tape_t *tape = drive->driver_data; - -#if IDETAPE_DEBUG_LOG - printk (KERN_INFO "ide-tape: Reached idetape_pc_callback\n"); -#endif /* IDETAPE_DEBUG_LOG */ - - idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive)); - return ide_stopped; -} - static ide_startstop_t idetape_rw_callback (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; struct request *rq = HWGROUP(drive)->rq; int blocks = tape->pc->actually_transferred / tape->tape_block_size; + tape->avg_size += blocks * tape->tape_block_size; + tape->insert_size += blocks * tape->tape_block_size; + if (tape->insert_size > 1024 * 1024) + tape->measure_insert_time = 1; + if (tape->measure_insert_time) { + tape->measure_insert_time = 0; + tape->insert_time = jiffies; + tape->insert_size = 0; + } + if (jiffies > tape->insert_time) + tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time); + if (jiffies - tape->avg_time >= HZ) { + tape->avg_speed = tape->avg_size * HZ / (jiffies - tape->avg_time) / 1024; + tape->avg_size = 0; + tape->avg_time = jiffies; + } + #if IDETAPE_DEBUG_LOG printk (KERN_INFO "ide-tape: Reached idetape_rw_callback\n"); #endif /* IDETAPE_DEBUG_LOG */ - tape->block_address += blocks; + tape->first_frame_position += blocks; rq->current_nr_sectors -= blocks; if (!tape->pc->error) @@ -2058,82 +2290,9 @@ return ide_stopped; } -static void idetape_create_locate_cmd (idetape_pc_t *pc, unsigned int block, byte partition) -{ - idetape_init_pc (pc); - pc->c[0] = IDETAPE_LOCATE_CMD; - pc->c[1] = 2; - put_unaligned (htonl (block), (unsigned int *) &pc->c[3]); - pc->c[8] = partition; - set_bit (PC_WAIT_FOR_DSC, &pc->flags); - pc->callback = &idetape_pc_callback; -} - -static void idetape_create_rewind_cmd (idetape_pc_t *pc) -{ - idetape_init_pc (pc); - pc->c[0] = IDETAPE_REWIND_CMD; - set_bit (PC_WAIT_FOR_DSC, &pc->flags); - pc->callback = &idetape_pc_callback; -} - -/* - * A mode sense command is used to "sense" tape parameters. - */ -static void idetape_create_mode_sense_cmd (idetape_pc_t *pc, byte page_code) -{ - idetape_init_pc (pc); - pc->c[0] = IDETAPE_MODE_SENSE_CMD; - pc->c[1] = 8; /* DBD = 1 - Don't return block descriptors for now */ - pc->c[2] = page_code; - pc->c[3] = 255; /* Don't limit the returned information */ - pc->c[4] = 255; /* (We will just discard data in that case) */ - if (page_code == IDETAPE_CAPABILITIES_PAGE) - pc->request_transfer = 24; -#if IDETAPE_DEBUG_BUGS - else - printk (KERN_ERR "ide-tape: unsupported page code in create_mode_sense_cmd\n"); -#endif /* IDETAPE_DEBUG_BUGS */ - pc->callback = &idetape_pc_callback; -} - -/* - * idetape_create_write_filemark_cmd will: - * - * 1. Write a filemark if write_filemark=1. - * 2. Flush the device buffers without writing a filemark - * if write_filemark=0. - * - */ -static void idetape_create_write_filemark_cmd (idetape_pc_t *pc,int write_filemark) -{ - idetape_init_pc (pc); - pc->c[0] = IDETAPE_WRITE_FILEMARK_CMD; - pc->c[4] = write_filemark; - set_bit (PC_WAIT_FOR_DSC, &pc->flags); - pc->callback = &idetape_pc_callback; -} - -static void idetape_create_load_unload_cmd (idetape_pc_t *pc,int cmd) -{ - idetape_init_pc (pc); - pc->c[0] = IDETAPE_LOAD_UNLOAD_CMD; - pc->c[4] = cmd; - set_bit (PC_WAIT_FOR_DSC, &pc->flags); - pc->callback = &idetape_pc_callback; -} - -static void idetape_create_erase_cmd (idetape_pc_t *pc) -{ - idetape_init_pc (pc); - pc->c[0] = IDETAPE_ERASE_CMD; - pc->c[1] = 1; - set_bit (PC_WAIT_FOR_DSC, &pc->flags); - pc->callback = &idetape_pc_callback; -} - -static void idetape_create_read_cmd (idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh) +static void idetape_create_read_cmd (idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh) { + struct buffer_head *p = bh; idetape_init_pc (pc); pc->c[0] = IDETAPE_READ_CMD; put_unaligned (htonl (length), (unsigned int *) &pc->c[1]); @@ -2142,77 +2301,76 @@ pc->bh = bh; atomic_set(&bh->b_count, 0); pc->buffer = NULL; - pc->request_transfer = pc->buffer_size = length * tape->tape_block_size; - if (pc->request_transfer == tape->stage_size) - set_bit (PC_DMA_RECOMMENDED, &pc->flags); + if (tape->onstream) { + while (p) { + atomic_set(&p->b_count, 0); + p = p->b_reqnext; + } + } + if (!tape->onstream) { + pc->request_transfer = pc->buffer_size = length * tape->tape_block_size; + if (pc->request_transfer == tape->stage_size) + set_bit (PC_DMA_RECOMMENDED, &pc->flags); + } else { + if (length) { + pc->request_transfer = pc->buffer_size = 32768 + 512; + set_bit (PC_DMA_RECOMMENDED, &pc->flags); + } else + pc->request_transfer = 0; + } } -static void idetape_create_space_cmd (idetape_pc_t *pc,int count,byte cmd) +static void idetape_create_read_buffer_cmd(idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh) { + int size = 32768; + + struct buffer_head *p = bh; idetape_init_pc (pc); - pc->c[0] = IDETAPE_SPACE_CMD; - put_unaligned (htonl (count), (unsigned int *) &pc->c[1]); - pc->c[1] = cmd; - set_bit (PC_WAIT_FOR_DSC, &pc->flags); + pc->c[0] = IDETAPE_READ_BUFFER_CMD; + pc->c[1] = IDETAPE_RETRIEVE_FAULTY_BLOCK; + pc->c[7] = size >> 8; + pc->c[8] = size & 0xff; pc->callback = &idetape_pc_callback; + pc->bh = bh; + atomic_set(&bh->b_count, 0); + pc->buffer = NULL; + while (p) { + atomic_set(&p->b_count, 0); + p = p->b_reqnext; + } + pc->request_transfer = pc->buffer_size = size; } static void idetape_create_write_cmd (idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh) { + struct buffer_head *p = bh; idetape_init_pc (pc); pc->c[0] = IDETAPE_WRITE_CMD; put_unaligned (htonl (length), (unsigned int *) &pc->c[1]); pc->c[1] = 1; pc->callback = &idetape_rw_callback; set_bit (PC_WRITING, &pc->flags); + if (tape->onstream) { + while (p) { + atomic_set(&p->b_count, p->b_size); + p = p->b_reqnext; + } + } pc->bh = bh; pc->b_data = bh->b_data; pc->b_count = atomic_read(&bh->b_count); pc->buffer = NULL; - pc->request_transfer = pc->buffer_size = length * tape->tape_block_size; - if (pc->request_transfer == tape->stage_size) - set_bit (PC_DMA_RECOMMENDED, &pc->flags); -} - -static ide_startstop_t idetape_read_position_callback (ide_drive_t *drive) -{ - idetape_tape_t *tape = drive->driver_data; - idetape_read_position_result_t *result; - -#if IDETAPE_DEBUG_LOG - printk (KERN_INFO "ide-tape: Reached idetape_read_position_callback\n"); -#endif /* IDETAPE_DEBUG_LOG */ - - if (!tape->pc->error) { - result = (idetape_read_position_result_t *) tape->pc->buffer; -#if IDETAPE_DEBUG_LOG - printk (KERN_INFO "BOP - %s\n",result->bop ? "Yes":"No"); - printk (KERN_INFO "EOP - %s\n",result->eop ? "Yes":"No"); -#endif /* IDETAPE_DEBUG_LOG */ - if (result->bpu) { - printk (KERN_INFO "ide-tape: Block location is unknown to the tape\n"); - clear_bit (IDETAPE_ADDRESS_VALID, &tape->flags); - idetape_end_request (0,HWGROUP (drive)); - } else { -#if IDETAPE_DEBUG_LOG - printk (KERN_INFO "Block Location - %lu\n", ntohl (result->first_block)); -#endif /* IDETAPE_DEBUG_LOG */ - tape->partition = result->partition; - tape->block_address = ntohl (result->first_block); - set_bit (IDETAPE_ADDRESS_VALID, &tape->flags); - idetape_end_request (1,HWGROUP (drive)); - } - } else - idetape_end_request (0,HWGROUP (drive)); - return ide_stopped; -} - -static void idetape_create_read_position_cmd (idetape_pc_t *pc) -{ - idetape_init_pc (pc); - pc->c[0] = IDETAPE_READ_POSITION_CMD; - pc->request_transfer = 20; - pc->callback = &idetape_read_position_callback; + if (!tape->onstream) { + pc->request_transfer = pc->buffer_size = length * tape->tape_block_size; + if (pc->request_transfer == tape->stage_size) + set_bit (PC_DMA_RECOMMENDED, &pc->flags); + } else { + if (length) { + pc->request_transfer = pc->buffer_size = 32768 + 512; + set_bit (PC_DMA_RECOMMENDED, &pc->flags); + } else + pc->request_transfer = 0; + } } /* @@ -2261,8 +2419,54 @@ * the other device meanwhile. */ status.all = GET_STAT(); + + /* + * The OnStream tape drive doesn't support DSC. Assume + * that DSC is always set. + */ + if (tape->onstream) + status.b.dsc = 1; if (!drive->dsc_overlap && rq->cmd != IDETAPE_PC_RQ2) set_bit (IDETAPE_IGNORE_DSC, &tape->flags); + + /* + * For the OnStream tape, check the current status of the tape + * internal buffer using data gathered from the buffer fill + * mode page, and postpone our request, effectively "disconnecting" + * from the IDE bus, in case the buffer is full (writing) or + * empty (reading), and there is a danger that our request will + * hold the IDE bus during actual media access. + */ + if (tape->tape_still_time > 100 && tape->tape_still_time < 200) + tape->measure_insert_time = 1; + if (tape->req_buffer_fill && (rq->cmd == IDETAPE_WRITE_RQ || rq->cmd == IDETAPE_READ_RQ)) { + tape->req_buffer_fill = 0; + tape->writes_since_buffer_fill = 0; + tape->reads_since_buffer_fill = 0; + tape->last_buffer_fill = jiffies; + idetape_queue_onstream_buffer_fill(drive); + if (jiffies > tape->insert_time) + tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time); + return ide_stopped; + } + if (jiffies > tape->insert_time) + tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time); + calculate_speeds(drive); + if (tape->onstream && tape->max_frames && + ((rq->cmd == IDETAPE_WRITE_RQ && (tape->cur_frames == tape->max_frames || (tape->speed_control && tape->cur_frames > 5 && (tape->insert_speed > tape->max_insert_speed || (0 /* tape->cur_frames > 30 && tape->tape_still_time > 200 */))))) || + (rq->cmd == IDETAPE_READ_RQ && (tape->cur_frames == 0 || (tape->speed_control && (tape->cur_frames < tape->max_frames - 5) && tape->insert_speed > tape->max_insert_speed)) && rq->nr_sectors))) { +#if IDETAPE_DEBUG_LOG + printk(KERN_INFO "postponing request, cmd %d, cur %d, max %d\n", + rq->cmd, tape->cur_frames, tape->max_frames); +#endif + if (tape->postpone_cnt++ < 500) { + status.b.dsc = 0; + tape->req_buffer_fill = 1; + } +#if ONSTREAM_DEBUG + else printk(KERN_INFO "ide-tape: %s: postpone_cnt %d\n", tape->name, tape->postpone_cnt); +#endif + } if (!test_and_clear_bit (IDETAPE_IGNORE_DSC, &tape->flags) && !status.b.dsc) { if (postponed_rq == NULL) { tape->dsc_polling_start = jiffies; @@ -2283,17 +2487,53 @@ } switch (rq->cmd) { case IDETAPE_READ_RQ: + tape->buffer_head++; +#if USE_IOTRACE + IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor); +#endif + tape->postpone_cnt = 0; + tape->reads_since_buffer_fill++; + if (tape->onstream) { + if (tape->cur_frames - tape->reads_since_buffer_fill <= 0) + tape->req_buffer_fill = 1; + if (jiffies > tape->last_buffer_fill + 5 * HZ / 100) + tape->req_buffer_fill = 1; + } pc=idetape_next_pc_storage (drive); idetape_create_read_cmd (tape, pc, rq->current_nr_sectors, rq->bh); break; case IDETAPE_WRITE_RQ: + tape->buffer_head++; +#if USE_IOTRACE + IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor); +#endif + tape->postpone_cnt = 0; + tape->writes_since_buffer_fill++; + if (tape->onstream) { + if (tape->cur_frames + tape->writes_since_buffer_fill >= tape->max_frames) + tape->req_buffer_fill = 1; + if (jiffies > tape->last_buffer_fill + 5 * HZ / 100) + tape->req_buffer_fill = 1; + calculate_speeds(drive); + } pc=idetape_next_pc_storage (drive); idetape_create_write_cmd (tape, pc, rq->current_nr_sectors, rq->bh); break; + case IDETAPE_READ_BUFFER_RQ: + tape->postpone_cnt = 0; + pc=idetape_next_pc_storage (drive); + idetape_create_read_buffer_cmd (tape, pc, rq->current_nr_sectors, rq->bh); + break; case IDETAPE_ABORTED_WRITE_RQ: rq->cmd = IDETAPE_WRITE_RQ; - rq->errors = IDETAPE_ERROR_EOD; - idetape_end_request (1, HWGROUP(drive)); + idetape_end_request (IDETAPE_ERROR_EOD, HWGROUP(drive)); + return ide_stopped; + case IDETAPE_ABORTED_READ_RQ: +#if IDETAPE_DEBUG_LOG + printk(KERN_INFO "ide-tape: %s: detected aborted read rq\n", tape->name); +#endif + rq->cmd = IDETAPE_READ_RQ; + idetape_end_request (IDETAPE_ERROR_EOD, HWGROUP(drive)); return ide_stopped; case IDETAPE_PC_RQ1: pc=(idetape_pc_t *) rq->buffer; @@ -2311,153 +2551,889 @@ } /* - * idetape_queue_pc_tail is based on the following functions: - * - * ide_do_drive_cmd from ide.c - * cdrom_queue_request and cdrom_queue_packet_command from ide-cd.c - * - * We add a special packet command request to the tail of the request queue, - * and wait for it to be serviced. - * - * This is not to be called from within the request handling part - * of the driver ! We allocate here data in the stack, and it is valid - * until the request is finished. This is not the case for the bottom - * part of the driver, where we are always leaving the functions to wait - * for an interrupt or a timer event. - * - * From the bottom part of the driver, we should allocate safe memory - * using idetape_next_pc_storage and idetape_next_rq_storage, and add - * the request to the request list without waiting for it to be serviced ! - * In that case, we usually use idetape_queue_pc_head. + * Pipeline related functions */ -static int idetape_queue_pc_tail (ide_drive_t *drive,idetape_pc_t *pc) +static inline int idetape_pipeline_active (idetape_tape_t *tape) { - struct request rq; + int rc1, rc2; - ide_init_drive_cmd (&rq); - rq.buffer = (char *) pc; - rq.cmd = IDETAPE_PC_RQ1; - return ide_do_drive_cmd (drive, &rq, ide_wait); + rc1 = test_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags); + rc2 = (tape->active_data_request != NULL); + return rc1; } /* - * idetape_wait_for_request installs a semaphore in a pending request - * and sleeps until it is serviced. + * idetape_kmalloc_stage uses __get_free_page to allocate a pipeline + * stage, along with all the necessary small buffers which together make + * a buffer of size tape->stage_size (or a bit more). We attempt to + * combine sequential pages as much as possible. * - * The caller should ensure that the request will not be serviced - * before we install the semaphore (usually by disabling interrupts). + * Returns a pointer to the new allocated stage, or NULL if we + * can't (or don't want to) allocate a stage. + * + * Pipeline stages are optional and are used to increase performance. + * If we can't allocate them, we'll manage without them. */ -static void idetape_wait_for_request (ide_drive_t *drive, struct request *rq) +static idetape_stage_t *__idetape_kmalloc_stage (idetape_tape_t *tape, int full, int clear) { - DECLARE_MUTEX_LOCKED(sem); - idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage; + struct buffer_head *prev_bh, *bh; + int pages = tape->pages_per_stage; + char *b_data; -#if IDETAPE_DEBUG_BUGS - if (rq == NULL || !IDETAPE_RQ_CMD (rq->cmd)) { - printk (KERN_ERR "ide-tape: bug: Trying to sleep on non-valid request\n"); - return; + if ((stage = (idetape_stage_t *) kmalloc (sizeof (idetape_stage_t),GFP_KERNEL)) == NULL) + return NULL; + stage->next = NULL; + + bh = stage->bh = (struct buffer_head *) kmalloc (sizeof (struct buffer_head), GFP_KERNEL); + if (bh == NULL) + goto abort; + bh->b_reqnext = NULL; + if ((bh->b_data = (char *) __get_free_page (GFP_KERNEL)) == NULL) + goto abort; + if (clear) + memset(bh->b_data, 0, PAGE_SIZE); + bh->b_size = PAGE_SIZE; + atomic_set(&bh->b_count, full ? bh->b_size : 0); + set_bit (BH_Lock, &bh->b_state); + + while (--pages) { + if ((b_data = (char *) __get_free_page (GFP_KERNEL)) == NULL) + goto abort; + if (clear) + memset(b_data, 0, PAGE_SIZE); + if (bh->b_data == b_data + PAGE_SIZE && virt_to_bus (bh->b_data) == virt_to_bus (b_data) + PAGE_SIZE) { + bh->b_size += PAGE_SIZE; + bh->b_data -= PAGE_SIZE; + if (full) + atomic_add(PAGE_SIZE, &bh->b_count); + continue; + } + if (b_data == bh->b_data + bh->b_size && virt_to_bus (b_data) == virt_to_bus (bh->b_data) + bh->b_size) { + bh->b_size += PAGE_SIZE; + if (full) + atomic_add(PAGE_SIZE, &bh->b_count); + continue; + } + prev_bh = bh; + if ((bh = (struct buffer_head *) kmalloc (sizeof (struct buffer_head), GFP_KERNEL)) == NULL) { + free_page ((unsigned long) b_data); + goto abort; + } + bh->b_reqnext = NULL; + bh->b_data = b_data; + bh->b_size = PAGE_SIZE; + atomic_set(&bh->b_count, full ? bh->b_size : 0); + set_bit (BH_Lock, &bh->b_state); + prev_bh->b_reqnext = bh; } -#endif /* IDETAPE_DEBUG_BUGS */ - rq->sem = &sem; - spin_unlock(&tape->spinlock); - down(&sem); - spin_lock_irq(&tape->spinlock); + bh->b_size -= tape->excess_bh_size; + if (full) + atomic_sub(tape->excess_bh_size, &bh->b_count); + if (tape->onstream) + stage->aux = (os_aux_t *) (bh->b_data + bh->b_size - OS_AUX_SIZE); + return stage; +abort: + __idetape_kfree_stage (stage); + return NULL; } -/* - * idetape_queue_rw_tail generates a read/write request for the block - * device interface and wait for it to be serviced. - */ -static int idetape_queue_rw_tail (ide_drive_t *drive, int cmd, int blocks, struct buffer_head *bh) +static idetape_stage_t *idetape_kmalloc_stage (idetape_tape_t *tape) { - idetape_tape_t *tape = drive->driver_data; - struct request rq; + idetape_stage_t *cache_stage = tape->cache_stage; #if IDETAPE_DEBUG_LOG - printk (KERN_INFO "idetape_queue_rw_tail: cmd=%d\n",cmd); + printk (KERN_INFO "Reached idetape_kmalloc_stage\n"); #endif /* IDETAPE_DEBUG_LOG */ -#if IDETAPE_DEBUG_BUGS - if (idetape_pipeline_active (tape)) { - printk (KERN_ERR "ide-tape: bug: the pipeline is active in idetape_queue_rw_tail\n"); - return (0); - } -#endif /* IDETAPE_DEBUG_BUGS */ - ide_init_drive_cmd (&rq); - rq.bh = bh; - rq.cmd = cmd; - rq.sector = tape->block_address; + if (tape->nr_stages >= tape->max_stages) + return NULL; + if (cache_stage != NULL) { + tape->cache_stage = NULL; + return cache_stage; + } + return __idetape_kmalloc_stage (tape, 0, 0); +} + +static void idetape_copy_stage_from_user (idetape_tape_t *tape, idetape_stage_t *stage, const char *buf, int n) +{ + struct buffer_head *bh = tape->bh; + int count; + + while (n) { +#if IDETAPE_DEBUG_BUGS + if (bh == NULL) { + printk (KERN_ERR "ide-tape: bh == NULL in idetape_copy_stage_from_user\n"); + return; + } +#endif /* IDETAPE_DEBUG_BUGS */ + count = IDE_MIN (bh->b_size - atomic_read(&bh->b_count), n); + copy_from_user (bh->b_data + atomic_read(&bh->b_count), buf, count); + n -= count; atomic_add(count, &bh->b_count); buf += count; + if (atomic_read(&bh->b_count) == bh->b_size) { + bh = bh->b_reqnext; + if (bh) + atomic_set(&bh->b_count, 0); + } + } + tape->bh = bh; +} + +static void idetape_copy_stage_to_user (idetape_tape_t *tape, char *buf, idetape_stage_t *stage, int n) +{ + struct buffer_head *bh = tape->bh; + int count; + + while (n) { +#if IDETAPE_DEBUG_BUGS + if (bh == NULL) { + printk (KERN_ERR "ide-tape: bh == NULL in idetape_copy_stage_to_user\n"); + return; + } +#endif /* IDETAPE_DEBUG_BUGS */ + count = IDE_MIN (tape->b_count, n); + copy_to_user (buf, tape->b_data, count); + n -= count; tape->b_data += count; tape->b_count -= count; buf += count; + if (!tape->b_count) { + tape->bh = bh = bh->b_reqnext; + if (bh) { + tape->b_data = bh->b_data; + tape->b_count = atomic_read(&bh->b_count); + } + } + } +} + +static void idetape_init_merge_stage (idetape_tape_t *tape) +{ + struct buffer_head *bh = tape->merge_stage->bh; + + tape->bh = bh; + if (tape->chrdev_direction == idetape_direction_write) + atomic_set(&bh->b_count, 0); + else { + tape->b_data = bh->b_data; + tape->b_count = atomic_read(&bh->b_count); + } +} + +static void idetape_switch_buffers (idetape_tape_t *tape, idetape_stage_t *stage) +{ + struct buffer_head *tmp; + os_aux_t *tmp_aux; + + tmp = stage->bh; tmp_aux = stage->aux; + stage->bh = tape->merge_stage->bh; stage->aux = tape->merge_stage->aux; + tape->merge_stage->bh = tmp; tape->merge_stage->aux = tmp_aux; + idetape_init_merge_stage (tape); +} + +/* + * idetape_add_stage_tail adds a new stage at the end of the pipeline. + */ +static void idetape_add_stage_tail (ide_drive_t *drive,idetape_stage_t *stage) +{ + idetape_tape_t *tape = drive->driver_data; + unsigned long flags; + +#if IDETAPE_DEBUG_LOG + printk (KERN_INFO "Reached idetape_add_stage_tail\n"); +#endif /* IDETAPE_DEBUG_LOG */ + spin_lock_irqsave(&tape->spinlock, flags); + stage->next=NULL; + if (tape->last_stage != NULL) + tape->last_stage->next=stage; + else + tape->first_stage=tape->next_stage=stage; + tape->last_stage=stage; + if (tape->next_stage == NULL) + tape->next_stage=tape->last_stage; + tape->nr_stages++; + tape->nr_pending_stages++; + spin_unlock_irqrestore(&tape->spinlock, flags); +} + +/* + * Initialize the OnStream AUX + */ +static void idetape_init_stage(ide_drive_t *drive, idetape_stage_t *stage, int frame_type, int logical_blk_num) +{ + idetape_tape_t *tape = drive->driver_data; + os_aux_t *aux = stage->aux; + os_partition_t *par = &aux->partition; + os_dat_t *dat = &aux->dat; + + if (!tape->onstream || tape->raw) + return; + memset(aux, 0, sizeof(*aux)); + aux->format_id = htonl(0); + memcpy(aux->application_sig, "LIN3", 4); + aux->hdwr = htonl(0); + aux->frame_type = frame_type; + + if (frame_type == OS_FRAME_TYPE_HEADER) { + aux->update_frame_cntr = htonl(tape->update_frame_cntr); + par->partition_num = OS_CONFIG_PARTITION; + par->par_desc_ver = OS_PARTITION_VERSION; + par->wrt_pass_cntr = htons(0xffff); + par->first_frame_addr = htonl(0); + par->last_frame_addr = htonl(0xbb7); + } else { + aux->update_frame_cntr = htonl(0); + par->partition_num = OS_DATA_PARTITION; + par->par_desc_ver = OS_PARTITION_VERSION; + par->wrt_pass_cntr = htons(tape->wrt_pass_cntr); + par->first_frame_addr = htonl(0x14); + par->last_frame_addr = htonl(19239 * 24); + } + if (frame_type != OS_FRAME_TYPE_HEADER) { + aux->frame_seq_num = htonl(logical_blk_num); + aux->logical_blk_num_high = htonl(0); + aux->logical_blk_num = htonl(logical_blk_num); + } else { + aux->frame_seq_num = htonl(0); + aux->logical_blk_num_high = htonl(0); + aux->logical_blk_num = htonl(0); + } + + if (frame_type != OS_FRAME_TYPE_HEADER) { + dat->dat_sz = 8; + dat->reserved1 = 0; + dat->entry_cnt = 1; + dat->reserved3 = 0; + if (frame_type == OS_FRAME_TYPE_DATA) + dat->dat_list[0].blk_sz = htonl(32 * 1024); + else + dat->dat_list[0].blk_sz = 0; + dat->dat_list[0].blk_cnt = htons(1); + if (frame_type == OS_FRAME_TYPE_MARKER) + dat->dat_list[0].flags = OS_DAT_FLAGS_MARK; + else + dat->dat_list[0].flags = OS_DAT_FLAGS_DATA; + dat->dat_list[0].reserved = 0; + } else + aux->next_mark_addr = htonl(tape->first_mark_addr); + aux->filemark_cnt = ntohl(tape->filemark_cnt); + aux->phys_fm = ntohl(0xffffffff); + aux->last_mark_addr = ntohl(tape->last_mark_addr); +} + +/* + * idetape_wait_for_request installs a semaphore in a pending request + * and sleeps until it is serviced. + * + * The caller should ensure that the request will not be serviced + * before we install the semaphore (usually by disabling interrupts). + */ +static void idetape_wait_for_request (ide_drive_t *drive, struct request *rq) +{ + DECLARE_MUTEX_LOCKED(sem); + idetape_tape_t *tape = drive->driver_data; + +#if IDETAPE_DEBUG_BUGS + if (rq == NULL || !IDETAPE_RQ_CMD (rq->cmd)) { + printk (KERN_ERR "ide-tape: bug: Trying to sleep on non-valid request\n"); + return; + } +#endif /* IDETAPE_DEBUG_BUGS */ + rq->sem = &sem; + tape->sem = &sem; + spin_unlock(&tape->spinlock); + down(&sem); + rq->sem = NULL; + tape->sem = NULL; + spin_lock_irq(&tape->spinlock); +} + +static ide_startstop_t idetape_read_position_callback (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_read_position_result_t *result; + +#if IDETAPE_DEBUG_LOG + printk (KERN_INFO "ide-tape: Reached idetape_read_position_callback\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + if (!tape->pc->error) { + result = (idetape_read_position_result_t *) tape->pc->buffer; +#if IDETAPE_DEBUG_LOG + printk (KERN_INFO "BOP - %s\n",result->bop ? "Yes":"No"); + printk (KERN_INFO "EOP - %s\n",result->eop ? "Yes":"No"); +#endif /* IDETAPE_DEBUG_LOG */ + if (result->bpu) { + printk (KERN_INFO "ide-tape: Block location is unknown to the tape\n"); + clear_bit (IDETAPE_ADDRESS_VALID, &tape->flags); + idetape_end_request (0,HWGROUP (drive)); + } else { +#if IDETAPE_DEBUG_LOG + printk (KERN_INFO "Block Location - %lu\n", ntohl (result->first_block)); +#endif /* IDETAPE_DEBUG_LOG */ + tape->partition = result->partition; + tape->first_frame_position = ntohl (result->first_block); + tape->last_frame_position = ntohl (result->last_block); + tape->blocks_in_buffer = result->blocks_in_buffer[2]; + set_bit (IDETAPE_ADDRESS_VALID, &tape->flags); + idetape_end_request (1,HWGROUP (drive)); + } + } else { + idetape_end_request (0,HWGROUP (drive)); + } + return ide_stopped; +} + +/* + * idetape_create_write_filemark_cmd will: + * + * 1. Write a filemark if write_filemark=1. + * 2. Flush the device buffers without writing a filemark + * if write_filemark=0. + * + */ +static void idetape_create_write_filemark_cmd (ide_drive_t *drive, idetape_pc_t *pc,int write_filemark) +{ + idetape_tape_t *tape = drive->driver_data; + + idetape_init_pc (pc); + pc->c[0] = IDETAPE_WRITE_FILEMARK_CMD; + if (tape->onstream) + pc->c[1] = 1; + pc->c[4] = write_filemark; + set_bit (PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +static void idetape_create_test_unit_ready_cmd(idetape_pc_t *pc) +{ + idetape_init_pc(pc); + pc->c[0] = IDETAPE_TEST_UNIT_READY_CMD; + pc->callback = &idetape_pc_callback; +} + +/* + * idetape_queue_pc_tail is based on the following functions: + * + * ide_do_drive_cmd from ide.c + * cdrom_queue_request and cdrom_queue_packet_command from ide-cd.c + * + * We add a special packet command request to the tail of the request queue, + * and wait for it to be serviced. + * + * This is not to be called from within the request handling part + * of the driver ! We allocate here data in the stack, and it is valid + * until the request is finished. This is not the case for the bottom + * part of the driver, where we are always leaving the functions to wait + * for an interrupt or a timer event. + * + * From the bottom part of the driver, we should allocate safe memory + * using idetape_next_pc_storage and idetape_next_rq_storage, and add + * the request to the request list without waiting for it to be serviced ! + * In that case, we usually use idetape_queue_pc_head. + */ +static int __idetape_queue_pc_tail (ide_drive_t *drive,idetape_pc_t *pc) +{ + struct request rq; + + ide_init_drive_cmd (&rq); + rq.buffer = (char *) pc; + rq.cmd = IDETAPE_PC_RQ1; + return ide_do_drive_cmd (drive, &rq, ide_wait); +} + +static void idetape_create_load_unload_cmd (ide_drive_t *drive, idetape_pc_t *pc,int cmd) +{ + idetape_tape_t *tape = drive->driver_data; + + idetape_init_pc (pc); + pc->c[0] = IDETAPE_LOAD_UNLOAD_CMD; + pc->c[4] = cmd; + if (tape->onstream) + pc->c[1] = 1; + set_bit (PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +static int idetape_wait_ready(ide_drive_t *drive, unsigned long long timeout) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + + /* + * Wait for the tape to become ready + */ + timeout += jiffies; + while (jiffies < timeout) { + idetape_create_test_unit_ready_cmd(&pc); + if (!__idetape_queue_pc_tail(drive, &pc)) + return 0; + if (tape->sense_key == 2 && tape->asc == 4 && tape->ascq == 2) { + idetape_create_load_unload_cmd (drive, &pc, IDETAPE_LU_LOAD_MASK); + __idetape_queue_pc_tail(drive,&pc); + idetape_create_test_unit_ready_cmd(&pc); + if (!__idetape_queue_pc_tail(drive, &pc)) + return 0; + } + if (!(tape->sense_key == 2 && tape->asc == 4 && (tape->ascq == 1 || tape->ascq == 8))) + break; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ / 10); + } + return -EIO; +} + +static int idetape_queue_pc_tail (ide_drive_t *drive,idetape_pc_t *pc) +{ + idetape_tape_t *tape = drive->driver_data; + int rc; + + rc = __idetape_queue_pc_tail(drive, pc); + if (rc) return rc; + if (tape->onstream && test_bit(PC_WAIT_FOR_DSC, &pc->flags)) + rc = idetape_wait_ready(drive, 60 * 5 * HZ); + return rc; +} + +static int idetape_flush_tape_buffers (ide_drive_t *drive) +{ + idetape_pc_t pc; + int rc; + + idetape_create_write_filemark_cmd(drive, &pc, 0); + if ((rc = idetape_queue_pc_tail (drive,&pc))) + return rc; + idetape_wait_ready(drive, 60 * 5 * HZ); + return 0; +} + +static void idetape_create_read_position_cmd (idetape_pc_t *pc) +{ + idetape_init_pc (pc); + pc->c[0] = IDETAPE_READ_POSITION_CMD; + pc->request_transfer = 20; + pc->callback = &idetape_read_position_callback; +} + +static int idetape_read_position(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + int position; + + idetape_flush_tape_buffers(drive); + idetape_create_read_position_cmd(&pc); + if (idetape_queue_pc_tail (drive,&pc)) + return -1; + position = tape->first_frame_position; + if (tape->onstream) { + if ((position != tape->last_frame_position - tape->blocks_in_buffer) && + (position != tape->last_frame_position + tape->blocks_in_buffer)) { + if (tape->blocks_in_buffer == 0) { + printk("ide-tape: %s: correcting read position %d, %d, %d\n", tape->name, position, tape->last_frame_position, tape->blocks_in_buffer); + position = tape->last_frame_position; + tape->first_frame_position = position; + } + } + } + return position; +} + +static void idetape_create_locate_cmd (ide_drive_t *drive, idetape_pc_t *pc, unsigned int block, byte partition, int skip) +{ + idetape_tape_t *tape = drive->driver_data; + + idetape_init_pc (pc); + pc->c[0] = IDETAPE_LOCATE_CMD; + if (tape->onstream) + pc->c[1] = 1; + else + pc->c[1] = 2; + put_unaligned (htonl (block), (unsigned int *) &pc->c[3]); + pc->c[8] = partition; + if (tape->onstream) + pc->c[9] = skip << 7; + set_bit (PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +static void idetape_create_prevent_cmd(ide_drive_t *drive, idetape_pc_t *pc, int prevent) +{ + idetape_init_pc(pc); + pc->c[0] = IDETAPE_PREVENT_CMD; + pc->c[4] = prevent; + pc->callback = &idetape_pc_callback; +} + +static int __idetape_discard_read_pipeline (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + unsigned long flags; + int cnt; + + if (tape->chrdev_direction != idetape_direction_read) + return 0; + tape->merge_stage_size = 0; + if (tape->merge_stage != NULL) { + __idetape_kfree_stage (tape->merge_stage); + tape->merge_stage = NULL; + } + tape->chrdev_direction = idetape_direction_none; + + if (tape->first_stage == NULL) + return 0; + + spin_lock_irqsave(&tape->spinlock, flags); + tape->next_stage = NULL; + if (idetape_pipeline_active (tape)) + idetape_wait_for_request(drive, tape->active_data_request); + spin_unlock_irqrestore(&tape->spinlock, flags); + + cnt = tape->nr_stages - tape->nr_pending_stages; + while (tape->first_stage != NULL) + idetape_remove_stage_head (drive); + tape->nr_pending_stages = 0; + tape->max_stages = tape->min_pipeline; + return cnt; +} + +/* + * idetape_position_tape positions the tape to the requested block + * using the LOCATE packet command. A READ POSITION command is then + * issued to check where we are positioned. + * + * Like all higher level operations, we queue the commands at the tail + * of the request queue and wait for their completion. + * + */ +static int idetape_position_tape (ide_drive_t *drive, unsigned int block, byte partition, int skip) +{ + idetape_tape_t *tape = drive->driver_data; + int retval; + idetape_pc_t pc; + + if (tape->chrdev_direction == idetape_direction_read) + __idetape_discard_read_pipeline(drive); + idetape_wait_ready(drive, 60 * 5 * HZ); + idetape_create_locate_cmd (drive, &pc, block, partition, skip); + retval=idetape_queue_pc_tail (drive,&pc); + if (retval) return (retval); + + idetape_create_read_position_cmd (&pc); + return (idetape_queue_pc_tail (drive,&pc)); +} + +static void idetape_discard_read_pipeline (ide_drive_t *drive, int restore_position) +{ + idetape_tape_t *tape = drive->driver_data; + int cnt; + int seek, position; + + cnt = __idetape_discard_read_pipeline(drive); + if (restore_position) { + position = idetape_read_position(drive); +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: address %u, nr_stages %d\n", position, cnt); +#endif + seek = position > cnt ? position - cnt : 0; + if (idetape_position_tape(drive, seek, 0, 0)) { + printk(KERN_INFO "ide-tape: %s: position_tape failed in discard_pipeline()\n", tape->name); + return; + } + } +} + +static void idetape_update_stats(ide_drive_t *drive) +{ + idetape_pc_t pc; + + idetape_create_mode_sense_cmd (&pc, 0x33); + pc.callback = idetape_onstream_buffer_fill_callback; + (void) idetape_queue_pc_tail(drive, &pc); +} + +/* + * idetape_queue_rw_tail generates a read/write request for the block + * device interface and wait for it to be serviced. + */ +static int idetape_queue_rw_tail (ide_drive_t *drive, int cmd, int blocks, struct buffer_head *bh) +{ + idetape_tape_t *tape = drive->driver_data; + struct request rq; + +#if IDETAPE_DEBUG_LOG + printk (KERN_INFO "idetape_queue_rw_tail: cmd=%d\n",cmd); +#endif /* IDETAPE_DEBUG_LOG */ +#if IDETAPE_DEBUG_BUGS + if (idetape_pipeline_active (tape)) { + printk (KERN_ERR "ide-tape: bug: the pipeline is active in idetape_queue_rw_tail\n"); + return (0); + } +#endif /* IDETAPE_DEBUG_BUGS */ + + ide_init_drive_cmd (&rq); + rq.bh = bh; + rq.cmd = cmd; + rq.sector = tape->first_frame_position; rq.nr_sectors = rq.current_nr_sectors = blocks; + if (tape->onstream) + tape->postpone_cnt = 600; (void) ide_do_drive_cmd (drive, &rq, ide_wait); - idetape_init_merge_stage (tape); - if (rq.errors == IDETAPE_ERROR_GENERAL) - return -EIO; - return (tape->tape_block_size * (blocks-rq.current_nr_sectors)); + if (cmd != IDETAPE_READ_RQ && cmd != IDETAPE_WRITE_RQ) + return 0; + + if (tape->merge_stage) + idetape_init_merge_stage (tape); + if (rq.errors == IDETAPE_ERROR_GENERAL) + return -EIO; + return (tape->tape_block_size * (blocks-rq.current_nr_sectors)); +} + +/* + * Read back the drive's internal buffer contents, as a part + * of the write error recovery mechanism for old OnStream + * firmware revisions. + */ +static void idetape_onstream_read_back_buffer(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + int frames, i, logical_blk_num; + idetape_stage_t *stage, *first = NULL, *last = NULL; + os_aux_t *aux; + struct request *rq; + unsigned char *p; + unsigned long flags; + + idetape_update_stats(drive); + frames = tape->cur_frames; + logical_blk_num = ntohl(tape->first_stage->aux->logical_blk_num) - frames; + printk(KERN_INFO "ide-tape: %s: reading back %d frames from the drive's internal buffer\n", tape->name, frames); + for (i = 0; i < frames; i++) { + stage = __idetape_kmalloc_stage(tape, 0, 0); + if (!first) + first = stage; + aux = stage->aux; + p = stage->bh->b_data; + idetape_queue_rw_tail(drive, IDETAPE_READ_BUFFER_RQ, tape->capabilities.ctl, stage->bh); +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: read back logical block %d, data %x %x %x %x\n", tape->name, logical_blk_num, *p++, *p++, *p++, *p++); +#endif + rq = &stage->rq; + ide_init_drive_cmd (rq); + rq->cmd = IDETAPE_WRITE_RQ; + rq->sector = tape->first_frame_position; + rq->nr_sectors = rq->current_nr_sectors = tape->capabilities.ctl; + idetape_init_stage(drive, stage, OS_FRAME_TYPE_DATA, logical_blk_num++); + stage->next = NULL; + if (last) + last->next = stage; + last = stage; + } + if (frames) { + spin_lock_irqsave(&tape->spinlock, flags); + last->next = tape->first_stage; + tape->next_stage = tape->first_stage = first; + tape->nr_stages += frames; + tape->nr_pending_stages += frames; + spin_unlock_irqrestore(&tape->spinlock, flags); + } + idetape_update_stats(drive); +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: frames left in buffer: %d\n", tape->name, tape->cur_frames); +#endif +} + +/* + * Error recovery algorithm for the OnStream tape. + */ +static void idetape_onstream_write_error_recovery(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + unsigned int block; + + if (tape->onstream_write_error == 1) { + printk(KERN_ERR "ide-tape: %s: detected physical bad block at %lu\n", tape->name, ntohl(tape->sense.information)); + block = ntohl(tape->sense.information) + 80; + idetape_update_stats(drive); + printk(KERN_ERR "ide-tape: %s: relocating %d buffered logical blocks to physical block %u\n", tape->name, tape->cur_frames, block); + idetape_update_stats(drive); + if (tape->firmware_revision_num >= 106) + idetape_position_tape(drive, block, 0, 1); + else { + idetape_onstream_read_back_buffer(drive); + idetape_position_tape(drive, block, 0, 0); + } + idetape_read_position(drive); +#if ONSTREAM_DEBUG + printk(KERN_ERR "ide-tape: %s: positioning complete, cur_frames %d, pos %d, tape pos %d\n", tape->name, tape->cur_frames, tape->first_frame_position, tape->last_frame_position); +#endif + } else if (tape->onstream_write_error == 2) { +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: skipping over config partition\n", tape->name); +#endif + idetape_flush_tape_buffers(drive); + block = idetape_read_position(drive); + if (block != 0xba4) + printk(KERN_ERR "ide-tape: warning, current position %d, expected %d\n", block, 0xba4); + idetape_position_tape(drive, 0xbb8, 0, 0); + } + tape->onstream_write_error = 0; +} + +/* + * idetape_insert_pipeline_into_queue is used to start servicing the + * pipeline stages, starting from tape->next_stage. + */ +static void idetape_insert_pipeline_into_queue (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + if (tape->next_stage == NULL) + return; + if (!idetape_pipeline_active (tape)) { + if (tape->onstream_write_error) + idetape_onstream_write_error_recovery(drive); + set_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags); + idetape_active_next_stage (drive); + (void) ide_do_drive_cmd (drive, tape->active_data_request, ide_end); + } +} + +static void idetape_create_inquiry_cmd(idetape_pc_t *pc) +{ + idetape_init_pc(pc); + pc->c[0] = IDETAPE_INQUIRY_CMD; + pc->c[4] = pc->request_transfer = 254; + pc->callback = &idetape_pc_callback; +} + +static void idetape_create_rewind_cmd (ide_drive_t *drive, idetape_pc_t *pc) +{ + idetape_tape_t *tape = drive->driver_data; + + idetape_init_pc (pc); + pc->c[0] = IDETAPE_REWIND_CMD; + if (tape->onstream) + pc->c[1] = 1; + set_bit (PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +static void idetape_create_mode_select_cmd (idetape_pc_t *pc, int length) +{ + idetape_init_pc (pc); + set_bit (PC_WRITING, &pc->flags); + pc->c[0] = IDETAPE_MODE_SELECT_CMD; + pc->c[1] = 0x10; + put_unaligned (htons(length), (unsigned short *) &pc->c[3]); + pc->request_transfer = 255; + pc->callback = &idetape_pc_callback; +} + +static void idetape_create_erase_cmd (idetape_pc_t *pc) +{ + idetape_init_pc (pc); + pc->c[0] = IDETAPE_ERASE_CMD; + pc->c[1] = 1; + set_bit (PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +static void idetape_create_space_cmd (idetape_pc_t *pc,int count,byte cmd) +{ + idetape_init_pc (pc); + pc->c[0] = IDETAPE_SPACE_CMD; + put_unaligned (htonl (count), (unsigned int *) &pc->c[1]); + pc->c[1] = cmd; + set_bit (PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; } /* - * idetape_add_chrdev_read_request is called from idetape_chrdev_read - * to service a character device read request and add read-ahead - * requests to our pipeline. + * Verify that we have the correct tape frame */ -static int idetape_add_chrdev_read_request (ide_drive_t *drive,int blocks) +static int idetape_verify_stage(ide_drive_t *drive, idetape_stage_t *stage, int logical_blk_num, int quiet) { idetape_tape_t *tape = drive->driver_data; - idetape_stage_t *new_stage; - unsigned long flags; - struct request rq,*rq_ptr; - int bytes_read; - -#if IDETAPE_DEBUG_LOG - printk (KERN_INFO "Reached idetape_add_chrdev_read_request\n"); -#endif /* IDETAPE_DEBUG_LOG */ - - ide_init_drive_cmd (&rq); - rq.cmd = IDETAPE_READ_RQ; - rq.sector = tape->block_address; - rq.nr_sectors = rq.current_nr_sectors = blocks; + os_aux_t *aux = stage->aux; + os_partition_t *par = &aux->partition; + struct request *rq = &stage->rq; + struct buffer_head *bh; - if (idetape_pipeline_active (tape) || tape->nr_stages <= tape->max_stages / 4) { - new_stage=idetape_kmalloc_stage (tape); - while (new_stage != NULL) { - new_stage->rq=rq; - idetape_add_stage_tail (drive,new_stage); - new_stage=idetape_kmalloc_stage (tape); + if (!tape->onstream) + return 1; + if (tape->raw) { + if (rq->errors) { + bh = stage->bh; + while (bh) { + memset(bh->b_data, 0, bh->b_size); + bh = bh->b_reqnext; + } + strcpy(stage->bh->b_data, "READ ERROR ON FRAME"); } - if (!idetape_pipeline_active (tape)) - idetape_insert_pipeline_into_queue (drive); + return 1; } - if (tape->first_stage == NULL) { - /* - * Linux is short on memory. Revert to non-pipelined - * operation mode for this request. - */ - return (idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, blocks, tape->merge_stage->bh)); + if (rq->errors == IDETAPE_ERROR_GENERAL) { + printk(KERN_INFO "ide-tape: %s: skipping frame, read error\n", tape->name); + return 0; + } + if (rq->errors == IDETAPE_ERROR_EOD) { + printk(KERN_INFO "ide-tape: %s: skipping frame, eod\n", tape->name); + return 0; + } + if (ntohl(aux->format_id) != 0) { + printk(KERN_INFO "ide-tape: %s: skipping frame, format_id %lu\n", tape->name, ntohl(aux->format_id)); + return 0; + } + if (memcmp(aux->application_sig, tape->application_sig, 4) != 0) { + printk(KERN_INFO "ide-tape: %s: skipping frame, incorrect application signature\n", tape->name); + return 0; + } + if (aux->frame_type != OS_FRAME_TYPE_DATA && + aux->frame_type != OS_FRAME_TYPE_EOD && + aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "ide-tape: %s: skipping frame, frame type %x\n", tape->name, aux->frame_type); + return 0; + } + if (par->partition_num != OS_DATA_PARTITION) { + if (!tape->linux_media || tape->linux_media_version != 2) { + printk(KERN_INFO "ide-tape: %s: skipping frame, partition num %d\n", tape->name, par->partition_num); + return 0; + } + } + if (par->par_desc_ver != OS_PARTITION_VERSION) { + printk(KERN_INFO "ide-tape: %s: skipping frame, partition version %d\n", tape->name, par->par_desc_ver); + return 0; + } + if (ntohs(par->wrt_pass_cntr) != tape->wrt_pass_cntr) { + printk(KERN_INFO "ide-tape: %s: skipping frame, wrt_pass_cntr %d (expected %d)\n", tape->name, ntohs(par->wrt_pass_cntr), tape->wrt_pass_cntr); + return 0; + } + if (aux->frame_seq_num != aux->logical_blk_num) { + printk(KERN_INFO "ide-tape: %s: skipping frame, seq != logical\n", tape->name); + return 0; + } + if (logical_blk_num != -1 && ntohl(aux->logical_blk_num) != logical_blk_num) { + if (!quiet) + printk(KERN_INFO "ide-tape: %s: skipping frame, logical_blk_num %lu (expected %d)\n", tape->name, ntohl(aux->logical_blk_num), logical_blk_num); + return 0; + } + if (aux->frame_type == OS_FRAME_TYPE_MARKER) { + rq->errors = IDETAPE_ERROR_FILEMARK; + rq->current_nr_sectors = rq->nr_sectors; } + return 1; +} + +static void idetape_wait_first_stage(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + unsigned long flags; + + if (tape->first_stage == NULL) + return; spin_lock_irqsave(&tape->spinlock, flags); if (tape->active_stage == tape->first_stage) idetape_wait_for_request(drive, tape->active_data_request); spin_unlock_irqrestore(&tape->spinlock, flags); - - rq_ptr = &tape->first_stage->rq; - bytes_read = tape->tape_block_size * (rq_ptr->nr_sectors - rq_ptr->current_nr_sectors); - rq_ptr->nr_sectors = rq_ptr->current_nr_sectors = 0; - - idetape_switch_buffers (tape, tape->first_stage); - - if (rq_ptr->errors != IDETAPE_ERROR_FILEMARK) { - clear_bit (IDETAPE_FILEMARK, &tape->flags); - idetape_remove_stage_head (drive); - } else - set_bit (IDETAPE_FILEMARK, &tape->flags); -#if IDETAPE_DEBUG_BUGS - if (bytes_read > blocks*tape->tape_block_size) { - printk (KERN_ERR "ide-tape: bug: trying to return more bytes than requested\n"); - bytes_read=blocks*tape->tape_block_size; - } -#endif /* IDETAPE_DEBUG_BUGS */ - return (bytes_read); } /* @@ -2506,84 +3482,341 @@ rq = &new_stage->rq; ide_init_drive_cmd (rq); rq->cmd = IDETAPE_WRITE_RQ; - rq->sector = tape->block_address; /* Doesn't actually matter - We always assume sequential access */ + rq->sector = tape->first_frame_position; /* Doesn't actually matter - We always assume sequential access */ rq->nr_sectors = rq->current_nr_sectors = blocks; idetape_switch_buffers (tape, new_stage); + idetape_init_stage(drive, new_stage, OS_FRAME_TYPE_DATA, tape->logical_blk_num); + tape->logical_blk_num++; idetape_add_stage_tail (drive,new_stage); + tape->pipeline_head++; +#if USE_IOTRACE + IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor); +#endif + calculate_speeds(drive); /* - * Check if we are currently servicing requests in the bottom - * part of the driver. + * Estimate whether the tape has stopped writing by checking + * if our write pipeline is currently empty. If we are not + * writing anymore, wait for the pipeline to be full enough + * (90%) before starting to service requests, so that we will + * be able to keep up with the higher speeds of the tape. * - * If not, wait for the pipeline to be full enough (75%) before - * starting to service requests, so that we will be able to - * keep up with the higher speeds of the tape. + * For the OnStream drive, we can query the number of pending + * frames in the drive's internal buffer. As long as the tape + * is still writing, it is better to write frames immediately + * rather than gather them in the pipeline. This will give the + * tape's firmware the ability to sense the current incoming + * data rate more accurately, and since the OnStream tape + * supports variable speeds, it can try to adjust itself to the + * incoming data rate. */ - if (!idetape_pipeline_active (tape) && tape->nr_stages >= (3 * tape->max_stages) / 4) - idetape_insert_pipeline_into_queue (drive); - + if (!idetape_pipeline_active(tape)) { + if (tape->nr_stages >= tape->max_stages * 9 / 10 || + tape->nr_stages >= tape->max_stages - tape->uncontrolled_pipeline_head_speed * 3 * 1024 / tape->tape_block_size) { + tape->measure_insert_time = 1; + tape->insert_time = jiffies; + tape->insert_size = 0; + tape->insert_speed = 0; + idetape_insert_pipeline_into_queue (drive); + } else if (tape->onstream) { + idetape_update_stats(drive); + if (tape->cur_frames > 5) + idetape_insert_pipeline_into_queue (drive); + } + } if (test_and_clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags)) /* Return a deferred error */ return -EIO; return blocks; } -static void idetape_discard_read_pipeline (ide_drive_t *drive) +/* + * idetape_wait_for_pipeline will wait until all pending pipeline + * requests are serviced. Typically called on device close. + */ +static void idetape_wait_for_pipeline (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; unsigned long flags; + while (tape->next_stage || idetape_pipeline_active(tape)) { + idetape_insert_pipeline_into_queue (drive); + spin_lock_irqsave(&tape->spinlock, flags); + if (idetape_pipeline_active(tape)) + idetape_wait_for_request(drive, tape->active_data_request); + spin_unlock_irqrestore(&tape->spinlock, flags); + } +} + +static void idetape_empty_write_pipeline (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + int blocks, i, min; + struct buffer_head *bh; + #if IDETAPE_DEBUG_BUGS - if (tape->chrdev_direction != idetape_direction_read) { - printk (KERN_ERR "ide-tape: bug: Trying to discard read pipeline, but we are not reading.\n"); + if (tape->chrdev_direction != idetape_direction_write) { + printk (KERN_ERR "ide-tape: bug: Trying to empty write pipeline, but we are not writing.\n"); return; } + if (tape->merge_stage_size > tape->stage_size) { + printk (KERN_ERR "ide-tape: bug: merge_buffer too big\n"); + tape->merge_stage_size = tape->stage_size; + } #endif /* IDETAPE_DEBUG_BUGS */ - tape->merge_stage_size = 0; + if (tape->merge_stage_size) { + blocks=tape->merge_stage_size/tape->tape_block_size; + if (tape->merge_stage_size % tape->tape_block_size) { + blocks++; + i = tape->tape_block_size - tape->merge_stage_size % tape->tape_block_size; + bh = tape->bh->b_reqnext; + while (bh) { + atomic_set(&bh->b_count, 0); + bh = bh->b_reqnext; + } + bh = tape->bh; + while (i) { + if (bh == NULL) { + printk(KERN_INFO "bug, bh NULL\n"); + break; + } + min = IDE_MIN(i, bh->b_size - atomic_read(&bh->b_count)); + memset(bh->b_data + atomic_read(&bh->b_count), 0, min); + atomic_add(min, &bh->b_count); + i -= min; + bh = bh->b_reqnext; + } + } + (void) idetape_add_chrdev_write_request (drive, blocks); + tape->merge_stage_size = 0; + } + idetape_wait_for_pipeline (drive); if (tape->merge_stage != NULL) { __idetape_kfree_stage (tape->merge_stage); tape->merge_stage = NULL; } - tape->chrdev_direction = idetape_direction_none; - - if (tape->first_stage == NULL) - return; - - spin_lock_irqsave(&tape->spinlock, flags); - tape->next_stage = NULL; - if (idetape_pipeline_active (tape)) - idetape_wait_for_request(drive, tape->active_data_request); - spin_unlock_irqrestore(&tape->spinlock, flags); + clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags); + tape->chrdev_direction=idetape_direction_none; - while (tape->first_stage != NULL) - idetape_remove_stage_head (drive); - tape->nr_pending_stages = 0; + /* + * On the next backup, perform the feedback loop again. + * (I don't want to keep sense information between backups, + * as some systems are constantly on, and the system load + * can be totally different on the next backup). + */ tape->max_stages = tape->min_pipeline; +#if IDETAPE_DEBUG_BUGS + if (tape->first_stage != NULL || tape->next_stage != NULL || tape->last_stage != NULL || tape->nr_stages != 0) { + printk (KERN_ERR "ide-tape: ide-tape pipeline bug, " + "first_stage %p, next_stage %p, last_stage %p, nr_stages %d\n", + tape->first_stage, tape->next_stage, tape->last_stage, tape->nr_stages); + } +#endif /* IDETAPE_DEBUG_BUGS */ +} + +static void idetape_restart_speed_control(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + tape->restart_speed_control_req = 0; + tape->pipeline_head = 0; + tape->buffer_head = tape->tape_head = tape->cur_frames; + tape->controlled_last_pipeline_head = tape->uncontrolled_last_pipeline_head = 0; + tape->controlled_previous_pipeline_head = tape->uncontrolled_previous_pipeline_head = 0; + tape->pipeline_head_speed = tape->controlled_pipeline_head_speed = 5000; + tape->uncontrolled_pipeline_head_speed = 0; + tape->controlled_pipeline_head_time = tape->uncontrolled_pipeline_head_time = jiffies; + tape->controlled_previous_head_time = tape->uncontrolled_previous_head_time = jiffies; +} + +static int idetape_initiate_read(ide_drive_t *drive, int max_stages) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *new_stage; + struct request rq; + int bytes_read; + int blocks = tape->capabilities.ctl; + + if (tape->chrdev_direction != idetape_direction_read) { /* Initialize read operation */ + if (tape->chrdev_direction == idetape_direction_write) { + idetape_empty_write_pipeline (drive); + idetape_flush_tape_buffers (drive); + } +#if IDETAPE_DEBUG_BUGS + if (tape->merge_stage || tape->merge_stage_size) { + printk (KERN_ERR "ide-tape: merge_stage_size should be 0 now\n"); + tape->merge_stage_size = 0; + } +#endif /* IDETAPE_DEBUG_BUGS */ + if ((tape->merge_stage = __idetape_kmalloc_stage (tape, 0, 0)) == NULL) + return -ENOMEM; + tape->chrdev_direction = idetape_direction_read; + tape->logical_blk_num = 0; + + /* + * Issue a read 0 command to ensure that DSC handshake + * is switched from completion mode to buffer available + * mode. + */ + bytes_read = idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 0, tape->merge_stage->bh); + if (bytes_read < 0) { + kfree (tape->merge_stage); + tape->merge_stage = NULL; + tape->chrdev_direction = idetape_direction_none; + return bytes_read; + } + } + if (tape->restart_speed_control_req) + idetape_restart_speed_control(drive); + ide_init_drive_cmd (&rq); + rq.cmd = IDETAPE_READ_RQ; + rq.sector = tape->first_frame_position; + rq.nr_sectors = rq.current_nr_sectors = blocks; + if (!test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags) && tape->nr_stages <= max_stages) { + new_stage=idetape_kmalloc_stage (tape); + while (new_stage != NULL) { + new_stage->rq=rq; + idetape_add_stage_tail (drive,new_stage); + if (tape->nr_stages >= max_stages) + break; + new_stage=idetape_kmalloc_stage (tape); + } + } + if (!idetape_pipeline_active(tape)) { + if (tape->nr_pending_stages >= 3 * max_stages / 4) { + tape->measure_insert_time = 1; + tape->insert_time = jiffies; + tape->insert_size = 0; + tape->insert_speed = 0; + idetape_insert_pipeline_into_queue (drive); + } else if (tape->onstream) { + idetape_update_stats(drive); + if (tape->cur_frames < tape->max_frames - 5) + idetape_insert_pipeline_into_queue (drive); + } + } + return 0; +} + +static int idetape_get_logical_blk(ide_drive_t *drive, int logical_blk_num, int max_stages, int quiet) +{ + idetape_tape_t *tape = drive->driver_data; + unsigned long flags; + int cnt = 0, x, position; + + /* + * Search and wait for the next logical tape block + */ + while (1) { + if (cnt++ > 100) { + printk(KERN_INFO "ide-tape: %s: couldn't find logical block %d, aborting\n", tape->name, logical_blk_num); + return 0; + } + idetape_initiate_read(drive, max_stages); + if (tape->first_stage == NULL) { + if (tape->onstream) { +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: first_stage == NULL, pipeline error %d\n", tape->name, test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags)); +#endif + clear_bit(IDETAPE_PIPELINE_ERROR, &tape->flags); + position = idetape_read_position(drive); + printk(KERN_INFO "ide-tape: %s: blank block detected, positioning tape to block %d\n", tape->name, position + 60); + idetape_position_tape(drive, position + 60, 0, 1); + cnt += 40; + continue; + } else + return 0; + } + idetape_wait_first_stage(drive); + if (idetape_verify_stage(drive, tape->first_stage, logical_blk_num, quiet)) + break; + if (tape->first_stage->rq.errors == IDETAPE_ERROR_EOD) + cnt--; + if (idetape_verify_stage(drive, tape->first_stage, -1, quiet)) { + x = ntohl(tape->first_stage->aux->logical_blk_num); + if (x > logical_blk_num) { + printk(KERN_ERR "ide-tape: %s: couldn't find logical block %d, aborting (block %d found)\n", tape->name, logical_blk_num, x); + return 0; + } + } + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head(drive); + spin_unlock_irqrestore(&tape->spinlock, flags); + } + if (tape->onstream) + tape->logical_blk_num = ntohl(tape->first_stage->aux->logical_blk_num); + return 1; } /* - * idetape_wait_for_pipeline will wait until all pending pipeline - * requests are serviced. Typically called on device close. + * idetape_add_chrdev_read_request is called from idetape_chrdev_read + * to service a character device read request and add read-ahead + * requests to our pipeline. */ -static void idetape_wait_for_pipeline (ide_drive_t *drive) +static int idetape_add_chrdev_read_request (ide_drive_t *drive,int blocks) { idetape_tape_t *tape = drive->driver_data; unsigned long flags; + struct request *rq_ptr; + int bytes_read; + +#if IDETAPE_DEBUG_LOG + printk (KERN_INFO "Reached idetape_add_chrdev_read_request, %d blocks\n", blocks); +#endif /* IDETAPE_DEBUG_LOG */ + + /* + * Wait for the next logical block to be available at the head + * of the pipeline + */ + if (!idetape_get_logical_blk(drive, tape->logical_blk_num, tape->max_stages, 0)) { + if (tape->onstream) { + set_bit(IDETAPE_READ_ERROR, &tape->flags); + return 0; + } + if (test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags)) + return 0; + return idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, blocks, tape->merge_stage->bh); + } + rq_ptr = &tape->first_stage->rq; + bytes_read = tape->tape_block_size * (rq_ptr->nr_sectors - rq_ptr->current_nr_sectors); + rq_ptr->nr_sectors = rq_ptr->current_nr_sectors = 0; - if (!idetape_pipeline_active (tape)) - idetape_insert_pipeline_into_queue (drive); - spin_lock_irqsave(&tape->spinlock, flags); - if (!idetape_pipeline_active (tape)) - goto abort; + if (tape->onstream && !tape->raw && tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) { +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: EOD reached\n", tape->name); +#endif + return 0; + } + if (rq_ptr->errors == IDETAPE_ERROR_EOD) + return 0; + else if (rq_ptr->errors == IDETAPE_ERROR_FILEMARK) + set_bit (IDETAPE_FILEMARK, &tape->flags); + else { + idetape_switch_buffers (tape, tape->first_stage); + if (rq_ptr->errors == IDETAPE_ERROR_GENERAL) { +#if ONSTREAM_DEBUG + printk(KERN_INFO "error detected, bytes_read %d\n", bytes_read); +#endif + } + clear_bit (IDETAPE_FILEMARK, &tape->flags); + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head (drive); + spin_unlock_irqrestore(&tape->spinlock, flags); + tape->logical_blk_num++; + tape->pipeline_head++; +#if USE_IOTRACE + IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor); +#endif + calculate_speeds(drive); + } #if IDETAPE_DEBUG_BUGS - if (tape->last_stage == NULL) - printk ("ide-tape: tape->last_stage == NULL\n"); - else + if (bytes_read > blocks*tape->tape_block_size) { + printk (KERN_ERR "ide-tape: bug: trying to return more bytes than requested\n"); + bytes_read=blocks*tape->tape_block_size; + } #endif /* IDETAPE_DEBUG_BUGS */ - idetape_wait_for_request(drive, &tape->last_stage->rq); -abort: - spin_unlock_irqrestore(&tape->spinlock, flags); + return (bytes_read); } static void idetape_pad_zeros (ide_drive_t *drive, int bcount) @@ -2607,54 +3840,6 @@ } } -static void idetape_empty_write_pipeline (ide_drive_t *drive) -{ - idetape_tape_t *tape = drive->driver_data; - int blocks, i; - -#if IDETAPE_DEBUG_BUGS - if (tape->chrdev_direction != idetape_direction_write) { - printk (KERN_ERR "ide-tape: bug: Trying to empty write pipeline, but we are not writing.\n"); - return; - } - if (tape->merge_stage_size > tape->stage_size) { - printk (KERN_ERR "ide-tape: bug: merge_buffer too big\n"); - tape->merge_stage_size = tape->stage_size; - } -#endif /* IDETAPE_DEBUG_BUGS */ - if (tape->merge_stage_size) { - blocks=tape->merge_stage_size/tape->tape_block_size; - if (tape->merge_stage_size % tape->tape_block_size) { - blocks++; - i = tape->tape_block_size - tape->merge_stage_size % tape->tape_block_size; - memset (tape->bh->b_data + atomic_read(&tape->bh->b_count), 0, i); - atomic_add(i, &tape->bh->b_count); - } - (void) idetape_add_chrdev_write_request (drive, blocks); - tape->merge_stage_size = 0; - } - idetape_wait_for_pipeline (drive); - if (tape->merge_stage != NULL) { - __idetape_kfree_stage (tape->merge_stage); - tape->merge_stage = NULL; - } - clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags); - tape->chrdev_direction=idetape_direction_none; - - /* - * On the next backup, perform the feedback loop again. - * (I don't want to keep sense information between backups, - * as some systems are constantly on, and the system load - * can be totally different on the next backup). - */ - tape->max_stages = tape->min_pipeline; -#if IDETAPE_DEBUG_BUGS - if (tape->first_stage != NULL || tape->next_stage != NULL || tape->last_stage != NULL || tape->nr_stages != 0) { - printk (KERN_ERR "ide-tape: ide-tape pipeline bug\n"); - } -#endif /* IDETAPE_DEBUG_BUGS */ -} - static int idetape_pipeline_size (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; @@ -2676,28 +3861,6 @@ } /* - * idetape_position_tape positions the tape to the requested block - * using the LOCATE packet command. A READ POSITION command is then - * issued to check where we are positioned. - * - * Like all higher level operations, we queue the commands at the tail - * of the request queue and wait for their completion. - * - */ -static int idetape_position_tape (ide_drive_t *drive, unsigned int block, byte partition) -{ - int retval; - idetape_pc_t pc; - - idetape_create_locate_cmd (&pc, block, partition); - retval=idetape_queue_pc_tail (drive,&pc); - if (retval) return (retval); - - idetape_create_read_position_cmd (&pc); - return (idetape_queue_pc_tail (drive,&pc)); -} - -/* * Rewinds the tape to the Beginning Of the current Partition (BOP). * * We currently support only one partition. @@ -2706,24 +3869,20 @@ { int retval; idetape_pc_t pc; + idetape_tape_t *tape = drive->driver_data; #if IDETAPE_DEBUG_LOG printk (KERN_INFO "Reached idetape_rewind_tape\n"); #endif /* IDETAPE_DEBUG_LOG */ - idetape_create_rewind_cmd (&pc); + idetape_create_rewind_cmd (drive, &pc); retval=idetape_queue_pc_tail (drive,&pc); - if (retval) return (retval); + if (retval) return retval; idetape_create_read_position_cmd (&pc); - return (idetape_queue_pc_tail (drive,&pc)); -} - -static int idetape_flush_tape_buffers (ide_drive_t *drive) -{ - idetape_pc_t pc; - - idetape_create_write_filemark_cmd (&pc,0); - return (idetape_queue_pc_tail (drive,&pc)); + retval = idetape_queue_pc_tail (drive,&pc); + if (retval) return retval; + tape->logical_blk_num = 0; + return 0; } /* @@ -2792,13 +3951,171 @@ */ static ide_drive_t *get_drive_ptr (kdev_t i_rdev) { - unsigned int i = MINOR(i_rdev) & ~0x80; + unsigned int i = MINOR(i_rdev) & ~0xc0; if (i >= MAX_HWIFS * MAX_DRIVES) return NULL; return (idetape_chrdevs[i].drive); } +static int idetape_onstream_space_over_filemarks_backward (ide_drive_t *drive,short mt_op,int mt_count) +{ + idetape_tape_t *tape = drive->driver_data; + int cnt = 0; + int last_mark_addr; + unsigned long flags; + + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_bwd\n", tape->name); + return -EIO; + } + while (cnt != mt_count) { + last_mark_addr = ntohl(tape->first_stage->aux->last_mark_addr); + if (last_mark_addr == -1) + return -EIO; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: positioning to last mark at %d\n", last_mark_addr); +#endif + idetape_position_tape(drive, last_mark_addr, 0, 0); + cnt++; + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name); + return -EIO; + } + if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "ide-tape: %s: expected to find marker at block %d, not found\n", tape->name, last_mark_addr); + return -EIO; + } + } + if (mt_op == MTBSFM) { + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head (drive); + tape->logical_blk_num++; + spin_unlock_irqrestore(&tape->spinlock, flags); + } + return 0; +} + +/* + * ADRL 1.1 compatible "slow" space filemarks fwd version + * + * Just scans for the filemark sequentially. + */ +static int idetape_onstream_space_over_filemarks_forward_slow(ide_drive_t *drive,short mt_op,int mt_count) +{ + idetape_tape_t *tape = drive->driver_data; + int cnt = 0; + unsigned long flags; + + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd\n", tape->name); + return -EIO; + } + while (1) { + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name); + return -EIO; + } + if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_MARKER) + cnt++; + if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) { +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: space_fwd: EOD reached\n", tape->name); +#endif + return -EIO; + } + if (cnt == mt_count) + break; + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head (drive); + spin_unlock_irqrestore(&tape->spinlock, flags); + } + if (mt_op == MTFSF) { + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head (drive); + tape->logical_blk_num++; + spin_unlock_irqrestore(&tape->spinlock, flags); + } + return 0; +} + + +/* + * Fast linux specific version of OnStream FSF + */ +static int idetape_onstream_space_over_filemarks_forward_fast(ide_drive_t *drive,short mt_op,int mt_count) +{ + idetape_tape_t *tape = drive->driver_data; + int cnt = 0, next_mark_addr; + unsigned long flags; + + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd\n", tape->name); + return -EIO; + } + + /* + * Find nearest (usually previous) marker + */ + while (1) { + if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_MARKER) + break; + if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) { +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: space_fwd: EOD reached\n", tape->name); +#endif + return -EIO; + } + if (ntohl(tape->first_stage->aux->filemark_cnt) == 0) { + if (tape->first_mark_addr == -1) { + printk(KERN_INFO "ide-tape: %s: reverting to slow filemark space\n", tape->name); + return idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count); + } + idetape_position_tape(drive, tape->first_mark_addr, 0, 0); + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd_fast\n", tape->name); + return -EIO; + } + if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "ide-tape: %s: expected to find filemark at %d\n", tape->name, tape->first_mark_addr); + return -EIO; + } + } else { + if (idetape_onstream_space_over_filemarks_backward(drive, MTBSF, 1) < 0) + return -EIO; + mt_count++; + } + } + cnt++; + while (cnt != mt_count) { + next_mark_addr = ntohl(tape->first_stage->aux->next_mark_addr); + if (!next_mark_addr || next_mark_addr > tape->eod_frame_addr) { + printk(KERN_INFO "ide-tape: %s: reverting to slow filemark space\n", tape->name); + return idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count - cnt); + } +#if ONSTREAM_DEBUG + else printk(KERN_INFO "ide-tape: positioning to next mark at %d\n", next_mark_addr); +#endif + idetape_position_tape(drive, next_mark_addr, 0, 0); + cnt++; + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name); + return -EIO; + } + if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "ide-tape: %s: expected to find marker at block %d, not found\n", tape->name, next_mark_addr); + return -EIO; + } + } + if (mt_op == MTFSF) { + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head (drive); + tape->logical_blk_num++; + spin_unlock_irqrestore(&tape->spinlock, flags); + } + return 0; +} + /* * idetape_space_over_filemarks is now a bit more complicated than just * passing the command to the tape since we may have crossed some @@ -2814,9 +4131,26 @@ idetape_pc_t pc; unsigned long flags; int retval,count=0; + int speed_control; - if (tape->chrdev_direction == idetape_direction_read) { + if (tape->onstream) { + if (tape->raw) + return -EIO; + speed_control = tape->speed_control; + tape->speed_control = 0; + if (mt_op == MTFSF || mt_op == MTFSFM) { + if (tape->linux_media) + retval = idetape_onstream_space_over_filemarks_forward_fast(drive, mt_op, mt_count); + else + retval = idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count); + } else + retval = idetape_onstream_space_over_filemarks_backward(drive, mt_op, mt_count); + tape->speed_control = speed_control; + tape->restart_speed_control_req = 1; + return retval; + } + if (tape->chrdev_direction == idetape_direction_read) { /* * We have a read-ahead buffer. Scan it for crossed * filemarks. @@ -2824,30 +4158,26 @@ tape->merge_stage_size = 0; clear_bit (IDETAPE_FILEMARK, &tape->flags); while (tape->first_stage != NULL) { - /* - * Wait until the first read-ahead request - * is serviced. - */ - spin_lock_irqsave(&tape->spinlock, flags); - if (tape->active_stage == tape->first_stage) - idetape_wait_for_request(drive, tape->active_data_request); - spin_unlock_irqrestore(&tape->spinlock, flags); - + idetape_wait_first_stage(drive); if (tape->first_stage->rq.errors == IDETAPE_ERROR_FILEMARK) count++; if (count == mt_count) { switch (mt_op) { case MTFSF: + spin_lock_irqsave(&tape->spinlock, flags); idetape_remove_stage_head (drive); + spin_unlock_irqrestore(&tape->spinlock, flags); case MTFSFM: return (0); default: break; } } + spin_lock_irqsave(&tape->spinlock, flags); idetape_remove_stage_head (drive); + spin_unlock_irqrestore(&tape->spinlock, flags); } - idetape_discard_read_pipeline (drive); + idetape_discard_read_pipeline (drive, 1); } /* @@ -2903,48 +4233,27 @@ struct inode *inode = file->f_dentry->d_inode; ide_drive_t *drive = get_drive_ptr (inode->i_rdev); idetape_tape_t *tape = drive->driver_data; - ssize_t bytes_read,temp,actually_read=0; + ssize_t bytes_read,temp,actually_read=0, rc; if (ppos != &file->f_pos) { /* "A request was outside the capabilities of the device." */ return -ENXIO; } - + if (tape->onstream && (count != tape->tape_block_size)) { + printk(KERN_ERR "ide-tape: %s: use %d bytes as block size (%d used)\n", tape->name, tape->tape_block_size, count); + return -EINVAL; + } #if IDETAPE_DEBUG_LOG - printk (KERN_INFO "Reached idetape_chrdev_read\n"); + printk (KERN_INFO "Reached idetape_chrdev_read, count %d\n", count); #endif /* IDETAPE_DEBUG_LOG */ - - if (tape->chrdev_direction != idetape_direction_read) { /* Initialize read operation */ - if (tape->chrdev_direction == idetape_direction_write) { - idetape_empty_write_pipeline (drive); - idetape_flush_tape_buffers (drive); - } -#if IDETAPE_DEBUG_BUGS - if (tape->merge_stage || tape->merge_stage_size) { - printk (KERN_ERR "ide-tape: merge_stage_size should be 0 now\n"); - tape->merge_stage_size = 0; - } -#endif /* IDETAPE_DEBUG_BUGS */ - if ((tape->merge_stage = __idetape_kmalloc_stage (tape)) == NULL) - return -ENOMEM; - tape->chrdev_direction = idetape_direction_read; - /* - * Issue a read 0 command to ensure that DSC handshake - * is switched from completion mode to buffer available - * mode. - */ - bytes_read = idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 0, tape->merge_stage->bh); - if (bytes_read < 0) { - kfree (tape->merge_stage); - tape->merge_stage = NULL; - tape->chrdev_direction = idetape_direction_none; - return bytes_read; - } + if (tape->chrdev_direction != idetape_direction_read) { if (test_bit (IDETAPE_DETECT_BS, &tape->flags)) if (count > tape->tape_block_size && (count % tape->tape_block_size) == 0) tape->user_bs_factor = count / tape->tape_block_size; } + if ((rc = idetape_initiate_read(drive, tape->max_stages)) < 0) + return rc; if (count==0) return (0); if (tape->merge_stage_size) { @@ -2952,28 +4261,143 @@ idetape_copy_stage_to_user (tape, buf, tape->merge_stage, actually_read); buf += actually_read; tape->merge_stage_size -= actually_read; count-=actually_read; } - while (count >= tape->stage_size) { - bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl); - if (bytes_read <= 0) - goto finish; - idetape_copy_stage_to_user (tape, buf, tape->merge_stage, bytes_read); - buf += bytes_read; count -= bytes_read; actually_read += bytes_read; + while (count >= tape->stage_size) { + bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl); + if (bytes_read <= 0) + goto finish; + idetape_copy_stage_to_user (tape, buf, tape->merge_stage, bytes_read); + buf += bytes_read; count -= bytes_read; actually_read += bytes_read; + } + if (count) { + bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl); + if (bytes_read <= 0) + goto finish; + temp=IDE_MIN (count,bytes_read); + idetape_copy_stage_to_user (tape, buf, tape->merge_stage, temp); + actually_read+=temp; + tape->merge_stage_size=bytes_read-temp; + } +finish: + if (!actually_read && test_bit (IDETAPE_FILEMARK, &tape->flags)) { +#if IDETAPE_DEBUG_LOG + printk(KERN_INFO "ide-tape: %s: spacing over filemark\n", tape->name); +#endif + idetape_space_over_filemarks (drive, MTFSF, 1); + return 0; + } + if (tape->onstream && !actually_read && test_and_clear_bit(IDETAPE_READ_ERROR, &tape->flags)) { + printk(KERN_ERR "ide-tape: %s: unrecovered read error on logical block number %d, skipping\n", tape->name, tape->logical_blk_num); + tape->logical_blk_num++; + return -EIO; + } + return actually_read; +} + +static void idetape_update_last_marker(ide_drive_t *drive, int last_mark_addr, int next_mark_addr) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage; + os_aux_t *aux; + int position; + + if (!tape->onstream || tape->raw) + return; + if (last_mark_addr == -1) + return; + stage = __idetape_kmalloc_stage(tape, 0, 0); + if (stage == NULL) + return; + idetape_flush_tape_buffers(drive); + position = idetape_read_position(drive); +#if ONSTREAM_DEBUG + printk(KERN_INFO "current position (2) %d, lblk %d\n", position, tape->logical_blk_num); + printk(KERN_INFO "current position (2) tape block %d\n", tape->last_frame_position); +#endif + idetape_position_tape(drive, last_mark_addr, 0, 0); + if (!idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 1, stage->bh)) { + printk(KERN_INFO "ide-tape: %s: couldn't read last marker\n", tape->name); + __idetape_kfree_stage (stage); + idetape_position_tape(drive, position, 0, 0); + return; + } + aux = stage->aux; + if (aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "ide-tape: %s: expected to find marker at addr %d\n", tape->name, last_mark_addr); + __idetape_kfree_stage (stage); + idetape_position_tape(drive, position, 0, 0); + return; + } +#if ONSTREAM_DEBUG + printk(KERN_INFO "writing back marker\n"); +#endif + aux->next_mark_addr = htonl(next_mark_addr); + idetape_position_tape(drive, last_mark_addr, 0, 0); + if (!idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, 1, stage->bh)) { + printk(KERN_INFO "ide-tape: %s: couldn't write back marker frame at %d\n", tape->name, last_mark_addr); + __idetape_kfree_stage (stage); + idetape_position_tape(drive, position, 0, 0); + return; } - if (count) { - bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl); - if (bytes_read <= 0) - goto finish; - temp=IDE_MIN (count,bytes_read); - idetape_copy_stage_to_user (tape, buf, tape->merge_stage, temp); - actually_read+=temp; - tape->merge_stage_size=bytes_read-temp; + __idetape_kfree_stage (stage); + idetape_flush_tape_buffers (drive); + idetape_position_tape(drive, position, 0, 0); + return; +} + +static void __idetape_write_header(ide_drive_t *drive, int block, int cnt) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage; + os_header_t header; + + stage = __idetape_kmalloc_stage(tape, 1, 1); + if (stage == NULL) + return; + idetape_init_stage(drive, stage, OS_FRAME_TYPE_HEADER, tape->logical_blk_num); + idetape_wait_ready(drive, 60 * 5 * HZ); + idetape_position_tape(drive, block, 0, 0); + memset(&header, 0, sizeof(header)); + strcpy(header.ident_str, "ADR_SEQ"); + header.major_rev = header.minor_rev = 1; + header.par_num = 1; + header.partition.partition_num = OS_DATA_PARTITION; + header.partition.par_desc_ver = OS_PARTITION_VERSION; + header.partition.first_frame_addr = htonl(0x14); + header.partition.last_frame_addr = htonl(19239 * 24); + header.partition.wrt_pass_cntr = htons(tape->wrt_pass_cntr); + header.partition.eod_frame_addr = htonl(tape->eod_frame_addr); + memcpy(stage->bh->b_data, &header, sizeof(header)); + while (cnt--) { + if (!idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, 1, stage->bh)) { + printk(KERN_INFO "ide-tape: %s: couldn't write header frame\n", tape->name); + __idetape_kfree_stage (stage); + return; + } } -finish: - if (!actually_read && test_bit (IDETAPE_FILEMARK, &tape->flags)) - idetape_space_over_filemarks (drive, MTFSF, 1); - return (actually_read); + __idetape_kfree_stage (stage); + idetape_flush_tape_buffers (drive); } - + +static void idetape_write_header(ide_drive_t *drive, int locate_eod) +{ + idetape_tape_t *tape = drive->driver_data; + +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: writing tape header\n", tape->name); +#endif + if (!tape->onstream || tape->raw) + return; + tape->update_frame_cntr++; + __idetape_write_header(drive, 5, 5); + __idetape_write_header(drive, 0xbb2, 6); + if (locate_eod) { +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: locating back to eod frame addr %d\n", tape->name, tape->eod_frame_addr); +#endif + idetape_position_tape(drive, tape->eod_frame_addr, 0, 0); + } +} + static ssize_t idetape_chrdev_write (struct file *file, const char *buf, size_t count, loff_t *ppos) { @@ -2981,30 +4405,59 @@ ide_drive_t *drive = get_drive_ptr (inode->i_rdev); idetape_tape_t *tape = drive->driver_data; ssize_t retval,actually_written=0; + int position; if (ppos != &file->f_pos) { /* "A request was outside the capabilities of the device." */ return -ENXIO; } - + if (tape->onstream && (count != tape->tape_block_size)) { + printk(KERN_ERR "ide-tape: %s: use %d bytes as block size (%d used)\n", tape->name, tape->tape_block_size, count); + return -EINVAL; + } #if IDETAPE_DEBUG_LOG - printk (KERN_INFO "Reached idetape_chrdev_write\n"); + printk (KERN_INFO "Reached idetape_chrdev_write, count %d\n", count); #endif /* IDETAPE_DEBUG_LOG */ if (tape->chrdev_direction != idetape_direction_write) { /* Initialize write operation */ if (tape->chrdev_direction == idetape_direction_read) - idetape_discard_read_pipeline (drive); + idetape_discard_read_pipeline (drive, 1); #if IDETAPE_DEBUG_BUGS if (tape->merge_stage || tape->merge_stage_size) { printk (KERN_ERR "ide-tape: merge_stage_size should be 0 now\n"); tape->merge_stage_size = 0; } #endif /* IDETAPE_DEBUG_BUGS */ - if ((tape->merge_stage = __idetape_kmalloc_stage (tape)) == NULL) + if ((tape->merge_stage = __idetape_kmalloc_stage (tape, 0, 0)) == NULL) return -ENOMEM; tape->chrdev_direction = idetape_direction_write; idetape_init_merge_stage (tape); + if (tape->onstream) { + position = idetape_read_position(drive); + if (position <= 20) { + tape->logical_blk_num = 0; + tape->wrt_pass_cntr++; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: logical block num 0, setting eod to 20\n", tape->name); + printk(KERN_INFO "ide-tape: %s: allocating new write pass counter %d\n", tape->name, tape->wrt_pass_cntr); +#endif + tape->filemark_cnt = 0; + tape->eod_frame_addr = 20; + tape->first_mark_addr = tape->last_mark_addr = -1; + idetape_write_header(drive, 1); + } +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: positioning tape to eod at %d\n", tape->name, tape->eod_frame_addr); +#endif + position = idetape_read_position(drive); + if (position != tape->eod_frame_addr) + idetape_position_tape(drive, tape->eod_frame_addr, 0, 0); +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: first_frame_position %d\n", tape->name, tape->first_frame_position); +#endif + } + /* * Issue a write 0 command to ensure that DSC handshake * is switched from completion mode to buffer available @@ -3017,12 +4470,14 @@ tape->chrdev_direction = idetape_direction_none; return retval; } - if (test_bit (IDETAPE_DETECT_BS, &tape->flags)) - if (count > tape->tape_block_size && (count % tape->tape_block_size) == 0) - tape->user_bs_factor = count / tape->tape_block_size; +#if ONSTREAM_DEBUG + printk("ide-tape: first_frame_position %d\n", tape->first_frame_position); +#endif } if (count==0) return (0); + if (tape->restart_speed_control_req) + idetape_restart_speed_control(drive); if (tape->merge_stage_size) { #if IDETAPE_DEBUG_BUGS if (tape->merge_stage_size >= tape->stage_size) { @@ -3057,6 +4512,90 @@ return (actually_written); } +static int idetape_write_filemark(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + int last_mark_addr; + idetape_pc_t pc; + + if (!tape->onstream) { + idetape_create_write_filemark_cmd(drive, &pc,1); /* Write a filemark */ + if (idetape_queue_pc_tail (drive,&pc)) { + printk (KERN_ERR "ide-tape: Couldn't write a filemark\n"); + return -EIO; + } + } else if (!tape->raw) { + last_mark_addr = idetape_read_position(drive); + tape->merge_stage = __idetape_kmalloc_stage (tape, 1, 0); + if (tape->merge_stage != NULL) { + idetape_init_stage(drive, tape->merge_stage, OS_FRAME_TYPE_MARKER, tape->logical_blk_num); + idetape_pad_zeros (drive, tape->stage_size); + tape->logical_blk_num++; + __idetape_kfree_stage (tape->merge_stage); + tape->merge_stage = NULL; + } + if (tape->filemark_cnt) + idetape_update_last_marker(drive, tape->last_mark_addr, last_mark_addr); + tape->last_mark_addr = last_mark_addr; + if (tape->filemark_cnt++ == 0) + tape->first_mark_addr = last_mark_addr; + } + return 0; +} + +static void idetape_write_eod(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + if (!tape->onstream || tape->raw) + return; + tape->merge_stage = __idetape_kmalloc_stage (tape, 1, 0); + if (tape->merge_stage != NULL) { + tape->eod_frame_addr = idetape_read_position(drive); + idetape_init_stage(drive, tape->merge_stage, OS_FRAME_TYPE_EOD, tape->logical_blk_num); + idetape_pad_zeros (drive, tape->stage_size); + __idetape_kfree_stage (tape->merge_stage); + tape->merge_stage = NULL; + } + return; +} + +int idetape_seek_logical_blk(ide_drive_t *drive, int logical_blk_num) +{ + idetape_tape_t *tape = drive->driver_data; + int estimated_address = logical_blk_num + 20; + int retries = 0; + int speed_control; + + speed_control = tape->speed_control; + tape->speed_control = 0; + if (logical_blk_num < 0) + logical_blk_num = 0; + if (idetape_get_logical_blk(drive, logical_blk_num, 10, 1)) + goto ok; + while (++retries < 10) { + idetape_discard_read_pipeline(drive, 0); + idetape_position_tape(drive, estimated_address, 0, 0); + if (idetape_get_logical_blk(drive, logical_blk_num, 10, 1)) + goto ok; + if (!idetape_get_logical_blk(drive, -1, 10, 1)) + goto error; + if (tape->logical_blk_num < logical_blk_num) + estimated_address += logical_blk_num - tape->logical_blk_num; + else + break; + } +error: + tape->speed_control = speed_control; + tape->restart_speed_control_req = 1; + printk(KERN_INFO "ide-tape: %s: couldn't seek to logical block %d (at %d), %d retries\n", tape->name, logical_blk_num, tape->logical_blk_num, retries); + return -EIO; +ok: + tape->speed_control = speed_control; + tape->restart_speed_control_req = 1; + return 0; +} + /* * idetape_mtioctop is called from idetape_chrdev_ioctl when * the general mtio MTIOCTOP ioctl is requested. @@ -3111,9 +4650,13 @@ * * MTSETPART - Switches to another tape partition. * + * MTLOCK - Locks the tape door. + * + * MTUNLOCK - Unlocks the tape door. + * * The following commands are currently not supported: * - * MTFSR, MTBSR, MTFSS, MTBSS, MTWSM, MTSETDENSITY, + * MTFSS, MTBSS, MTWSM, MTSETDENSITY, * MTSETDRVBUFFER, MT_ST_BOOLEANS, MT_ST_WRITE_THRESHOLD. */ static int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count) @@ -3139,43 +4682,75 @@ default: break; } - - /* - * Empty the pipeline. - */ - if (tape->chrdev_direction == idetape_direction_read) - idetape_discard_read_pipeline (drive); - switch (mt_op) { case MTWEOF: - for (i=0;ionstream && !tape->raw) + return idetape_position_tape(drive, 20, 0, 0); + return 0; case MTLOAD: - idetape_create_load_unload_cmd (&pc, IDETAPE_LU_LOAD_MASK); + idetape_discard_read_pipeline (drive, 0); + idetape_create_load_unload_cmd (drive, &pc, IDETAPE_LU_LOAD_MASK); return (idetape_queue_pc_tail (drive,&pc)); case MTUNLOAD: case MTOFFL: - idetape_create_load_unload_cmd (&pc,!IDETAPE_LU_LOAD_MASK); + idetape_discard_read_pipeline (drive, 0); + idetape_create_load_unload_cmd (drive, &pc,!IDETAPE_LU_LOAD_MASK); return (idetape_queue_pc_tail (drive,&pc)); case MTNOP: + idetape_discard_read_pipeline (drive, 0); return (idetape_flush_tape_buffers (drive)); case MTRETEN: - idetape_create_load_unload_cmd (&pc,IDETAPE_LU_RETENSION_MASK | IDETAPE_LU_LOAD_MASK); + idetape_discard_read_pipeline (drive, 0); + idetape_create_load_unload_cmd (drive, &pc,IDETAPE_LU_RETENSION_MASK | IDETAPE_LU_LOAD_MASK); return (idetape_queue_pc_tail (drive,&pc)); case MTEOM: + if (tape->onstream) { +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: positioning tape to eod at %d\n", tape->name, tape->eod_frame_addr); +#endif + idetape_position_tape(drive, tape->eod_frame_addr, 0, 0); + if (!idetape_get_logical_blk(drive, -1, 10, 0)) + return -EIO; + if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_EOD) + return -EIO; + return 0; + } idetape_create_space_cmd (&pc,0,IDETAPE_SPACE_TO_EOD); return (idetape_queue_pc_tail (drive,&pc)); case MTERASE: + if (tape->onstream) { + tape->eod_frame_addr = 20; + tape->logical_blk_num = 0; + tape->first_mark_addr = tape->last_mark_addr = -1; + idetape_position_tape(drive, tape->eod_frame_addr, 0, 0); + idetape_write_eod(drive); + idetape_flush_tape_buffers (drive); + idetape_write_header(drive, 0); + idetape_flush_tape_buffers (drive); + (void) idetape_rewind_tape (drive); + return 0; + } (void) idetape_rewind_tape (drive); idetape_create_erase_cmd (&pc); return (idetape_queue_pc_tail (drive,&pc)); case MTSETBLK: + if (tape->onstream) { + if (mt_count != tape->tape_block_size) { + printk(KERN_INFO "ide-tape: %s: MTSETBLK %d -- only %d bytes block size supported\n", tape->name, mt_count, tape->tape_block_size); + return -EINVAL; + } + return 0; + } if (mt_count) { if (mt_count < tape->tape_block_size || mt_count % tape->tape_block_size) return -EIO; @@ -3185,9 +4760,40 @@ set_bit (IDETAPE_DETECT_BS, &tape->flags); return 0; case MTSEEK: - return (idetape_position_tape (drive, mt_count * tape->user_bs_factor, tape->partition)); + if (!tape->onstream || tape->raw) { + idetape_discard_read_pipeline (drive, 0); + return idetape_position_tape (drive, mt_count * tape->user_bs_factor, tape->partition, 0); + } + return idetape_seek_logical_blk(drive, mt_count); case MTSETPART: - return (idetape_position_tape (drive, 0, mt_count)); + idetape_discard_read_pipeline (drive, 0); + if (tape->onstream) + return -EIO; + return (idetape_position_tape (drive, 0, mt_count, 0)); + case MTFSR: + case MTBSR: + if (tape->onstream) { + if (!idetape_get_logical_blk(drive, -1, 10, 0)) + return -EIO; + if (mt_op == MTFSR) + return idetape_seek_logical_blk(drive, tape->logical_blk_num + mt_count); + else { + idetape_discard_read_pipeline (drive, 0); + return idetape_seek_logical_blk(drive, tape->logical_blk_num - mt_count); + } + } + case MTLOCK: + idetape_create_prevent_cmd(drive, &pc, 1); + retval = idetape_queue_pc_tail (drive,&pc); + if (retval) return retval; + tape->door_locked = DOOR_EXPLICITLY_LOCKED; + return 0; + case MTUNLOCK: + idetape_create_prevent_cmd(drive, &pc, 0); + retval = idetape_queue_pc_tail (drive,&pc); + if (retval) return retval; + tape->door_locked = DOOR_UNLOCKED; + return 0; default: printk (KERN_ERR "ide-tape: MTIO operation %d not supported\n",mt_op); return (-EIO); @@ -3221,25 +4827,24 @@ { ide_drive_t *drive = get_drive_ptr (inode->i_rdev); idetape_tape_t *tape = drive->driver_data; - idetape_pc_t pc; struct mtop mtop; struct mtget mtget; struct mtpos mtpos; - int retval, block_offset = 0; + int block_offset = 0, position = tape->first_frame_position; #if IDETAPE_DEBUG_LOG printk (KERN_INFO "Reached idetape_chrdev_ioctl, cmd=%u\n",cmd); #endif /* IDETAPE_DEBUG_LOG */ + tape->restart_speed_control_req = 1; if (tape->chrdev_direction == idetape_direction_write) { idetape_empty_write_pipeline (drive); idetape_flush_tape_buffers (drive); } if (cmd == MTIOCGET || cmd == MTIOCPOS) { block_offset = idetape_pipeline_size (drive) / (tape->tape_block_size * tape->user_bs_factor); - idetape_create_read_position_cmd (&pc); - retval=idetape_queue_pc_tail (drive,&pc); - if (retval) return (retval); + if ((position = idetape_read_position(drive)) < 0) + return -EIO; } switch (cmd) { case MTIOCTOP: @@ -3248,23 +4853,132 @@ return (idetape_mtioctop (drive,mtop.mt_op,mtop.mt_count)); case MTIOCGET: memset (&mtget, 0, sizeof (struct mtget)); - mtget.mt_blkno = tape->block_address / tape->user_bs_factor - block_offset; + mtget.mt_type = MT_ISSCSI2; + if (!tape->onstream || tape->raw) + mtget.mt_blkno = position / tape->user_bs_factor - block_offset; + else { + if (!idetape_get_logical_blk(drive, -1, 10, 0)) + mtget.mt_blkno = -1; + else + mtget.mt_blkno = tape->logical_blk_num; + } mtget.mt_dsreg = ((tape->tape_block_size * tape->user_bs_factor) << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK; + if (tape->onstream) { + mtget.mt_gstat |= GMT_ONLINE(0xffffffff); + if (tape->first_stage && tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) + mtget.mt_gstat |= GMT_EOD(0xffffffff); + if (position <= 20) + mtget.mt_gstat |= GMT_BOT(0xffffffff); + } if (copy_to_user ((char *) arg,(char *) &mtget, sizeof (struct mtget))) return -EFAULT; return 0; case MTIOCPOS: - mtpos.mt_blkno = tape->block_address / tape->user_bs_factor - block_offset; + if (tape->onstream && !tape->raw) { + if (!idetape_get_logical_blk(drive, -1, 10, 0)) + return -EIO; + mtpos.mt_blkno = tape->logical_blk_num; + } else + mtpos.mt_blkno = position / tape->user_bs_factor - block_offset; if (copy_to_user ((char *) arg,(char *) &mtpos, sizeof (struct mtpos))) return -EFAULT; return 0; default: if (tape->chrdev_direction == idetape_direction_read) - idetape_discard_read_pipeline (drive); + idetape_discard_read_pipeline (drive, 1); return (idetape_blkdev_ioctl (drive,inode,file,cmd,arg)); } } +static int __idetape_analyze_headers(ide_drive_t *drive, int block) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage; + os_header_t *header; + os_aux_t *aux; + + if (!tape->onstream || tape->raw) { + tape->header_ok = tape->linux_media = 1; + return 1; + } + tape->header_ok = tape->linux_media = 0; + tape->update_frame_cntr = 0; + tape->wrt_pass_cntr = 0; + tape->eod_frame_addr = 20; + tape->first_mark_addr = tape->last_mark_addr = -1; + stage = __idetape_kmalloc_stage (tape, 0, 0); + if (stage == NULL) + return 0; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: reading header\n", tape->name); +#endif + idetape_position_tape(drive, block, 0, 0); + if (!idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 1, stage->bh)) { + printk(KERN_INFO "ide-tape: %s: couldn't read header frame\n", tape->name); + __idetape_kfree_stage (stage); + return 0; + } + header = (os_header_t *) stage->bh->b_data; + aux = stage->aux; + if (strncmp(header->ident_str, "ADR_SEQ", 7) != 0) { + printk(KERN_INFO "ide-tape: %s: invalid header identification string\n", tape->name); + __idetape_kfree_stage (stage); + return 0; + } + if (header->major_rev != 1 || header->minor_rev != 1) + printk(KERN_INFO "ide-tape: warning: revision %d.%d detected (1.1 supported)\n", header->major_rev, header->minor_rev); + if (header->par_num != 1) + printk(KERN_INFO "ide-tape: warning: %d partitions defined, only one supported\n", header->par_num); + tape->wrt_pass_cntr = ntohs(header->partition.wrt_pass_cntr); + tape->eod_frame_addr = ntohl(header->partition.eod_frame_addr); + tape->filemark_cnt = ntohl(aux->filemark_cnt); + tape->first_mark_addr = ntohl(aux->next_mark_addr); + tape->last_mark_addr = ntohl(aux->last_mark_addr); + tape->update_frame_cntr = ntohl(aux->update_frame_cntr); + memcpy(tape->application_sig, aux->application_sig, 4); tape->application_sig[4] = 0; + if (memcmp(tape->application_sig, "LIN", 3) == 0) { + tape->linux_media = 1; + tape->linux_media_version = tape->application_sig[3] - '0'; + if (tape->linux_media_version != 3) + printk(KERN_INFO "ide-tape: %s: Linux media version %d detected (current 3)\n", tape->name, tape->linux_media_version); + } else { + printk(KERN_INFO "ide-tape: %s: non Linux media detected (%s)\n", tape->name, tape->application_sig); + tape->linux_media = 0; + } +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: %s: detected write pass counter %d, eod frame addr %d\n", tape->name, tape->wrt_pass_cntr, tape->eod_frame_addr); +#endif + __idetape_kfree_stage (stage); + return 1; +} + +static int idetape_analyze_headers(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + int position, block; + + if (!tape->onstream || tape->raw) { + tape->header_ok = tape->linux_media = 1; + return 1; + } + tape->header_ok = tape->linux_media = 0; + position = idetape_read_position(drive); + for (block = 5; block < 10; block++) + if (__idetape_analyze_headers(drive, block)) + goto ok; + for (block = 0xbb2; block < 0xbb8; block++) + if (__idetape_analyze_headers(drive, block)) + goto ok; + printk(KERN_ERR "ide-tape: %s: failed to find valid ADRL header\n", tape->name); + return 0; +ok: + if (position < 20) + position = 20; + idetape_position_tape(drive, position, 0, 0); + tape->header_ok = 1; + return 1; +} + /* * Our character device open function. */ @@ -3273,6 +4987,7 @@ ide_drive_t *drive; idetape_tape_t *tape; idetape_pc_t pc; + unsigned int minor=MINOR (inode->i_rdev); #if IDETAPE_DEBUG_LOG printk (KERN_INFO "Reached idetape_chrdev_open\n"); @@ -3285,14 +5000,41 @@ if (test_and_set_bit (IDETAPE_BUSY, &tape->flags)) return -EBUSY; MOD_INC_USE_COUNT; - idetape_create_read_position_cmd (&pc); - (void) idetape_queue_pc_tail (drive,&pc); - if (!test_bit (IDETAPE_ADDRESS_VALID, &tape->flags)) - (void) idetape_rewind_tape (drive); + if (!tape->onstream) { + idetape_read_position(drive); + if (!test_bit (IDETAPE_ADDRESS_VALID, &tape->flags)) + (void) idetape_rewind_tape (drive); + } else { + if (minor & 64) { + tape->tape_block_size = tape->stage_size = 32768 + 512; + tape->raw = 1; + } else { + tape->tape_block_size = tape->stage_size = 32768; + tape->raw = 0; + } + } + if (idetape_wait_ready(drive, 60 * HZ)) { + clear_bit(IDETAPE_BUSY, &tape->flags); + MOD_DEC_USE_COUNT; + printk(KERN_ERR "ide-tape: %s: drive not ready\n", tape->name); + return -EBUSY; + } + idetape_read_position(drive); MOD_DEC_USE_COUNT; + clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags); - if (tape->chrdev_direction == idetape_direction_none) + if (tape->chrdev_direction == idetape_direction_none) { MOD_INC_USE_COUNT; + idetape_create_prevent_cmd(drive, &pc, 1); + if (!idetape_queue_pc_tail (drive,&pc)) { + if (tape->door_locked != DOOR_EXPLICITLY_LOCKED) + tape->door_locked = DOOR_LOCKED; + } + idetape_analyze_headers(drive); + } + tape->max_frames = tape->cur_frames = tape->req_buffer_fill = 0; + idetape_restart_speed_control(drive); + tape->restart_speed_control_req = 0; return 0; } @@ -3303,8 +5045,8 @@ { ide_drive_t *drive = get_drive_ptr (inode->i_rdev); idetape_tape_t *tape = drive->driver_data; - unsigned int minor=MINOR (inode->i_rdev); idetape_pc_t pc; + unsigned int minor=MINOR (inode->i_rdev); #if IDETAPE_DEBUG_LOG printk (KERN_INFO "Reached idetape_chrdev_release\n"); @@ -3312,19 +5054,21 @@ if (tape->chrdev_direction == idetape_direction_write) { idetape_empty_write_pipeline (drive); - tape->merge_stage = __idetape_kmalloc_stage (tape); + tape->merge_stage = __idetape_kmalloc_stage (tape, 1, 0); if (tape->merge_stage != NULL) { idetape_pad_zeros (drive, tape->tape_block_size * (tape->user_bs_factor - 1)); __idetape_kfree_stage (tape->merge_stage); tape->merge_stage = NULL; } - idetape_create_write_filemark_cmd (&pc,1); /* Write a filemark */ - if (idetape_queue_pc_tail (drive,&pc)) - printk (KERN_ERR "ide-tape: Couldn't write a filemark\n"); + idetape_write_filemark(drive); + idetape_write_eod(drive); + idetape_flush_tape_buffers (drive); + idetape_write_header(drive, minor >= 128); + idetape_flush_tape_buffers (drive); } if (tape->chrdev_direction == idetape_direction_read) { if (minor < 128) - idetape_discard_read_pipeline (drive); + idetape_discard_read_pipeline (drive, 1); else idetape_wait_for_pipeline (drive); } @@ -3334,10 +5078,15 @@ } if (minor < 128) (void) idetape_rewind_tape (drive); - - clear_bit (IDETAPE_BUSY, &tape->flags); - if (tape->chrdev_direction == idetape_direction_none) + if (tape->chrdev_direction == idetape_direction_none) { + if (tape->door_locked != DOOR_EXPLICITLY_LOCKED) { + idetape_create_prevent_cmd(drive, &pc, 0); + if (!idetape_queue_pc_tail (drive,&pc)) + tape->door_locked = DOOR_UNLOCKED; + } MOD_DEC_USE_COUNT; + } + clear_bit (IDETAPE_BUSY, &tape->flags); return 0; } @@ -3353,16 +5102,16 @@ static int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id) { struct idetape_id_gcw gcw; -#if IDETAPE_INFO_LOG +#if IDETAPE_DEBUG_LOG unsigned short mask,i; -#endif /* IDETAPE_INFO_LOG */ +#endif /* IDETAPE_DEBUG_LOG */ if (!id) return 0; *((unsigned short *) &gcw) = id->config; -#if IDETAPE_INFO_LOG +#if IDETAPE_DEBUG_LOG printk (KERN_INFO "Dumping ATAPI Identify Device tape parameters\n"); printk (KERN_INFO "Protocol Type: "); switch (gcw.protocol) { @@ -3450,7 +5199,7 @@ } else printk (KERN_INFO "According to the device, fields 64-70 are not valid.\n"); -#endif /* IDETAPE_INFO_LOG */ +#endif /* IDETAPE_DEBUG_LOG */ /* Check that we can support this device */ @@ -3470,6 +5219,153 @@ } /* + * Notify vendor ID to the OnStream tape drive + */ +static void idetape_onstream_set_vendor(ide_drive_t *drive, char *vendor) +{ + idetape_pc_t pc; + idetape_mode_parameter_header_t *header; + + idetape_create_mode_select_cmd(&pc, sizeof(*header) + 8); + pc.buffer[0] = 3 + 8; /* Mode Data Length */ + pc.buffer[1] = 0; /* Medium Type - ignoring */ + pc.buffer[2] = 0; /* Reserved */ + pc.buffer[3] = 0; /* Block Descriptor Length */ + pc.buffer[4 + 0] = 0x36 | (1 << 7); + pc.buffer[4 + 1] = 6; + pc.buffer[4 + 2] = vendor[0]; + pc.buffer[4 + 3] = vendor[1]; + pc.buffer[4 + 4] = vendor[2]; + pc.buffer[4 + 5] = vendor[3]; + pc.buffer[4 + 6] = 0; + pc.buffer[4 + 7] = 0; + if (idetape_queue_pc_tail (drive,&pc)) + printk (KERN_ERR "ide-tape: Couldn't set vendor name to %s\n", vendor); + +} + +/* + * Various unused OnStream commands + */ +#if ONSTREAM_DEBUG +static void idetape_onstream_set_retries(ide_drive_t *drive, int retries) +{ + idetape_pc_t pc; + + idetape_create_mode_select_cmd(&pc, sizeof(idetape_mode_parameter_header_t) + 4); + pc.buffer[0] = 3 + 4; + pc.buffer[1] = 0; /* Medium Type - ignoring */ + pc.buffer[2] = 0; /* Reserved */ + pc.buffer[3] = 0; /* Block Descriptor Length */ + pc.buffer[4 + 0] = 0x2f | (1 << 7); + pc.buffer[4 + 1] = 2; + pc.buffer[4 + 2] = 4; + pc.buffer[4 + 3] = retries; + if (idetape_queue_pc_tail (drive,&pc)) + printk (KERN_ERR "ide-tape: Couldn't set retries to %d\n", retries); +} +#endif + +/* + * Configure 32.5KB block size. + */ +static void idetape_onstream_configure_block_size(ide_drive_t *drive) +{ + idetape_pc_t pc; + idetape_mode_parameter_header_t *header; + idetape_block_size_page_t *bs; + + /* + * Get the current block size from the block size mode page + */ + idetape_create_mode_sense_cmd (&pc,IDETAPE_BLOCK_SIZE_PAGE); + if (idetape_queue_pc_tail (drive,&pc)) + printk (KERN_ERR "ide-tape: can't get tape block size mode page\n"); + header = (idetape_mode_parameter_header_t *) pc.buffer; + bs = (idetape_block_size_page_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t) + header->bdl); + +#if IDETAPE_DEBUG_LOG + printk(KERN_INFO "32KB play back: %s\n", bs->play32 ? "Yes" : "No"); + printk(KERN_INFO "32.5KB play back: %s\n", bs->play32_5 ? "Yes" : "No"); + printk(KERN_INFO "32KB record: %s\n", bs->record32 ? "Yes" : "No"); + printk(KERN_INFO "32.5KB record: %s\n", bs->record32_5 ? "Yes" : "No"); +#endif + + /* + * Configure default auto columns mode, 32.5KB block size + */ + bs->one = 1; + bs->play32 = 0; + bs->play32_5 = 1; + bs->record32 = 0; + bs->record32_5 = 1; + idetape_create_mode_select_cmd(&pc, sizeof(*header) + sizeof(*bs)); + if (idetape_queue_pc_tail (drive,&pc)) + printk (KERN_ERR "ide-tape: Couldn't set tape block size mode page\n"); + +#if ONSTREAM_DEBUG + /* + * In debug mode, we want to see as many errors as possible + * to test the error recovery mechanism. + */ + idetape_onstream_set_retries(drive, 0); +#endif +} + +/* + * Use INQUIRY to get the firmware revision + */ +static void idetape_get_inquiry_results(ide_drive_t *drive) +{ + char *r; + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + idetape_inquiry_result_t *inquiry; + + idetape_create_inquiry_cmd(&pc); + if (idetape_queue_pc_tail (drive,&pc)) { + printk (KERN_ERR "ide-tape: %s: can't get INQUIRY results\n", tape->name); + return; + } + inquiry = (idetape_inquiry_result_t *) pc.buffer; + memcpy(tape->vendor_id, inquiry->vendor_id, 8); + memcpy(tape->product_id, inquiry->product_id, 16); + memcpy(tape->firmware_revision, inquiry->revision_level, 4); + ide_fixstring(tape->vendor_id, 10, 0); + ide_fixstring(tape->product_id, 18, 0); + ide_fixstring(tape->firmware_revision, 6, 0); + r = tape->firmware_revision; + if (*(r + 1) == '.') + tape->firmware_revision_num = (*r - '0') * 100 + (*(r + 2) - '0') * 10 + *(r + 3) - '0'; + else if (tape->onstream) + tape->firmware_revision_num = (*r - '0') * 100 + (*(r + 1) - '0') * 10 + *(r + 2) - '0'; + printk(KERN_INFO "ide-tape: %s <-> %s: %s %s rev %s\n", drive->name, tape->name, tape->vendor_id, tape->product_id, tape->firmware_revision); +} + +/* + * Configure the OnStream ATAPI tape drive for default operation + */ +static void idetape_configure_onstream(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + if (tape->firmware_revision_num < 105) { + printk("ide-tape: %s: Old OnStream firmware revision detected (%s)\n", tape->name, tape->firmware_revision); + printk("ide-tape: %s: An upgrade to version 1.05 or above is recommended\n", tape->name); + } + + /* + * Configure 32.5KB (data+aux) block size. + */ + idetape_onstream_configure_block_size(drive); + + /* + * Set vendor name to 'LIN3' for "Linux support version 3". + */ + idetape_onstream_set_vendor(drive, "LIN3"); +} + +/* * idetape_get_mode_sense_results asks the tape about its various * parameters. In particular, we will adjust our data transfer buffer * size to the recommended value as returned by the tape. @@ -3497,17 +5393,23 @@ capabilities->buffer_size = ntohs (capabilities->buffer_size); if (!capabilities->speed) { - printk("ide-tape: %s: overriding capabilities->speed (assuming 650KB/sec)\n", drive->name); + printk(KERN_INFO "ide-tape: %s: overriding capabilities->speed (assuming 650KB/sec)\n", drive->name); capabilities->speed = 650; } if (!capabilities->max_speed) { - printk("ide-tape: %s: overriding capabilities->max_speed (assuming 650KB/sec)\n", drive->name); + printk(KERN_INFO "ide-tape: %s: overriding capabilities->max_speed (assuming 650KB/sec)\n", drive->name); capabilities->max_speed = 650; } tape->capabilities = *capabilities; /* Save us a copy */ - tape->tape_block_size = capabilities->blk512 ? 512:1024; -#if IDETAPE_INFO_LOG + if (capabilities->blk512) + tape->tape_block_size = 512; + else if (capabilities->blk1024) + tape->tape_block_size = 1024; + else if (tape->onstream && capabilities->blk32768) + tape->tape_block_size = 32768; + +#if IDETAPE_DEBUG_LOG printk (KERN_INFO "Dumping the results of the MODE SENSE packet command\n"); printk (KERN_INFO "Mode Parameter Header:\n"); printk (KERN_INFO "Mode Data Length - %d\n",header->mode_data_length); @@ -3530,12 +5432,12 @@ printk (KERN_INFO "Supports data compression - %s\n",capabilities->cmprs ? "Yes":"No"); printk (KERN_INFO "Supports 512 bytes block size - %s\n",capabilities->blk512 ? "Yes":"No"); printk (KERN_INFO "Supports 1024 bytes block size - %s\n",capabilities->blk1024 ? "Yes":"No"); - printk (KERN_INFO "Restricted byte count for PIO transfers - %s\n",capabilities->slowb ? "Yes":"No"); + printk (KERN_INFO "Supports 32768 bytes block size / Restricted byte count for PIO transfers - %s\n",capabilities->blk32768 ? "Yes":"No"); printk (KERN_INFO "Maximum supported speed in KBps - %d\n",capabilities->max_speed); printk (KERN_INFO "Continuous transfer limits in blocks - %d\n",capabilities->ctl); printk (KERN_INFO "Current speed in KBps - %d\n",capabilities->speed); printk (KERN_INFO "Buffer size - %d\n",capabilities->buffer_size*512); -#endif /* IDETAPE_INFO_LOG */ +#endif /* IDETAPE_DEBUG_LOG */ } static void idetape_add_settings(ide_drive_t *drive) @@ -3546,14 +5448,27 @@ * drive setting name read/write ioctl ioctl data type min max mul_factor div_factor data pointer set function */ ide_add_setting(drive, "buffer", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 2, &tape->capabilities.buffer_size, NULL); - ide_add_setting(drive, "pipeline_min", SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->min_pipeline, NULL); - ide_add_setting(drive, "pipeline", SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->max_stages, NULL); - ide_add_setting(drive, "pipeline_max", SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->max_pipeline, NULL); + ide_add_setting(drive, "pipeline_min", SETTING_RW, -1, -1, TYPE_INT, 2, 0xffff, tape->stage_size / 1024, 1, &tape->min_pipeline, NULL); + ide_add_setting(drive, "pipeline", SETTING_RW, -1, -1, TYPE_INT, 2, 0xffff, tape->stage_size / 1024, 1, &tape->max_stages, NULL); + ide_add_setting(drive, "pipeline_max", SETTING_RW, -1, -1, TYPE_INT, 2, 0xffff, tape->stage_size / 1024, 1, &tape->max_pipeline, NULL); ide_add_setting(drive, "pipeline_used",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->nr_stages, NULL); + ide_add_setting(drive, "pipeline_pending",SETTING_READ,-1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->nr_pending_stages, NULL); ide_add_setting(drive, "speed", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 1, &tape->capabilities.speed, NULL); ide_add_setting(drive, "stage", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1024, &tape->stage_size, NULL); ide_add_setting(drive, "tdsc", SETTING_RW, -1, -1, TYPE_INT, IDETAPE_DSC_RW_MIN, IDETAPE_DSC_RW_MAX, 1000, HZ, &tape->best_dsc_rw_frequency, NULL); ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL); + ide_add_setting(drive, "pipeline_head_speed_c",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->controlled_pipeline_head_speed, NULL); + ide_add_setting(drive, "pipeline_head_speed_u",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->uncontrolled_pipeline_head_speed, NULL); + ide_add_setting(drive, "avg_speed", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->avg_speed, NULL); + if (tape->onstream) { + ide_add_setting(drive, "cur_frames", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 1, &tape->cur_frames, NULL); + ide_add_setting(drive, "max_frames", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 1, &tape->max_frames, NULL); + ide_add_setting(drive, "insert_speed", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->insert_speed, NULL); + ide_add_setting(drive, "speed_control",SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->speed_control, NULL); + ide_add_setting(drive, "tape_still_time",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->tape_still_time, NULL); + ide_add_setting(drive, "max_insert_speed",SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->max_insert_speed, NULL); + ide_add_setting(drive, "insert_size", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->insert_size, NULL); + } } /* @@ -3570,42 +5485,48 @@ */ static void idetape_setup (ide_drive_t *drive, idetape_tape_t *tape, int minor) { - ide_hwif_t *hwif = HWIF(drive); unsigned long t1, tmid, tn, t; - u16 speed; + int speed; struct idetape_id_gcw gcw; + int stage_size; memset (tape, 0, sizeof (idetape_tape_t)); spin_lock_init(&tape->spinlock); drive->driver_data = tape; drive->ready_stat = 0; /* An ATAPI device ignores DRDY */ + if (strstr(drive->id->model, "OnStream DI-30")) + tape->onstream = 1; + drive->dsc_overlap = 1; #ifdef CONFIG_BLK_DEV_IDEPCI - /* - * These two ide-pci host adapters appear to need this disabled. - */ - if (HWIF(drive)->pci_dev != NULL && ( - (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) || - (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_TTI_HPT343))) { - drive->dsc_overlap = 0; - } else -#endif /* CONFIG_BLK_DEV_IDEPCI */ - { - drive->dsc_overlap = 1; + if (!tape->onstream && HWIF(drive)->pci_dev != NULL) { + /* + * These two ide-pci host adapters appear to need DSC overlap disabled. + * This probably needs further analysis. + */ + if ((HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) || + (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_TTI_HPT343)) { + printk(KERN_INFO "ide-tape: %s: disabling DSC overlap\n", tape->name); + drive->dsc_overlap = 0; + } } - memset (tape, 0, sizeof (idetape_tape_t)); +#endif /* CONFIG_BLK_DEV_IDEPCI */ tape->drive = drive; tape->minor = minor; tape->name[0] = 'h'; tape->name[1] = 't'; tape->name[2] = '0' + minor; tape->chrdev_direction = idetape_direction_none; tape->pc = tape->pc_stack; - tape->min_pipeline = IDETAPE_MIN_PIPELINE_STAGES; - tape->max_pipeline = IDETAPE_MAX_PIPELINE_STAGES; - tape->max_stages = tape->min_pipeline; + tape->max_insert_speed = 10000; + tape->speed_control = 1; *((unsigned short *) &gcw) = drive->id->config; if (gcw.drq_type == 1) set_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags); - idetape_get_mode_sense_results (drive); + tape->min_pipeline = tape->max_pipeline = tape->max_stages = 10; + + idetape_get_inquiry_results(drive); + idetape_get_mode_sense_results(drive); + if (tape->onstream) + idetape_configure_onstream(drive); tape->user_bs_factor = 1; tape->stage_size = tape->capabilities.ctl * tape->tape_block_size; @@ -3614,45 +5535,40 @@ tape->capabilities.ctl /= 2; tape->stage_size = tape->capabilities.ctl * tape->tape_block_size; } - tape->pages_per_stage = tape->stage_size / PAGE_SIZE; - if (tape->stage_size % PAGE_SIZE) { + stage_size = tape->stage_size; + if (tape->onstream) + stage_size = 32768 + 512; + tape->pages_per_stage = stage_size / PAGE_SIZE; + if (stage_size % PAGE_SIZE) { tape->pages_per_stage++; - tape->excess_bh_size = PAGE_SIZE - tape->stage_size % PAGE_SIZE; + tape->excess_bh_size = PAGE_SIZE - stage_size % PAGE_SIZE; } /* - * Select the "best" DSC read/write polling frequency. - * The following algorithm attempts to find a balance between - * good latency and good system throughput. It will be nice to - * have all this configurable in run time at some point. + * Select the "best" DSC read/write polling frequency + * and pipeline size. */ speed = IDE_MAX (tape->capabilities.speed, tape->capabilities.max_speed); + + tape->max_stages = speed * 1000 * 10 / tape->stage_size; + tape->min_pipeline = tape->max_stages; + tape->max_pipeline = tape->max_stages * 2; + t1 = (tape->stage_size * HZ) / (speed * 1000); tmid = (tape->capabilities.buffer_size * 32 * HZ) / (speed * 125); tn = (IDETAPE_FIFO_THRESHOLD * tape->stage_size * HZ) / (speed * 1000); - if (tape->max_stages) { - if (drive->using_dma) - t = tmid; - else { - if (hwif->drives[drive->select.b.unit ^ 1].present || hwif->next != hwif) - t = (tn + tmid) / 2; - else - t = tn; - } - } else + if (tape->max_stages) + t = tn; + else t = t1; - t = IDE_MIN (t, tmid); /* - * Ensure that the number we got makes sense. + * Ensure that the number we got makes sense; limit + * it within IDETAPE_DSC_RW_MIN and IDETAPE_DSC_RW_MAX. */ tape->best_dsc_rw_frequency = IDE_MAX (IDE_MIN (t, IDETAPE_DSC_RW_MAX), IDETAPE_DSC_RW_MIN); - if (tape->best_dsc_rw_frequency != t) { - printk (KERN_NOTICE "ide-tape: Although the recommended polling period is %lu jiffies\n", t); - printk (KERN_NOTICE "ide-tape: we will use %lu jiffies\n", tape->best_dsc_rw_frequency); - } - printk (KERN_INFO "ide-tape: %s <-> %s, %dKBps, %d*%dkB buffer, %dkB pipeline, %lums tDSC%s\n", + printk (KERN_INFO "ide-tape: %s <-> %s: %dKBps, %d*%dkB buffer, %dkB pipeline, %lums tDSC%s\n", drive->name, tape->name, tape->capabilities.speed, (tape->capabilities.buffer_size * 512) / tape->stage_size, tape->stage_size / 1024, tape->max_stages * tape->stage_size / 1024, tape->best_dsc_rw_frequency * 1000 / HZ, drive->using_dma ? ", DMA":""); @@ -3791,6 +5707,14 @@ printk (KERN_ERR "ide-tape: %s: not supported by this version of ide-tape\n", drive->name); continue; } + if (drive->scsi) { + if (strstr(drive->id->model, "OnStream DI-30")) { + printk("ide-tape: ide-scsi emulation is not supported for %s.\n", drive->id->model); + } else { + printk("ide-tape: passing drive %s to ide-scsi emulation.\n", drive->name); + continue; + } + } tape = (idetape_tape_t *) kmalloc (sizeof (idetape_tape_t), GFP_KERNEL); if (tape == NULL) { printk (KERN_ERR "ide-tape: %s: Can't allocate a tape structure\n", drive->name); @@ -3828,14 +5752,8 @@ for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++) { drive = idetape_chrdevs[minor].drive; - if (drive) { - if (idetape_cleanup (drive)) - printk (KERN_ERR "ide-tape: %s: cleanup_module() called while still busy\n", drive->name); - /* We must remove proc entries defined in this module. - Otherwise we oops while accessing these entries */ - if (drive->proc) - ide_remove_proc_entries(drive->proc, idetape_proc); - } + if (drive != NULL && idetape_cleanup (drive)) + printk (KERN_ERR "ide-tape: %s: cleanup_module() called while still busy\n", drive->name); } ide_unregister_module(&idetape_module); } diff -u --recursive --new-file v2.3.33/linux/drivers/block/ide.c linux/drivers/block/ide.c --- v2.3.33/linux/drivers/block/ide.c Tue Dec 14 01:27:23 1999 +++ linux/drivers/block/ide.c Thu Dec 16 17:46:09 1999 @@ -136,6 +136,9 @@ #include #include #include +#ifndef MODULE +#include +#endif /* MODULE */ #include #include #include @@ -1219,6 +1222,8 @@ ide_get_lock(&ide_lock, ide_intr, hwgroup); /* for atari only: POSSIBLY BROKEN HERE(?) */ + __cli(); /* necessary paranoia: ensure IRQs are masked on local CPU */ + while (!hwgroup->busy) { hwgroup->busy = 1; drive = choose_drive(hwgroup); @@ -1538,14 +1543,8 @@ } else { /* * Whack the status register, just in case we have a leftover pending IRQ. - * - * Unless we are some version of a Promise Ultra66 :: PDC20262. - * We will hang like a rock.... */ - byte skip_status = ((hwif->pci_dev->device == PCI_DEVICE_ID_PROMISE_20262) || - (hwif->pci_dev->device == PCI_DEVICE_ID_TTI_HPT366)) ? 1 : 0; - if (!skip_status) - (void) IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); + (void) IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); #endif /* CONFIG_BLK_DEV_IDEPCI */ } spin_unlock_irqrestore(&io_request_lock, flags); @@ -2337,6 +2336,7 @@ ide_add_setting(drive, "slow", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->slow, NULL); ide_add_setting(drive, "unmaskirq", drive->no_unmask ? SETTING_READ : SETTING_RW, HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, TYPE_BYTE, 0, 1, 1, 1, &drive->unmask, NULL); ide_add_setting(drive, "using_dma", SETTING_RW, HDIO_GET_DMA, HDIO_SET_DMA, TYPE_BYTE, 0, 1, 1, 1, &drive->using_dma, set_using_dma); + ide_add_setting(drive, "ide_scsi", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->scsi, NULL); } int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf) @@ -2457,54 +2457,7 @@ err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf); if (!err && set_transfer(drive, args[0], args[1], args[2])) { - -#if 1 ide_driveid_update(drive); -#else /* - * Re-read drive->id for possible DMA mode - * change (copied from ide-probe.c) - */ - struct hd_driveid *id; - unsigned long timeout, irqs, flags; - - probe_irq_off(probe_irq_on()); - irqs = probe_irq_on(); - if (IDE_CONTROL_REG) - OUT_BYTE(drive->ctl,IDE_CONTROL_REG); - ide_delay_50ms(); - OUT_BYTE(WIN_IDENTIFY, IDE_COMMAND_REG); - timeout = jiffies + WAIT_WORSTCASE; - do { - if (0 < (signed long)(jiffies - timeout)) { - if (irqs) - (void) probe_irq_off(irqs); - goto abort; /* drive timed-out */ - } - ide_delay_50ms(); /* give drive a breather */ - } while (IN_BYTE(IDE_ALTSTATUS_REG) & BUSY_STAT); - ide_delay_50ms(); /* wait for IRQ and DRQ_STAT */ - if (!OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) - goto abort; - __save_flags(flags); /* local CPU only */ - __cli(); /* local CPU only; some systems need this */ - id = kmalloc(SECTOR_WORDS*4, GFP_ATOMIC); - ide_input_data(drive, id, SECTOR_WORDS); - (void) GET_STAT(); /* clear drive IRQ */ - ide__sti(); /* local CPU only */ - __restore_flags(flags); /* local CPU only */ - ide_fix_driveid(id); - if (id && id->cyls) { - drive->id->dma_ultra = id->dma_ultra; - drive->id->dma_mword = id->dma_mword; - drive->id->dma_1word = id->dma_1word; - /* anything more ? */ -#ifdef DEBUG - printk("%s: dma_ultra=%04X, dma_mword=%04X, dma_1word=%04X\n", - drive->name, id->dma_ultra, id->dma_mword, id->dma_1word); -#endif - kfree(id); - } -#endif } abort: if (copy_to_user((void *)arg, argbuf, argsize)) @@ -2690,7 +2643,9 @@ * "hdx=flash" : allows for more than one ata_flash disk to be * registered. In most cases, only one device * will be present. - * + * "hdx=scsi" : the return of the ide-scsi flag, this is useful for + * allowwing ide-floppy, ide-tape, and ide-cdrom|writers + * to use ide-scsi emulation on a device specific option. * "idebus=xx" : inform IDE driver of VESA/PCI bus speed in MHz, * where "xx" is between 20 and 66 inclusive, * used when tuning chipset PIO modes. @@ -2752,7 +2707,7 @@ * "idex=dc4030" : probe/support Promise DC4030VL interface * "ide=doubler" : probe/support IDE doublers on Amiga */ -void __init ide_setup (char *s) +int __init ide_setup (char *s) { int i, vals[3]; ide_hwif_t *hwif; @@ -2769,7 +2724,7 @@ printk("ide: Enabled support for IDE doublers\n"); ide_doubler = 1; - return; + return 0; } #endif /* CONFIG_BLK_DEV_IDEDOUBLER */ @@ -2782,7 +2737,7 @@ const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom", "serialize", "autotune", "noautotune", "slow", "swapdata", "bswap", "flash", - "remap", "noremap", NULL}; + "remap", "noremap", "scsi", NULL}; unit = s[2] - 'a'; hw = unit / MAX_DRIVES; unit = unit % MAX_DRIVES; @@ -2845,6 +2800,14 @@ case -13: /* "noremap" */ drive->remap_0_to_1 = 2; goto done; + case -14: /* "scsi" */ +#if defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) + drive->scsi = 1; + goto done; +#else + drive->scsi = 0; + goto bad_option; +#endif /* defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) */ case 3: /* cyl,head,sect */ drive->media = ide_disk; drive->cyl = drive->bios_cyl = vals[0]; @@ -3076,27 +3039,20 @@ case 0: goto bad_option; default: printk(" -- SUPPORT NOT CONFIGURED IN THIS KERNEL\n"); - return; + return 0; } } bad_option: printk(" -- BAD OPTION\n"); - return; + return 0; bad_hwif: printk("-- NOT SUPPORTED ON ide%d", hw); done: printk("\n"); + return 0; } /* - * __setup("ide", ide_setup); - * #ifdef CONFIG_BLK_DEV_VIA82CXXX - * __setup("splitfifo", ide_setup); - * #endif - * __setup("hd", ide_setup); - */ - -/* * probe_for_hwifs() finds/initializes "known" IDE interfaces */ static void __init probe_for_hwifs (void) @@ -3517,10 +3473,6 @@ return 0; } -#ifdef MODULE -char *options = NULL; -MODULE_PARM(options,"s"); - static void __init parse_options (char *line) { char *next = line; @@ -3534,11 +3486,16 @@ #ifdef CONFIG_BLK_DEV_VIA82CXXX !strncmp(line,"splitfifo",9) || #endif /* CONFIG_BLK_DEV_VIA82CXXX */ + !strncmp(line,"hdxlun",6) || (!strncmp(line,"hd",2) && line[2] != '=')) - ide_setup(line); + (void) ide_setup(line); } } +#ifdef MODULE +char *options = NULL; +MODULE_PARM(options,"s"); + int init_module (void) { parse_options(options); @@ -3560,4 +3517,14 @@ proc_ide_destroy(); #endif } + +#else /* !MODULE */ + +static int parse_ide_setup (char *line) +{ + parse_options(line); + return 0; +} +__setup("", parse_ide_setup); + #endif /* MODULE */ diff -u --recursive --new-file v2.3.33/linux/drivers/block/ns87415.c linux/drivers/block/ns87415.c --- v2.3.33/linux/drivers/block/ns87415.c Mon Aug 9 10:23:09 1999 +++ linux/drivers/block/ns87415.c Tue Dec 14 23:03:50 1999 @@ -97,6 +97,10 @@ return 0; ns87415_prepare_drive(drive, 0); /* DMA failed: select PIO xfer */ return 1; + case ide_dma_check: + if (drive->media != ide_disk) + return ide_dmaproc(ide_dma_off_quietly, drive); + /* Fallthrough... */ default: return ide_dmaproc(func, drive); /* use standard DMA stuff */ } diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/aten.c linux/drivers/block/paride/aten.c --- v2.3.33/linux/drivers/block/paride/aten.c Thu May 14 19:11:48 1998 +++ linux/drivers/block/paride/aten.c Thu Dec 16 13:57:04 1999 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/bpck.c linux/drivers/block/paride/bpck.c --- v2.3.33/linux/drivers/block/paride/bpck.c Thu Aug 20 15:17:05 1998 +++ linux/drivers/block/paride/bpck.c Thu Dec 16 13:57:04 1999 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "paride.h" diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/comm.c linux/drivers/block/paride/comm.c --- v2.3.33/linux/drivers/block/paride/comm.c Thu May 14 19:11:48 1998 +++ linux/drivers/block/paride/comm.c Thu Dec 16 13:57:04 1999 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "paride.h" diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/dstr.c linux/drivers/block/paride/dstr.c --- v2.3.33/linux/drivers/block/paride/dstr.c Thu May 14 19:11:48 1998 +++ linux/drivers/block/paride/dstr.c Thu Dec 16 13:57:04 1999 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "paride.h" diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/epat.c linux/drivers/block/paride/epat.c --- v2.3.33/linux/drivers/block/paride/epat.c Thu May 14 19:11:48 1998 +++ linux/drivers/block/paride/epat.c Thu Dec 16 13:57:04 1999 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "paride.h" diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/epia.c linux/drivers/block/paride/epia.c --- v2.3.33/linux/drivers/block/paride/epia.c Wed Jun 24 14:27:18 1998 +++ linux/drivers/block/paride/epia.c Thu Dec 16 13:57:04 1999 @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "paride.h" diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/fit2.c linux/drivers/block/paride/fit2.c --- v2.3.33/linux/drivers/block/paride/fit2.c Thu May 14 19:11:48 1998 +++ linux/drivers/block/paride/fit2.c Thu Dec 16 13:57:04 1999 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "paride.h" diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/fit3.c linux/drivers/block/paride/fit3.c --- v2.3.33/linux/drivers/block/paride/fit3.c Sat Jun 13 12:08:19 1998 +++ linux/drivers/block/paride/fit3.c Thu Dec 16 13:57:04 1999 @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "paride.h" diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/friq.c linux/drivers/block/paride/friq.c --- v2.3.33/linux/drivers/block/paride/friq.c Tue Dec 22 08:29:00 1998 +++ linux/drivers/block/paride/friq.c Thu Dec 16 13:57:04 1999 @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "paride.h" diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/frpw.c linux/drivers/block/paride/frpw.c --- v2.3.33/linux/drivers/block/paride/frpw.c Tue Dec 22 08:29:00 1998 +++ linux/drivers/block/paride/frpw.c Thu Dec 16 13:57:04 1999 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "paride.h" diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/kbic.c linux/drivers/block/paride/kbic.c --- v2.3.33/linux/drivers/block/paride/kbic.c Thu May 14 19:11:48 1998 +++ linux/drivers/block/paride/kbic.c Thu Dec 16 13:57:04 1999 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "paride.h" diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/ktti.c linux/drivers/block/paride/ktti.c --- v2.3.33/linux/drivers/block/paride/ktti.c Thu May 14 19:11:48 1998 +++ linux/drivers/block/paride/ktti.c Thu Dec 16 13:57:04 1999 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "paride.h" diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/on20.c linux/drivers/block/paride/on20.c --- v2.3.33/linux/drivers/block/paride/on20.c Thu May 14 19:11:48 1998 +++ linux/drivers/block/paride/on20.c Thu Dec 16 13:57:04 1999 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "paride.h" diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/on26.c linux/drivers/block/paride/on26.c --- v2.3.33/linux/drivers/block/paride/on26.c Tue Dec 22 08:29:00 1998 +++ linux/drivers/block/paride/on26.c Thu Dec 16 13:57:04 1999 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "paride.h" diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/pg.c linux/drivers/block/paride/pg.c --- v2.3.33/linux/drivers/block/paride/pg.c Fri Oct 15 15:25:13 1999 +++ linux/drivers/block/paride/pg.c Thu Dec 16 13:57:04 1999 @@ -169,6 +169,7 @@ #include #include #include +#include #include diff -u --recursive --new-file v2.3.33/linux/drivers/block/paride/pt.c linux/drivers/block/paride/pt.c --- v2.3.33/linux/drivers/block/paride/pt.c Fri Oct 15 15:25:13 1999 +++ linux/drivers/block/paride/pt.c Thu Dec 16 13:57:04 1999 @@ -147,6 +147,7 @@ #include #include #include +#include #include diff -u --recursive --new-file v2.3.33/linux/drivers/block/pdc202xx.c linux/drivers/block/pdc202xx.c --- v2.3.33/linux/drivers/block/pdc202xx.c Thu Nov 18 20:25:37 1999 +++ linux/drivers/block/pdc202xx.c Thu Dec 16 20:33:10 1999 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/pdc202xx.c Version 0.27 Sept. 3, 1999 + * linux/drivers/block/pdc202xx.c Version 0.28 Dec. 13, 1999 * * Copyright (C) 1998-99 Andre Hedrick (andre@suse.com) * May be copied or modified under the terms of the GNU General Public License @@ -221,7 +221,7 @@ int err; unsigned int drive_conf; - byte drive_pci, speed_ok = 0; + byte drive_pci; byte test1, test2, speed = -1; byte AP, BP, CP, DP, TB, TC; unsigned short EP; @@ -279,20 +279,16 @@ switch(drive_number) { case 0: drive_pci = 0x60; pci_read_config_dword(dev, drive_pci, &drive_conf); - if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) { - speed_ok = 1; + if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) goto chipset_is_set; - } pci_read_config_byte(dev, (drive_pci), &test1); if (!(test1 & SYNC_ERRDY_EN)) pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN); break; case 1: drive_pci = 0x64; pci_read_config_dword(dev, drive_pci, &drive_conf); - if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) { - speed_ok = 1; + if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) goto chipset_is_set; - } pci_read_config_byte(dev, 0x60, &test1); pci_read_config_byte(dev, (drive_pci), &test2); if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN)) @@ -300,20 +296,16 @@ break; case 2: drive_pci = 0x68; pci_read_config_dword(dev, drive_pci, &drive_conf); - if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) { - speed_ok = 1; + if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) goto chipset_is_set; - } pci_read_config_byte(dev, (drive_pci), &test1); if (!(test1 & SYNC_ERRDY_EN)) pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN); break; case 3: drive_pci = 0x6c; pci_read_config_dword(dev, drive_pci, &drive_conf); - if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) { - speed_ok = 1; + if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) goto chipset_is_set; - } pci_read_config_byte(dev, 0x68, &test1); pci_read_config_byte(dev, (drive_pci), &test2); if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN)) @@ -411,8 +403,7 @@ decode_registers(REG_D, DP); #endif /* PDC202XX_DECODE_REGISTER_INFO */ - if (!speed_ok) - err = ide_config_drive_speed(drive, speed); + err = ide_config_drive_speed(drive, speed); #if PDC202XX_DEBUG_DRIVE_INFO printk("%s: %s drive%d 0x%08x ", @@ -670,6 +661,8 @@ if (hwif->dma_base) { hwif->dmaproc = &pdc202xx_dmaproc; + hwif->drives[0].autotune = 0; + hwif->drives[1].autotune = 0; } else { hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; diff -u --recursive --new-file v2.3.33/linux/drivers/block/piix.c linux/drivers/block/piix.c --- v2.3.33/linux/drivers/block/piix.c Tue Nov 23 22:42:20 1999 +++ linux/drivers/block/piix.c Tue Dec 14 23:03:50 1999 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/piix.c Version 0.27 Sept. 3, 1999 + * linux/drivers/block/piix.c Version 0.28 Dec. 13, 1999 * * Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer * Copyright (C) 1998-1999 Andre Hedrick (andre@suse.com) @@ -82,7 +82,9 @@ static int piix_get_info (char *buffer, char **addr, off_t offset, int count) { /* int rc; */ - int piix_who = (bmide_dev->device == PCI_DEVICE_ID_INTEL_82371AB) ? 4 : 3; + int piix_who = ((bmide_dev->device == PCI_DEVICE_ID_INTEL_82801AA_1) || + (bmide_dev->device == PCI_DEVICE_ID_INTEL_82801AB_1) || + (bmide_dev->device == PCI_DEVICE_ID_INTEL_82371AB)) ? 4 : 3; char *p = buffer; p += sprintf(p, "\n Intel PIIX%d Chipset.\n", piix_who); p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n\n"); @@ -186,11 +188,11 @@ #ifdef CONFIG_BLK_DEV_PIIX_TUNING -static int piix_config_drive_for_dma(ide_drive_t *drive) +static int piix_config_drive_for_dma (ide_drive_t *drive) { - struct hd_driveid *id = drive->id; - ide_hwif_t *hwif = HWIF(drive); - struct pci_dev *dev = hwif->pci_dev; + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; int sitre; short reg4042, reg44, reg48, reg4a; @@ -198,7 +200,10 @@ int u_speed; byte maslave = hwif->channel ? 0x42 : 0x40; - int ultra = (dev->device == PCI_DEVICE_ID_INTEL_82371AB) ? 1 : 0; + byte udma_66 = ((id->word93 & 0x2000) && (hwif->udma_four)) ? 1 : 0; + int ultra = ((dev->device == PCI_DEVICE_ID_INTEL_82371AB) || + (dev->device == PCI_DEVICE_ID_INTEL_82801AA_1)) ? 1 : 0; + int ultra66 = (dev->device == PCI_DEVICE_ID_INTEL_82801AB_1) ? 1 : 0; int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); int a_speed = 2 << (drive_number * 4); int u_flag = 1 << drive_number; @@ -264,7 +269,7 @@ printk("\n"); #endif /* PIIX_DEBUG_DRIVE_INFO */ - return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_off : + return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_on : ((id->dma_ultra >> 8) & 7) ? ide_dma_on : ((id->dma_mword >> 8) & 7) ? ide_dma_on : ((id->dma_1word >> 8) & 7) ? ide_dma_on : @@ -296,6 +301,13 @@ return 0; } +unsigned int __init ata66_piix (ide_hwif_t *hwif) +{ + if (0) + return 1; + return 0; +} + void __init ide_init_piix (ide_hwif_t *hwif) { hwif->tuneproc = &piix_tune_drive; @@ -310,4 +322,6 @@ hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; } + if (!hwif->irq) + hwif->irq = hwif->channel ? 15 : 14; } diff -u --recursive --new-file v2.3.33/linux/drivers/block/q40ide.c linux/drivers/block/q40ide.c --- v2.3.33/linux/drivers/block/q40ide.c Mon Aug 9 12:32:28 1999 +++ linux/drivers/block/q40ide.c Tue Dec 14 23:03:50 1999 @@ -22,7 +22,7 @@ #include #include -#include "ide.h" +#include /* * Bases of the IDE interfaces diff -u --recursive --new-file v2.3.33/linux/drivers/block/sis5513.c linux/drivers/block/sis5513.c --- v2.3.33/linux/drivers/block/sis5513.c Tue Nov 23 22:42:20 1999 +++ linux/drivers/block/sis5513.c Tue Dec 14 23:03:50 1999 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/sis5513.c Version 0.07 Sept. 3, 1999 + * linux/drivers/block/sis5513.c Version 0.08 Dec. 13, 1999 * * Copyright (C) 1999 Andre Hedrick (andre@suse.com) * May be copied or modified under the terms of the GNU General Public License @@ -266,15 +266,13 @@ pci_read_config_byte(dev, drive_pci|0x01, &test2); } - if ((id->dma_ultra & 0x0010) && (ultra) && - (udma_66) && (four_two)) { + if ((id->dma_ultra & 0x0010) && (ultra) && (udma_66) && (four_two)) { if (!(test2 & 0x90)) { pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask); pci_write_config_byte(dev, drive_pci|0x01, test2|0x90); } speed = XFER_UDMA_4; - } else if ((id->dma_ultra & 0x0008) && (ultra) && - (udma_66) && (four_two)) { + } else if ((id->dma_ultra & 0x0008) && (ultra) && (udma_66) && (four_two)) { if (!(test2 & 0xA0)) { pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask); pci_write_config_byte(dev, drive_pci|0x01, test2|0xA0); diff -u --recursive --new-file v2.3.33/linux/drivers/block/sl82c105.c linux/drivers/block/sl82c105.c --- v2.3.33/linux/drivers/block/sl82c105.c Tue Dec 7 09:32:43 1999 +++ linux/drivers/block/sl82c105.c Tue Dec 14 23:03:50 1999 @@ -79,13 +79,13 @@ BUSY_STAT|DRQ_STAT|ERR_STAT, WAIT_CMD)) { printk("%s: drive not ready for command\n", drive->name); - return 1; + return 1; /* return startstop; ?? */ } if (IDE_CONTROL_REG) OUT_BYTE(drive->ctl, IDE_CONTROL_REG); } - return 0; + return 0; /* return startstop; ?? */ } /* diff -u --recursive --new-file v2.3.33/linux/drivers/block/via82cxxx.c linux/drivers/block/via82cxxx.c --- v2.3.33/linux/drivers/block/via82cxxx.c Tue Nov 23 22:42:20 1999 +++ linux/drivers/block/via82cxxx.c Tue Dec 14 23:03:50 1999 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/via82cxxx.c Version 0.05 Sept. 03, 1999 + * linux/drivers/block/via82cxxx.c Version 0.06 Dec. 13, 1999 * * Copyright (C) 1998-99 Michel Aubry, Maintainer * Copyright (C) 1999 Jeff Garzik, MVP4 Support (jgarzik@pobox.com) @@ -542,6 +542,18 @@ void __init ide_init_via82cxxx (ide_hwif_t *hwif) { set_via_timings(hwif); +#if 0 + hwif->tuneproc = &via82cxxx_tune_drive; + if (hwif->dma_base) { + hwif->dmaproc = &via82cxxx_dmaproc; + hwif->drives[0].autotune = 0; + hwif->drives[1].autotune = 0; + } else { + hwif->autodma = 0; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + } +#endif } /* diff -u --recursive --new-file v2.3.33/linux/drivers/char/Config.in linux/drivers/char/Config.in --- v2.3.33/linux/drivers/char/Config.in Tue Dec 14 01:27:23 1999 +++ linux/drivers/char/Config.in Thu Dec 16 13:59:37 1999 @@ -62,6 +62,8 @@ dep_tristate 'Support for user-space parallel port device drivers' CONFIG_PPDEV $CONFIG_PARPORT fi +source drivers/i2c/Config.in + mainmenu_option next_comment comment 'Mice' tristate 'Bus Mouse Support' CONFIG_BUSMOUSE @@ -227,12 +229,12 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate '/dev/agpgart (AGP Support) (EXPERIMENTAL)' CONFIG_AGP if [ "$CONFIG_AGP" != "n" ]; then - bool ' Intel 440LX/BX/GX support' CONFIG_AGP_INTEL - bool ' Intel I810/I810 DC100/I810e support' CONFIG_AGP_I810 - bool ' VIA VP3/MVP3/Apollo Pro support' CONFIG_AGP_VIA - bool ' AMD Irongate support' CONFIG_AGP_AMD - bool ' Generic SiS support' CONFIG_AGP_SIS - bool ' ALI M1541 support' CONFIG_AGP_ALI + bool ' Intel 440LX/BX/GX support' CONFIG_AGP_INTEL + bool ' Intel I810/I810 DC100/I810e support' CONFIG_AGP_I810 + bool ' VIA VP3/MVP3/Apollo Pro support' CONFIG_AGP_VIA + bool ' AMD Irongate support' CONFIG_AGP_AMD + bool ' Generic SiS support' CONFIG_AGP_SIS + bool ' ALI M1541 support' CONFIG_AGP_ALI fi fi endmenu diff -u --recursive --new-file v2.3.33/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.3.33/linux/drivers/char/Makefile Tue Dec 14 01:27:23 1999 +++ linux/drivers/char/Makefile Thu Dec 16 13:59:38 1999 @@ -23,35 +23,54 @@ O_OBJS := tty_io.o n_tty.o tty_ioctl.o mem.o random.o raw.o OX_OBJS := pty.o misc.o +KEYMAP =defkeymap.o +KEYBD =pc_keyb.o +CONSOLE =console.o +SERIAL =serial.o + +ifeq ($(ARCH),m68k) + KEYMAP = + KEYBD = +endif + +ifeq ($(ARCH),arm) + KEYMAP = + KEYBD = + CONSOLE = + SERIAL = +endif + +ifneq ($(CONFIG_SUN_SERIAL),) + SERIAL = +endif + ifdef CONFIG_VT O_OBJS += vt.o vc_screen.o consolemap.o consolemap_deftbl.o -OX_OBJS += console.o selection.o +OX_OBJS += $(CONSOLE) selection.o endif ifeq ($(CONFIG_SERIAL),y) - ifeq ($(CONFIG_SUN_SERIAL),) - OX_OBJS += serial.o - endif +OX_OBJS += $(SERIAL) else ifeq ($(CONFIG_SERIAL),m) - ifeq ($(CONFIG_SUN_SERIAL),) - MX_OBJS += serial.o - endif + MX_OBJS += $(SERIAL) endif endif -ifndef CONFIG_SUN_KEYBOARD -ifdef CONFIG_VT -OX_OBJS += keyboard.o -endif - ifneq ($(ARCH),m68k) - O_OBJS += pc_keyb.o defkeymap.o - endif -else -ifdef CONFIG_PCI -O_OBJS += defkeymap.o -OX_OBJS += keyboard.o +ifeq ($(CONFIG_SERIAL_21285),y) +O_OBJS += serial_21285.o endif + +ifndef CONFIG_SUN_KEYBOARD + ifdef CONFIG_VT + OX_OBJS += keyboard.o + O_OBJS += $(KEYMAP) $(KEYBD) + endif +else + ifdef CONFIG_PCI + OX_OBJS += keyboard.o + O_OBJS += $(KEYMAP) + endif endif ifdef CONFIG_MAGIC_SYSRQ @@ -360,12 +379,12 @@ endif ifeq ($(CONFIG_VIDEO_BT848),y) -O_OBJS += bttv.o msp3400.o +O_OBJS += bttv.o msp3400.o tda8425.o tda9855.o tea6300.o L_I2C=y L_TUNERS=y else ifeq ($(CONFIG_VIDEO_BT848),m) - M_OBJS += bttv.o msp3400.o + M_OBJS += bttv.o msp3400.o tda8425.o tda9855.o tea6300.o M_I2C=y M_TUNERS=y endif @@ -614,10 +633,10 @@ # set when a framegrabber implements i2c support ifeq ($(L_I2C),y) -OX_OBJS += i2c.o +OX_OBJS += i2c-old.o else ifeq ($(M_I2C),y) - MX_OBJS += i2c.o + MX_OBJS += i2c-old.o endif endif diff -u --recursive --new-file v2.3.33/linux/drivers/char/audiochip.h linux/drivers/char/audiochip.h --- v2.3.33/linux/drivers/char/audiochip.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/audiochip.h Thu Dec 16 13:59:38 1999 @@ -0,0 +1,60 @@ +#ifndef AUDIOCHIP_H +#define AUDIOCHIP_H + +/* ---------------------------------------------------------------------- */ + +#define MIN(a,b) (((a)>(b))?(b):(a)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +/* v4l device was opened in Radio mode */ +#define AUDC_SET_RADIO _IO('m',2) +/* select from TV,radio,extern,MUTE */ +#define AUDC_SET_INPUT _IOW('m',17,int) + +/* all the stuff below is obsolete and just here for reference. I'll + * remove it once the driver is tested and works fine. + * + * Instead creating alot of tiny API's for all kinds of different + * chips, we'll just pass throuth the v4l ioctl structs (v4l2 not + * yet...). It is a bit less flexible, but most/all used i2c chips + * make sense in v4l context only. So I think that's acceptable... + */ + +#if 0 + +/* TODO (if it is ever [to be] accessible in the V4L[2] spec): + * maybe fade? (back/front) + * notes: + * NEWCHANNEL and SWITCH_MUTE are here because the MSP3400 has a special + * routine to go through when it tunes in to a new channel before turning + * back on the sound. + * Either SET_RADIO, NEWCHANNEL, and SWITCH_MUTE or SET_INPUT need to be + * implemented (MSP3400 uses SET_RADIO to select inputs, and SWITCH_MUTE for + * channel-change mute -- TEA6300 et al use SET_AUDIO to select input [TV, + * radio, external, or MUTE]). If both methods are implemented, you get a + * cookie for doing such a good job! :) + */ + +#define AUDC_SET_TVNORM _IOW('m',1,int) /* TV mode + PAL/SECAM/NTSC */ +#define AUDC_NEWCHANNEL _IO('m',3) /* indicate new chan - off mute */ + +#define AUDC_GET_VOLUME_LEFT _IOR('m',4,__u16) +#define AUDC_GET_VOLUME_RIGHT _IOR('m',5,__u16) +#define AUDC_SET_VOLUME_LEFT _IOW('m',6,__u16) +#define AUDC_SET_VOLUME_RIGHT _IOW('m',7,__u16) + +#define AUDC_GET_STEREO _IOR('m',8,__u16) +#define AUDC_SET_STEREO _IOW('m',9,__u16) + +#define AUDC_GET_DC _IOR('m',10,__u16)/* ??? */ + +#define AUDC_GET_BASS _IOR('m',11,__u16) +#define AUDC_SET_BASS _IOW('m',12,__u16) +#define AUDC_GET_TREBLE _IOR('m',13,__u16) +#define AUDC_SET_TREBLE _IOW('m',14,__u16) + +#define AUDC_GET_UNIT _IOR('m',15,int) /* ??? - unimplemented in MSP3400 */ +#define AUDC_SWITCH_MUTE _IO('m',16) /* turn on mute */ +#endif + +#endif /* AUDIOCHIP_H */ diff -u --recursive --new-file v2.3.33/linux/drivers/char/bttv.c linux/drivers/char/bttv.c --- v2.3.33/linux/drivers/char/bttv.c Tue Nov 23 22:42:20 1999 +++ linux/drivers/char/bttv.c Thu Dec 16 13:59:38 1999 @@ -1,9 +1,9 @@ - -/* +/* bttv - Bt848 frame grabber driver Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) & Marcus Metzler (mocm@thp.uni-koeln.de) + (c) 1999 Gerd Knorr 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 @@ -21,7 +21,6 @@ */ #include -#include #include #include #include @@ -41,43 +40,53 @@ #include #include #include - -#include +#include +#include #include -#include #include +#include +#include + + #include "bttv.h" #include "tuner.h" -#define DEBUG(x) /* Debug driver */ -#define IDEBUG(x) /* Debug interrupt handler */ +#define DEBUG(x) /* Debug driver */ +#define IDEBUG(x) /* Debug interrupt handler */ +#define MIN(a,b) (((a)>(b))?(b):(a)) +#define MAX(a,b) (((a)>(b))?(a):(b)) /* Anybody who uses more than four? */ #define BTTV_MAX 4 - static void bt848_set_risc_jmps(struct bttv *btv); -static unsigned int vidmem=0; /* manually set video mem address */ -static int triton1=0; -#ifndef USE_PLL -/* 0=no pll, 1=28MHz, 2=34MHz */ -#define USE_PLL 0 -#endif -#ifndef CARD_DEFAULT -/* card type (see bttv.h) 0=autodetect */ -#define CARD_DEFAULT 0 -#endif +static int bttv_num; /* number of Bt848s in use */ +static struct bttv bttvs[BTTV_MAX]; + -static unsigned long remap[BTTV_MAX]; /* remap Bt848 */ +/* insmod args */ +MODULE_PARM(triton1,"i"); +MODULE_PARM(remap,"1-4i"); +MODULE_PARM(radio,"1-4i"); +MODULE_PARM(card,"1-4i"); +MODULE_PARM(pll,"1-4i"); +MODULE_PARM(bigendian,"i"); +MODULE_PARM(fieldnr,"i"); + +#if defined(__sparc__) || defined(__powerpc__) +static unsigned int bigendian=1; +#else +static unsigned int bigendian=0; +#endif +static int triton1=0; +static unsigned long remap[BTTV_MAX]; static unsigned int radio[BTTV_MAX]; -static unsigned int card[BTTV_MAX] = { CARD_DEFAULT, CARD_DEFAULT, - CARD_DEFAULT, CARD_DEFAULT }; -static unsigned int pll[BTTV_MAX] = { USE_PLL, USE_PLL, USE_PLL, USE_PLL }; +static unsigned int card[BTTV_MAX] = { 0, 0, 0, 0 }; +static unsigned int pll[BTTV_MAX] = { 0, 0, 0, 0}; +static unsigned int fieldnr = 0; -static int bttv_num; /* number of Bt848s in use */ -static struct bttv bttvs[BTTV_MAX]; #define I2C_TIMING (0x7<<4) #define I2C_DELAY 10 @@ -86,9 +95,9 @@ { btwrite((CTRL<<1)|(DATA), BT848_I2C); udelay(I2C_DELAY); } #define I2C_GET() (btread(BT848_I2C)&1) -#define EEPROM_WRITE_DELAY 20000 #define BURSTOFFSET 76 + /*******************************/ /* Memory management functions */ /*******************************/ @@ -121,9 +130,8 @@ if (!pmd_none(*pmd)) { ptep = pte_offset(pmd, adr); pte = *ptep; - /* Note; page_address will panic for us if the page is high */ if(pte_present(pte)) - ret = page_address(pte_page(pte))|(adr&(PAGE_SIZE-1)); + ret = (page_address(pte_page(pte))|(adr&(PAGE_SIZE-1))); } } MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); @@ -205,12 +213,6 @@ } } -MODULE_PARM(vidmem,"i"); -MODULE_PARM(triton1,"i"); -MODULE_PARM(remap,"1-4i"); -MODULE_PARM(radio,"1-4i"); -MODULE_PARM(card,"1-4i"); -MODULE_PARM(pll,"1-4i"); /* @@ -223,7 +225,7 @@ static int fbuffer_alloc(struct bttv *btv) { if(!btv->fbuffer) - btv->fbuffer=(unsigned char *) rvmalloc(2*BTTV_MAX_FBUF); + btv->fbuffer=(unsigned char *) rvmalloc(MAX_GBUFFERS*BTTV_MAX_FBUF); else printk(KERN_ERR "bttv%d: Double alloc of fbuffer!\n", btv->nr); @@ -236,194 +238,204 @@ /* ----------------------------------------------------------------------- */ /* I2C functions */ -/* software I2C functions */ - -static void i2c_setlines(struct i2c_bus *bus,int ctrl,int data) +static void bttv_bit_setscl(void *data, int state) { - struct bttv *btv = (struct bttv*)bus->data; - btwrite((ctrl<<1)|data, BT848_I2C); - btread(BT848_I2C); /* flush buffers */ - udelay(I2C_DELAY); + struct bttv *btv = (struct bttv*)data; + + if (state) + btv->i2c_state |= 0x02; + else + btv->i2c_state &= ~0x02; + btwrite(btv->i2c_state, BT848_I2C); + btread(BT848_I2C); } -static int i2c_getdataline(struct i2c_bus *bus) +static void bttv_bit_setsda(void *data, int state) { - struct bttv *btv = (struct bttv*)bus->data; - return btread(BT848_I2C)&1; + struct bttv *btv = (struct bttv*)data; + + if (state) + btv->i2c_state |= 0x01; + else + btv->i2c_state &= ~0x01; + btwrite(btv->i2c_state, BT848_I2C); + btread(BT848_I2C); } -/* hardware I2C functions */ +static int bttv_bit_getscl(void *data) +{ + struct bttv *btv = (struct bttv*)data; + int state; + + state = btread(BT848_I2C) & 0x02 ? 1 : 0; + return state; +} -/* read I2C */ -static int I2CRead(struct i2c_bus *bus, unsigned char addr) +static int bttv_bit_getsda(void *data) { - u32 i; - u32 stat; - struct bttv *btv = (struct bttv*)bus->data; - - /* clear status bit ; BT848_INT_RACK is ro */ - btwrite(BT848_INT_I2CDONE, BT848_INT_STAT); - - btwrite(((addr & 0xff) << 24) | btv->i2c_command, BT848_I2C); - - /* - * Timeout for I2CRead is 1 second (this should be enough, really!) - */ - for (i=1000; i; i--) - { - stat=btread(BT848_INT_STAT); - if (stat & BT848_INT_I2CDONE) - break; - mdelay(1); - } - - if (!i) - { - printk(KERN_DEBUG "bttv%d: I2CRead timeout\n", - btv->nr); - return -1; - } - if (!(stat & BT848_INT_RACK)) - return -2; - - i=(btread(BT848_I2C)>>8)&0xff; - return i; + struct bttv *btv = (struct bttv*)data; + int state; + + state = btread(BT848_I2C) & 0x01; + return state; } -/* set both to write both bytes, reset it to write only b1 */ +static void bttv_inc_use(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} -static int I2CWrite(struct i2c_bus *bus, unsigned char addr, unsigned char b1, - unsigned char b2, int both) +static void bttv_dec_use(struct i2c_adapter *adap) { - u32 i; - u32 data; - u32 stat; - struct bttv *btv = (struct bttv*)bus->data; - - /* clear status bit; BT848_INT_RACK is ro */ - btwrite(BT848_INT_I2CDONE, BT848_INT_STAT); - - data=((addr & 0xff) << 24) | ((b1 & 0xff) << 16) | btv->i2c_command; - if (both) - { - data|=((b2 & 0xff) << 8); - data|=BT848_I2C_W3B; - } - - btwrite(data, BT848_I2C); + MOD_DEC_USE_COUNT; +} - for (i=0x1000; i; i--) - { - stat=btread(BT848_INT_STAT); - if (stat & BT848_INT_I2CDONE) - break; - mdelay(1); - } - - if (!i) - { - printk(KERN_DEBUG "bttv%d: I2CWrite timeout\n", - btv->nr); - return -1; +static void call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg) +{ + int i; + + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (NULL == btv->i2c_clients[i]) + continue; + if (NULL == btv->i2c_clients[i]->driver->command) + continue; + btv->i2c_clients[i]->driver->command( + btv->i2c_clients[i],cmd,arg); } - if (!(stat & BT848_INT_RACK)) - return -2; - - return 0; } -/* read EEPROM */ -static void readee(struct i2c_bus *bus, unsigned char *eedata) +static int attach_inform(struct i2c_client *client) { - int i, k; - - if (I2CWrite(bus, 0xa0, 0, -1, 0)<0) - { - printk(KERN_WARNING "bttv: readee error\n"); - return; - } - - for (i=0; i<256; i++) - { - k=I2CRead(bus, 0xa1); - if (k<0) - { - printk(KERN_WARNING "bttv: readee error\n"); + struct bttv *btv = (struct bttv*)client->adapter->data; + int i; + + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (btv->i2c_clients[i] == NULL || + btv->i2c_clients[i]->driver->id == client->driver->id) { + btv->i2c_clients[i] = client; break; } - eedata[i]=k; } + if (btv->tuner_type != -1) + call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type); + printk("bttv%d: i2c attach [%s]\n",btv->nr,client->name); + return 0; } -/* write EEPROM */ -static void writeee(struct i2c_bus *bus, unsigned char *eedata) +static int detach_inform(struct i2c_client *client) { + struct bttv *btv = (struct bttv*)client->adapter->data; int i; - - for (i=0; i<256; i++) - { - if (I2CWrite(bus, 0xa0, i, eedata[i], 1)<0) - { - printk(KERN_WARNING "bttv: writeee error (%d)\n", i); + + printk("bttv%d: i2c detach [%s]\n",btv->nr,client->name); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (NULL != btv->i2c_clients[i] && + btv->i2c_clients[i]->driver->id == client->driver->id) { + btv->i2c_clients[i] = NULL; break; } - udelay(EEPROM_WRITE_DELAY); } + return 0; } -static void attach_inform(struct i2c_bus *bus, int id) +static struct i2c_algo_bit_data i2c_algo_template = { + NULL, + bttv_bit_setsda, + bttv_bit_setscl, + bttv_bit_getsda, + bttv_bit_getscl, + 10, 10, 100, +}; + +static struct i2c_adapter i2c_adap_template = { + "bt848", + I2C_HW_B_BT848, + NULL, + NULL, + bttv_inc_use, + bttv_dec_use, + attach_inform, + detach_inform, + NULL, +}; + +static struct i2c_client i2c_client_template = { + "bttv internal", + -1, + 0, + 0, + NULL, + NULL +}; + +static int init_bttv_i2c(struct bttv *btv) { - struct bttv *btv = (struct bttv*)bus->data; - - switch (id) - { - case I2C_DRIVERID_MSP3400: - btv->have_msp3400 = 1; - break; - case I2C_DRIVERID_TUNER: - btv->have_tuner = 1; - if (btv->tuner_type != -1) - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_TUNER, - TUNER_SET_TYPE,&btv->tuner_type); - break; - } + /* i2c bit_adapter */ + memcpy(&btv->i2c_adap, &i2c_adap_template, sizeof(struct i2c_adapter)); + memcpy(&btv->i2c_algo, &i2c_algo_template, sizeof(struct i2c_algo_bit_data)); + memcpy(&btv->i2c_client, &i2c_client_template, sizeof(struct i2c_client)); + + sprintf(btv->i2c_adap.name+strlen(btv->i2c_adap.name), + " #%d", btv->nr); + btv->i2c_algo.data = btv; + btv->i2c_adap.data = btv; + btv->i2c_adap.algo_data = &btv->i2c_algo; + btv->i2c_client.adapter = &btv->i2c_adap; + + bttv_bit_setscl(btv,1); + bttv_bit_setsda(btv,1); + + return i2c_bit_add_bus(&btv->i2c_adap); } -static void detach_inform(struct i2c_bus *bus, int id) +/* read I2C */ +static int I2CRead(struct bttv *btv, unsigned char addr) { - struct bttv *btv = (struct bttv*)bus->data; + unsigned char buffer = 0; - switch (id) - { - case I2C_DRIVERID_MSP3400: - btv->have_msp3400 = 0; - break; - case I2C_DRIVERID_TUNER: - btv->have_tuner = 0; - break; + btv->i2c_client.addr = addr >> 1; + if (1 != i2c_master_recv(&btv->i2c_client, &buffer, 1)) { + printk("bttv%d: i2c read 0x%x: error\n",btv->nr,addr); + return -1; } + printk("bttv%d: i2c read 0x%x: %d\n",btv->nr,addr,buffer); + return buffer; } -static struct i2c_bus bttv_i2c_bus_template = +/* write I2C */ +static int I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, + unsigned char b2, int both) { - "bt848", - I2C_BUSID_BT848, - NULL, + unsigned char buffer[2]; + int bytes = both ? 2 : 1; + + btv->i2c_client.addr = addr >> 1; + buffer[0] = b1; + buffer[1] = b2; + if (bytes != i2c_master_send(&btv->i2c_client, buffer, bytes)) + return -1; + return 0; +} + +/* read EEPROM */ +static void readee(struct bttv *btv, unsigned char *eedata) +{ + int i; + + if (I2CWrite(btv, 0xa0, 0, -1, 0)<0) { + printk(KERN_WARNING "bttv: readee error\n"); + return; + } + btv->i2c_client.addr = 0xa0 >> 1; + for (i=0; i<256; i+=16) { + if (16 != i2c_master_recv(&btv->i2c_client,eedata+i,16)) { + printk(KERN_WARNING "bttv: readee error\n"); + break; + } + } +} -#if LINUX_VERSION_CODE >= 0x020100 - SPIN_LOCK_UNLOCKED, -#endif - attach_inform, - detach_inform, - - i2c_setlines, - i2c_getdataline, - I2CRead, - I2CWrite, -}; - /* ----------------------------------------------------------------------- */ /* some hauppauge specific stuff */ @@ -439,7 +451,7 @@ { TUNER_ABSENT, "External" }, { TUNER_ABSENT, "Unspecified" }, { TUNER_ABSENT, "Philips FI1216" }, - { TUNER_ABSENT, "Philips FI1216MF" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF" }, { TUNER_PHILIPS_NTSC, "Philips FI1236" }, { TUNER_ABSENT, "Philips FI1246" }, { TUNER_ABSENT, "Philips FI1256" }, @@ -462,11 +474,9 @@ }; static void -hauppauge_eeprom(struct i2c_bus *bus) +hauppauge_eeprom(struct bttv *btv) { - struct bttv *btv = (struct bttv*)bus->data; - - readee(bus, eeprom_data); + readee(btv, eeprom_data); if (eeprom_data[9] < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) { btv->tuner_type = hauppauge_tuner[eeprom_data[9]].id; @@ -488,76 +498,214 @@ /* btaor(0, ~32, BT848_GPIO_OUT_EN); */ } -/* ----------------------------------------------------------------------- */ +/* Imagenation L-Model PXC200 Framegrabber */ +/* This is basically the same procedure as + * used by Alessandro Rubini in his pxc200 + * driver, but using BTTV functions */ +static void init_PXC200(struct bttv *btv) +{ + int tmp; + + /* Initialise GPIO-connevted stuff */ + btwrite(1<<13,BT848_GPIO_OUT_EN); /* Reset pin only */ + btwrite(0,BT848_GPIO_DATA); + udelay(3); + btwrite(1<<13,BT848_GPIO_DATA); + /* GPIO inputs are pulled up, so no need to drive + * reset pin any longer */ + btwrite(0,BT848_GPIO_OUT_EN); + + + /* Initialise MAX517 DAC */ + printk(KERN_INFO "Setting DAC reference voltage level ...\n"); + I2CWrite(btv,0x5E,0,0x80,1); + + /* Initialise 12C508 PIC */ + /* The I2CWrite and I2CRead commmands are actually to the + * same chips - but the R/W bit is included in the address + * argument so the numbers are different */ + + printk(KERN_INFO "Initialising 12C508 PIC chip ...\n"); + + tmp=I2CWrite(btv,0x1E,0x08,0,1); + printk(KERN_INFO "I2C Write(0x08) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F)); + tmp=I2CWrite(btv,0x1E,0x09,0,1); + printk(KERN_INFO "I2C Write(0x09) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F)); + tmp=I2CWrite(btv,0x1E,0x0a,0,1); + printk(KERN_INFO "I2C Write(0x0a) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F)); + tmp=I2CWrite(btv,0x1E,0x0b,0,1); + printk(KERN_INFO "I2C Write(0x0b) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F)); + tmp=I2CWrite(btv,0x1E,0x0c,0,1); + printk(KERN_INFO "I2C Write(0x0c) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F)); + tmp=I2CWrite(btv,0x1E,0x0d,0,1); + printk(KERN_INFO "I2C Write(0x0d) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F)); + tmp=I2CWrite(btv,0x1E,0x01,0,1); + printk(KERN_INFO "I2C Write(0x01) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F)); + tmp=I2CWrite(btv,0x1E,0x02,0,1); + printk(KERN_INFO "I2C Write(0x02) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F)); + tmp=I2CWrite(btv,0x1E,0x03,0,1); + printk(KERN_INFO "I2C Write(0x03) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F)); + tmp=I2CWrite(btv,0x1E,0x04,0,1); + printk(KERN_INFO "I2C Write(0x04) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F)); + tmp=I2CWrite(btv,0x1E,0x05,0,1); + printk(KERN_INFO "I2C Write(0x05) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F)); + tmp=I2CWrite(btv,0x1E,0x06,0,1); + printk(KERN_INFO "I2C Write(0x06) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F)); + tmp=I2CWrite(btv,0x1E,0x00,0,1); + printk(KERN_INFO "I2C Write(0x00) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F)); + + printk(KERN_INFO "PXC200 Initialised.\n"); +} + +/* ----------------------------------------------------------------------- */ struct tvcard { + char *name; int video_inputs; int audio_inputs; int tuner; int svhs; u32 gpiomask; u32 muxsel[8]; - u32 audiomux[6]; /* Tuner, Radio, internal, external, mute, stereo */ - u32 gpiomask2; /* GPIO MUX mask */ + u32 audiomux[6]; /* Tuner, Radio, external, internal, mute, stereo */ + u32 gpiomask2; /* GPIO MUX mask */ + + /* look for these i2c audio chips */ + int msp34xx:1; + int tda8425:1; + int tda9840:1; + int tda985x:1; + int tea63xx:1; }; static struct tvcard tvcards[] = { - /* default */ - { 3, 1, 0, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0}}, - /* MIRO */ - { 4, 1, 0, 2,15, { 2, 3, 1, 1}, { 2, 0, 0, 0,10}}, - /* Hauppauge */ - { 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4}}, - /* STB */ - { 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 0, 2, 3, 1}}, - /* Intel??? */ - { 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4}}, - /* Diamond DTV2000 */ - { 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 0, 1, 0, 1, 3}}, - /* AVerMedia TVPhone */ - { 3, 1, 0, 3,15, { 2, 3, 1, 1}, {12, 0,11,11, 0}}, - /* Matrix Vision MV-Delta */ - { 5, 1, -1, 3, 0, { 2, 3, 1, 0, 0}}, - /* Fly Video II */ - { 3, 1, 0, 2, 0xc00, { 2, 3, 1, 1}, - {0, 0xc00, 0x800, 0x400, 0xc00, 0}}, - /* TurboTV */ - { 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 1, 1, 2, 3, 0}}, - /* Newer Hauppauge (bt878) */ - { 3, 1, 0, 2, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4}}, - /* MIRO PCTV pro */ - { 3, 1, 0, 2, 65551, { 2, 3, 1, 1}, {1,65537, 0, 0,10}}, - /* ADS Technologies Channel Surfer TV (and maybe TV+FM) */ - { 3, 4, 0, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0}, 0}, - /* AVerMedia TVCapture 98 */ - { 3, 4, 0, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0}, 0}, - /* Aimslab VHX */ - { 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4}}, - /* Zoltrix TV-Max */ - { 3, 1, 0, 2, 0x00000f, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0x8}}, - /* Pixelview PlayTV (bt878) */ - { 3, 4, 0, 2, 0x01e000, { 2, 0, 1, 1}, {0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 }}, - /* "Leadtek WinView 601", */ - { 3, 1, 0, 2, 0x8300f8, { 2, 3, 1, 1,0}, {0x4fa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007}}, - /* AVEC Intercapture */ - { 3, 2, 0, 2, 0, { 2, 3, 1, 1}, { 1, 0, 0, 0, 0}}, - /* LifeView FlyKit w/o Tuner */ - { 3, 1, -1, -1, 0x8dff00, { 2, 3, 1, 1}}, - /* CEI Raffles Card */ - { 3, 3, 0, 2, 0, {2, 3, 1, 1}, {0, 0, 0, 0 ,0}}, - /* Lucky Star Image World ConferenceTV */ - {3, 1, 0, 2, 16777215, { 2, 3, 1, 1}, { 131072, 1, 1638400, 3, 4}}, - /* Phoebe Tv Master + FM */ - { 3, 1, 0, 2, 0xc00, { 2, 3, 1, 1},{0, 1, 0x800, 0x400, 0xc00, 0}}, - /* Modular Technology MM205 PCTV, bt878 */ - { 2, 1, 0, -1, 7, { 2, 3 }, { 0, 0, 0, 0, 0 }}, - /* Magic TView CPH061 (bt878) */ - { 3, 1, 0, 2, 0xe00, { 2, 0, 1, 1}, {0x400, 0, 0, 0, 0}}, + /* 0x00 */ + { "unknown", + 3, 1, 0, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0},0, + 1,1,1,1,0 }, + { "MIRO PCTV", + 4, 1, 0, 2,15, { 2, 3, 1, 1}, { 2, 0, 0, 0,10},0, + 1,1,1,1,0 }, + { "Hauppauge old", + 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0, + 1,1,0,1,0 }, + { "STB", + 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 0, 2, 3, 1},0, + 0,1,1,1,1 }, + + { "Intel", + 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0, + 1,1,1,1,0 }, + { "Diamond DTV2000", + 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 0, 1, 0, 1, 3},0, + 1,1,1,1,0 }, + { "AVerMedia TVPhone", + 3, 1, 0, 3,15, { 2, 3, 1, 1}, {12, 0,11,11, 0},0, + 1,1,1,1,0 }, + { "MATRIX-Vision MV-Delta", + 5, 1, -1, 3, 0, { 2, 3, 1, 0, 0},{0 }, 0, + 1,1,1,1,0 }, + + /* 0x08 */ + { "Fly Video II", + 3, 1, 0, 2, 0xc00, { 2, 3, 1, 1}, + { 0, 0xc00, 0x800, 0x400, 0xc00, 0},0, + 1,1,1,1,0 }, + { "TurboTV", + 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 1, 1, 2, 3, 0},0, + 1,1,1,1,0 }, + { "Hauppauge new (bt878)", + 3, 1, 0, 2, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4},0, + 1,1,0,1,0 }, + { "MIRO PCTV pro", + 3, 1, 0, 2, 65551, { 2, 3, 1, 1}, {1,65537, 0, 0,10},0, + 1,1,1,1,0 }, + + { "ADS Technologies Channel Surfer TV", + 3, 1, 2, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0},0, + 1,1,1,1,0 }, + { "AVerMedia TVCapture 98", + 3, 1, 4, 0, 15, { 2, 3, 1, 0, 0}, { 13, 14, 11, 7, 0, 0},0, + 1,1,1,1,0 }, + { "Aimslab VHX", + 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0, + 1,1,1,1,0 }, + { "Zoltrix TV-Max", + 3, 1, 0, 2,15, { 2, 3, 1, 1}, {0 , 0, 1 , 0, 10},0, + 1,1,1,1,0 }, + + /* 0x10 */ + { "Pixelview PlayTV (bt878)", + 3, 1, 0, 2, 0x01e000, { 2, 0, 1, 1}, + { 0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 },0, + 1,1,1,1,0 }, + { "Leadtek WinView 601", + 3, 1, 0, 2, 0x8300f8, { 2, 3, 1, 1,0}, + { 0x4fa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007},0, + 1,1,1,1,0 }, + { "AVEC Intercapture", + 3, 2, 0, 2, 0, {2, 3, 1, 1}, {1, 0, 0, 0, 0},0, + 1,1,1,1,0 }, + { "LifeView FlyKit w/o Tuner", + 3, 1, -1, -1, 0x8dff00, { 2, 3, 1, 1}, { 0 },0, + 0,0,0,0,0 }, + + { "CEI Raffles Card", + 3, 3, 0, 2, 0, {2, 3, 1, 1}, {0, 0, 0, 0 ,0},0, + 1,1,1,1,0 }, + { "Lucky Star Image World ConferenceTV", + 3, 1, 0, 2, 16777215, { 2, 3, 1, 1}, { 131072, 1, 1638400, 3, 4},0, + 1,1,1,1,0 }, + { "Phoebe Tv Master + FM", + 3, 1, 0, 2, 0xc00, { 2, 3, 1, 1},{0, 1, 0x800, 0x400, 0xc00, 0},0, + 1,1,1,1,0 }, + { "Modular Technology MM205 PCTV, bt878", + 2, 1, 0, -1, 7, { 2, 3 }, { 0, 0, 0, 0, 0 },0, + 1,1,1,1,0 }, + + /* 0x18 */ + { "Magic TView CPH061 (bt878)", + 3, 1, 0, 2, 0xe00, { 2, 0, 1, 1}, {0x400, 0, 0, 0, 0},0, + 1,1,1,1,0 }, + { "Terratec/Vobis TV-Boostar", + 3, 1, 0, 2, 16777215 , { 2, 3, 1, 1}, { 131072, 1, 1638400, 3,4},0, + 1,1,1,1,0 }, + { "Newer Hauppauge WinCam (bt878)", + 4, 1, 0, 3, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4},0, + 1,1,1,1,0 }, + { "MAXI TV Video PCI2", + 3, 1, 0, 2, 0xffff, { 2, 3, 1, 1}, { 0, 1, 2, 3, 0xc00},0, + 1,1,1,1,0 }, + + { "Terratec TerraTV+", + 3, 1, 0, 2, 0x70000, { 2, 3, 1, 1}, + { 0x20000, 0x30000, 0x00000, 0x10000, 0x40000},0, + 1,1,1,1,0 }, + { "Imagenation PXC200", + 5, 1, -1, 4, 0, { 2, 3, 1, 0, 0}, { 0 }, 0, + 1,1,1,1,0 }, + { "FlyVideo 98", + 3, 1, 0, 2, 0x8dff00, {2, 3, 1, 1}, + { 0, 0x8dff00, 0x800, 0x400, 0x8dff00, 0 },0, + 1,1,1,1,0 }, + { "iProTV", + 3, 1, 0, 2, 1, { 2, 3, 1, 1}, { 1, 0, 0, 0, 0 },0, + 1,1,1,1,0 }, + + /* 0x20 */ + { "Intel Create and Share PCI", + 4, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 4, 4, 4, 4},0, + 1,1,1,1,0 }, + { "Askey/Typhoon/Anubis Magic TView", + 3, 1, 0, 2, 0xe00, { 2, 0, 1, 1}, {0x400, 0x400, 0x400, 0x400, 0},0, + 1,1,1,1,0 }, }; -#define TVCARDS (sizeof(tvcards)/sizeof(tvcard)) +#define TVCARDS (sizeof(tvcards)/sizeof(struct tvcard)) + +/* ----------------------------------------------------------------------- */ static void audio(struct bttv *btv, int mode) { @@ -585,17 +733,13 @@ break; } /* if audio mute or not in H-lock, turn audio off */ - if ((btv->audio&AUDIO_MUTE) -#if 0 - || - (!btv->radio && !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)) -#endif - ) + if ((btv->audio&AUDIO_MUTE)) mode=AUDIO_OFF; - if ((mode == 0) && (btv->radio)) - mode = 1; + if ((mode == AUDIO_TUNER) && (btv->radio)) + mode = AUDIO_RADIO; btaor(tvcards[btv->type].audiomux[mode], ~tvcards[btv->type].gpiomask, BT848_GPIO_DATA); + call_i2c_clients(btv,AUDC_SET_INPUT,&(mode)); } @@ -697,7 +841,7 @@ } while(time_before(jiffies,tv)); - for (i=0; i<10; i++) + for (i=0; i<100; i++) { if ((btread(BT848_DSTATUS)&BT848_DSTATUS_PLOCK)) btwrite(0,BT848_DSTATUS); @@ -743,15 +887,7 @@ ~tvcards[btv->type].gpiomask2, BT848_GPIO_DATA); } -/* - * Set the registers for the size we have specified. Don't bother - * trying to understand this without the BT848 manual in front of - * you [AC]. - * - * PS: The manual is free for download in .pdf format from - * www.brooktree.com - nicely done those folks. - */ - + struct tvnorm { u32 Fsc; @@ -768,29 +904,14 @@ /* PAL-BDGHI */ /* max. active video is actually 922, but 924 is divisible by 4 and 3! */ /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */ -#ifdef VIDEODAT { 35468950, 924, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), 1135, 186, 924, 0x20, 255}, -#else - { 35468950, - 924, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), - 1135, 186, 924, 0x20, 255}, -#endif -/* - { 35468950, - 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), - 944, 186, 922, 0x20, 255}, -*/ + /* NTSC */ { 28636363, 768, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0), 910, 128, 910, 0x1a, 144}, -/* - { 28636363, - 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0), - 780, 122, 754, 0x1a, 144}, -*/ #if 0 /* SECAM EAST */ { 35468950, @@ -889,8 +1010,10 @@ unsigned long bpl=1024; /* bytes per line */ unsigned long vadr=(unsigned long) vbuf; - *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(ro++)=0; - *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(re++)=0; + *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(ro++)=cpu_to_le32(0); + *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(re++)=cpu_to_le32(0); /* In PAL 650 blocks of 256 DWORDs are sampled, but only if VDELAY is 2 and without separate VBI grabbing. @@ -913,7 +1036,6 @@ return 0; } - static int make_prisctab(struct bttv *btv, unsigned int *ro, unsigned int *re, unsigned int *vbuf, unsigned short width, @@ -962,8 +1084,10 @@ cradr=cbadr+csize; inter = (height>btv->win.cropheight/2) ? 1 : 0; - *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3); *(ro++)=0; - *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3); *(re++)=0; + *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3); + *(ro++)=0; + *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3); + *(re++)=0; for (line=0; line < (height<<(1^inter)); line++) { @@ -1005,7 +1129,7 @@ vadr+=bl; if((rcmd&(15<<28))==BT848_RISC_WRITE123) { - *((*rp)++)=cpu_to_le32(kvirt_to_bus(cbadr)); + *((*rp)++)=(kvirt_to_bus(cbadr)); cbadr+=blcb; *((*rp)++)=cpu_to_le32(kvirt_to_bus(cradr)); cradr+=blcr; @@ -1045,8 +1169,10 @@ inter = (height>btv->win.cropheight/2) ? 1 : 0; bpl=width*fmtbppx2[palette2fmt[palette]&0xf]/2; - *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(ro++)=0; - *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(re++)=0; + *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(ro++)=cpu_to_le32(0); + *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(re++)=cpu_to_le32(0); for (line=0; line < (height<<(1^inter)); line++) { @@ -1059,7 +1185,7 @@ if (bpl<=bl) { *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL| - BT848_RISC_EOL|bpl); + BT848_RISC_EOL|bpl); *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr)); vadr+=bpl; } @@ -1156,18 +1282,28 @@ unsigned long adr; unsigned char *clipmap, cbit, lastbit, outofmem; + if (btv->win.use_yuv) { + /* yuv-to-offscreen (BT848_COLOR_FMT_YUY2) */ + bpp = 2; + bpl = btv->win.win2.pitch; + adr = btv->win.vidadr + btv->win.win2.start; + } else { + bpp=btv->win.bpp; + if (bpp==15) /* handle 15bpp as 16bpp in calculations */ + bpp++; + bpl=btv->win.bpl; + adr=btv->win.vidadr+btv->win.x*bpp+btv->win.y*bpl; + } inter=(btv->win.interlace&1)^1; - bpp=btv->win.bpp; - if (bpp==15) /* handle 15bpp as 16bpp in calculations */ - bpp++; - bpl=btv->win.bpl; - ro=btv->risc_odd; - re=btv->risc_even; - if((width=btv->win.width)>1023) + width=btv->win.width; + height=btv->win.height; + if(width > 1023) width = 1023; /* sanity check */ - if((height=btv->win.height)>625) + if(height > 625) height = 625; /* sanity check */ - adr=btv->win.vidadr+btv->win.x*bpp+btv->win.y*bpl; + ro=btv->risc_odd; + re=btv->risc_even; + if ((clipmap=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) { /* can't clip, don't generate any risc code */ *(ro++)=cpu_to_le32(BT848_RISC_JUMP); @@ -1186,17 +1322,21 @@ /* clip against viewing window AND screen so we do not have to rely on the user program */ - clip_draw_rectangle(clipmap,(btv->win.x+width>btv->win.swidth) ? - (btv->win.swidth-btv->win.x) : width, 0, 1024, 768); - clip_draw_rectangle(clipmap,0,(btv->win.y+height>btv->win.sheight) ? - (btv->win.sheight-btv->win.y) : height,1024,768); - if (btv->win.x<0) - clip_draw_rectangle(clipmap, 0, 0, -(btv->win.x), 768); - if (btv->win.y<0) - clip_draw_rectangle(clipmap, 0, 0, 1024, -(btv->win.y)); + if (!btv->win.use_yuv) { + clip_draw_rectangle(clipmap,(btv->win.x+width>btv->win.swidth) ? + (btv->win.swidth-btv->win.x) : width, 0, 1024, 768); + clip_draw_rectangle(clipmap,0,(btv->win.y+height>btv->win.sheight) ? + (btv->win.sheight-btv->win.y) : height,1024,768); + if (btv->win.x<0) + clip_draw_rectangle(clipmap, 0, 0, -(btv->win.x), 768); + if (btv->win.y<0) + clip_draw_rectangle(clipmap, 0, 0, 1024, -(btv->win.y)); + } - *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(ro++)=0; - *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(re++)=0; + *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(ro++)=cpu_to_le32(0); + *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(re++)=cpu_to_le32(0); /* translate bitmap to risc code */ for (line=outofmem=0; line < (height<bus_vbi_odd); } +/* + * Set the registers for the size we have specified. Don't bother + * trying to understand this without the BT848 manual in front of + * you [AC]. + * + * PS: The manual is free for download in .pdf format from + * www.brooktree.com - nicely done those folks. + */ + /* set geometry for even/odd frames just if you are wondering: handling of even and odd frames will be separated, e.g. for grabbing @@ -1266,7 +1417,8 @@ } -static void bt848_set_geo(struct bttv *btv, u16 width, u16 height, u16 fmt, int pllset) +static void bt848_set_geo(struct bttv *btv, u16 width, u16 height, + u16 fmt, int pllset) { u16 vscale, hscale; u32 xsf, sr; @@ -1279,7 +1431,7 @@ if (!width || !height) return; - + save_flags(flags); cli(); @@ -1288,17 +1440,6 @@ btv->win.cropheight=tvn->sheight; btv->win.cropwidth=tvn->swidth; -/* - if (btv->win.cropwidth>tvn->cropwidth) - btv->win.cropwidth=tvn->cropwidth; - if (btv->win.cropheight>tvn->cropheight) - btv->win.cropheight=tvn->cropheight; - - if (width>btv->win.cropwidth) - width=btv->win.cropwidth; - if (height>btv->win.cropheight) - height=btv->win.cropheight; -*/ btwrite(tvn->adelay, BT848_ADELAY); btwrite(tvn->bdelay, BT848_BDELAY); btaor(tvn->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), BT848_IFORM); @@ -1306,27 +1447,28 @@ btwrite(1, BT848_VBI_PACK_DEL); btv->pll.pll_ofreq = tvn->Fsc; - if(pllset) - set_pll(btv); + if (pllset) + set_pll(btv); btwrite(fmt, BT848_COLOR_FMT); -#ifdef __sparc__ - if(fmt == BT848_COLOR_FMT_RGB32 || - fmt == BT848_COLOR_FMT_RGB24) { + if (bigendian && + fmt == BT848_COLOR_FMT_RGB32) { btwrite((BT848_COLOR_CTL_GAMMA | - BT848_COLOR_CTL_WSWAP_ODD | - BT848_COLOR_CTL_WSWAP_EVEN | - BT848_COLOR_CTL_BSWAP_ODD | - BT848_COLOR_CTL_BSWAP_EVEN), - BT848_COLOR_CTL); - } else if(fmt == BT848_COLOR_FMT_RGB16 || - fmt == BT848_COLOR_FMT_RGB15) { - btwrite((BT848_COLOR_CTL_GAMMA | - BT848_COLOR_CTL_BSWAP_ODD | - BT848_COLOR_CTL_BSWAP_EVEN), - BT848_COLOR_CTL); - } -#endif + BT848_COLOR_CTL_WSWAP_ODD | + BT848_COLOR_CTL_WSWAP_EVEN | + BT848_COLOR_CTL_BSWAP_ODD | + BT848_COLOR_CTL_BSWAP_EVEN), + BT848_COLOR_CTL); + } else if (bigendian && + (fmt == BT848_COLOR_FMT_RGB16 || + fmt == BT848_COLOR_FMT_RGB15)) { + btwrite((BT848_COLOR_CTL_GAMMA | + BT848_COLOR_CTL_BSWAP_ODD | + BT848_COLOR_CTL_BSWAP_EVEN), + BT848_COLOR_CTL); + } else { + btwrite(0x10, BT848_COLOR_CTL); + } hactive=width; vtc=0; @@ -1350,18 +1492,18 @@ crop=((hactive>>8)&0x03)|((hdelay>>6)&0x0c)| ((vactive>>4)&0x30)|((vdelay>>2)&0xc0); vscale|= btv->win.interlace ? (BT848_VSCALE_INT<<8) : 0; - + bt848_set_eogeo(btv, 0, vtc, hscale, vscale, hactive, vactive, hdelay, vdelay, crop); bt848_set_eogeo(btv, 1, vtc, hscale, vscale, hactive, vactive, hdelay, vdelay, crop); - + restore_flags(flags); } int bpp2fmt[4] = { - BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT_RGB16, + BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT_RGB16, BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT_RGB32 }; @@ -1369,9 +1511,14 @@ { unsigned short format; - btv->win.color_fmt = format = - (btv->win.depth==15) ? BT848_COLOR_FMT_RGB15 : - bpp2fmt[(btv->win.bpp-1)&3]; + if (btv->win.use_yuv) { + /* yuv-to-offscreen */ + format = BT848_COLOR_FMT_YUY2; + } else { + format = (btv->win.depth==15) ? BT848_COLOR_FMT_RGB15 : + bpp2fmt[(btv->win.bpp-1)&3]; + } + btv->win.color_fmt = format; /* RGB8 seems to be a 9x5x5 GRB color cube starting at * color 16. Why the h... can't they even mention this in the @@ -1385,45 +1532,39 @@ else btor(BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL); - bt848_set_geo(btv, btv->win.width, btv->win.height, format, 1); + bt848_set_geo(btv, btv->win.width, btv->win.height, format,1); } /* * Set TSA5522 synthesizer frequency in 1/16 Mhz steps */ +#if 0 static void set_freq(struct bttv *btv, unsigned short freq) { + int naudio; int fixme = freq; /* XXX */ - + /* int oldAudio = btv->audio; */ + /* mute */ - if (btv->have_msp3400) - i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400, - MSP_SWITCH_MUTE,0); - - /* switch channel */ - if (btv->have_tuner) { - if (btv->radio) { - i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER, - TUNER_SET_RADIOFREQ,&fixme); - } else { - i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER, - TUNER_SET_TVFREQ,&fixme); - } - } + AUDIO(AUDC_SWITCH_MUTE,0); - if (btv->have_msp3400) { - if (btv->radio) { - i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400, - MSP_SET_RADIO,0); - } else { - i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400, - MSP_SET_TVNORM,&(btv->win.norm)); - i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400, - MSP_NEWCHANNEL,0); - } + /* tune */ + if (btv->radio) { + TUNER(TUNER_SET_RADIOFREQ,&fixme); + } else { + TUNER(TUNER_SET_TVFREQ,&fixme); + } + + if (btv->radio) { + AUDIO(AUDC_SET_RADIO,0); + } else { + AUDIO(AUDC_SET_TVNORM,&(btv->win.norm)); + AUDIO(AUDC_NEWCHANNEL,0); } } +#endif + /* * Grab into virtual memory. @@ -1447,7 +1588,7 @@ * No grabbing past the end of the buffer! */ - if(mp->frame>1 || mp->frame <0) + if(mp->frame>(MAX_GBUFFERS-1) || mp->frame <0) return -EINVAL; if(mp->height <0 || mp->width <0) @@ -1485,6 +1626,7 @@ re=ro+2048; make_vrisctab(btv, ro, re, vbuf, mp->width, mp->height, mp->format); /* bt848_set_risc_jmps(btv); */ + cli(); btv->frame_stat[mp->frame] = GBUFFER_GRABBING; if (btv->grabbing) { btv->gfmt_next=palette2fmt[mp->format]; @@ -1508,12 +1650,14 @@ } btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ); } + sti(); btor(3, BT848_CAP_CTL); btor(3, BT848_GPIO_DMA_CTL); /* interruptible_sleep_on(&btv->capq); */ return 0; } + static long bttv_write(struct video_device *v, const char *buf, unsigned long count, int nonblock) { return -EINVAL; @@ -1523,6 +1667,7 @@ { struct bttv *btv= (struct bttv *)v; int q,todo; + /* BROKEN: RETURNS VBI WHEN IT SHOULD RETURN GRABBED VIDEO FRAME */ todo=count; while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) @@ -1569,18 +1714,17 @@ static int bttv_open(struct video_device *dev, int flags) { struct bttv *btv = (struct bttv *)dev; - int i, ret; - + int i,ret; + ret = -EBUSY; - down(&btv->lock); - if (btv->user) + if (btv->user) goto out_unlock; - - btv->fbuffer= (unsigned char *) rvmalloc(2*BTTV_MAX_FBUF); + + btv->fbuffer=(unsigned char *) rvmalloc(MAX_GBUFFERS*BTTV_MAX_FBUF); ret = -ENOMEM; - if (!btv->fbuffer) + if (!btv->fbuffer) goto out_unlock; - audio(btv, AUDIO_UNMUTE); + btv->grabbing = 0; btv->grab = 0; btv->lastgrab = 0; @@ -1588,9 +1732,9 @@ btv->frame_stat[i] = GBUFFER_UNUSED; btv->user++; - up(&btv->lock); + up(&btv->lock); MOD_INC_USE_COUNT; - return 0; + return 0; out_unlock: up(&btv->lock); @@ -1601,9 +1745,8 @@ { struct bttv *btv=(struct bttv *)dev; - down(&btv->lock); + down(&btv->lock); btv->user--; - audio(btv, AUDIO_INTERN); btv->cap&=~3; bt848_set_risc_jmps(btv); @@ -1621,15 +1764,16 @@ * be sure its safe to free the buffer. We wait 5-6 fields * which is more than sufficient to be sure. */ - + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ/10); /* Wait 1/10th of a second */ /* * We have allowed it to drain. */ + if(btv->fbuffer) - rvfree((void *) btv->fbuffer, 2*BTTV_MAX_FBUF); + rvfree((void *) btv->fbuffer, MAX_GBUFFERS*BTTV_MAX_FBUF); btv->fbuffer=0; up(&btv->lock); MOD_DEC_USE_COUNT; @@ -1680,6 +1824,7 @@ btaor(datahi, ~1, BT848_O_CONTROL); } + /* * ioctl routine */ @@ -1687,619 +1832,597 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { - unsigned char eedata[256]; struct bttv *btv=(struct bttv *)dev; int i; - switch (cmd) - { - case VIDIOCGCAP: - { - struct video_capability b; - strcpy(b.name,btv->video_dev.name); - b.type = VID_TYPE_CAPTURE| - VID_TYPE_TUNER| - VID_TYPE_TELETEXT| - VID_TYPE_OVERLAY| - VID_TYPE_CLIPPING| - VID_TYPE_FRAMERAM| - VID_TYPE_SCALES; - b.channels = tvcards[btv->type].video_inputs; - b.audios = tvcards[btv->type].audio_inputs; - b.maxwidth = tvnorms[btv->win.norm].swidth; - b.maxheight = tvnorms[btv->win.norm].sheight; - b.minwidth = 32; - b.minheight = 32; - if(copy_to_user(arg,&b,sizeof(b))) - return -EFAULT; - return 0; - } - case VIDIOCGCHAN: + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability b; + strcpy(b.name,btv->video_dev.name); + b.type = VID_TYPE_CAPTURE| + ((tvcards[btv->type].tuner != -1) ? VID_TYPE_TUNER : 0) | + VID_TYPE_OVERLAY| + VID_TYPE_CLIPPING| + VID_TYPE_FRAMERAM| + VID_TYPE_SCALES; + b.channels = tvcards[btv->type].video_inputs; + b.audios = tvcards[btv->type].audio_inputs; + b.maxwidth = tvnorms[btv->win.norm].swidth; + b.maxheight = tvnorms[btv->win.norm].sheight; + b.minwidth = 32; + b.minheight = 32; + 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; + v.flags=VIDEO_VC_AUDIO; + v.tuners=0; + v.type=VIDEO_TYPE_CAMERA; + v.norm = btv->win.norm; + if (v.channel>=tvcards[btv->type].video_inputs) + return -EINVAL; + if(v.channel==tvcards[btv->type].tuner) { - struct video_channel v; - if(copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - v.flags=VIDEO_VC_AUDIO; - v.tuners=0; - v.type=VIDEO_TYPE_CAMERA; - v.norm = btv->win.norm; - if (v.channel>=tvcards[btv->type].video_inputs) - return -EINVAL; - if(v.channel==tvcards[btv->type].tuner) - { - strcpy(v.name,"Television"); - v.flags|=VIDEO_VC_TUNER; - v.type=VIDEO_TYPE_TV; - v.tuners=1; - } - else if(v.channel==tvcards[btv->type].svhs) - strcpy(v.name,"S-Video"); - else - sprintf(v.name,"Composite%d",v.channel); - - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - return 0; - } - /* - * Each channel has 1 tuner - */ - case VIDIOCSCHAN: - { - struct video_channel v; - if(copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - - if (v.channel>tvcards[btv->type].video_inputs) - return -EINVAL; - if(v.norm!=VIDEO_MODE_PAL&&v.norm!=VIDEO_MODE_NTSC - &&v.norm!=VIDEO_MODE_SECAM) - return -EOPNOTSUPP; - down(&btv->lock); - bt848_muxsel(btv, v.channel); + strcpy(v.name,"Television"); + v.flags|=VIDEO_VC_TUNER; + v.type=VIDEO_TYPE_TV; + v.tuners=1; + } + else if(v.channel==tvcards[btv->type].svhs) + strcpy(v.name,"S-Video"); + else + sprintf(v.name,"Composite%d",v.channel); + + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + /* + * Each channel has 1 tuner + */ + case VIDIOCSCHAN: + { + struct video_channel v; + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + + if (v.channel>tvcards[btv->type].video_inputs) + return -EINVAL; + if (v.norm > (sizeof(tvnorms)/sizeof(*tvnorms))) + return -EOPNOTSUPP; + + call_i2c_clients(btv,cmd,&v); + down(&btv->lock); + bt848_muxsel(btv, v.channel); + btv->channel=v.channel; + if (btv->win.norm != v.norm) { btv->win.norm = v.norm; - make_vbitab(btv); + make_vbitab(btv); bt848_set_winsize(btv); - btv->channel=v.channel; - up(&btv->lock); - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner v; - if(copy_from_user(&v,arg,sizeof(v))!=0) - return -EFAULT; - if(v.tuner||btv->channel) /* Only tuner 0 */ - return -EINVAL; - strcpy(v.name, "Television"); - v.rangelow=0; - v.rangehigh=0xFFFFFFFF; - v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; - if (btv->audio_chip == TDA9840) { - v.flags |= VIDEO_AUDIO_VOLUME; - v.mode = VIDEO_SOUND_MONO|VIDEO_SOUND_STEREO; - v.mode |= VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2; - } - if (btv->audio_chip == TDA9850) { - unsigned char ALR1; - ALR1 = I2CRead(&(btv->i2c), I2C_TDA9850|1); - if (ALR1 & 32) - v.flags |= VIDEO_TUNER_STEREO_ON; - } - v.mode = btv->win.norm; - v.signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0; - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - return 0; } - /* We have but one tuner */ - case VIDIOCSTUNER: - { - struct video_tuner v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - /* Only one channel has a tuner */ - if(v.tuner!=tvcards[btv->type].tuner) - return -EINVAL; + up(&btv->lock); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&v,arg,sizeof(v))!=0) + return -EFAULT; + if(v.tuner||btv->channel) /* Only tuner 0 */ + return -EINVAL; + strcpy(v.name, "Television"); + v.rangelow=0; + v.rangehigh=0xFFFFFFFF; + v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; + v.mode = btv->win.norm; + v.signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0; + call_i2c_clients(btv,cmd,&v); + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + /* We have but one tuner */ + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + /* Only one channel has a tuner */ + if(v.tuner!=tvcards[btv->type].tuner) + return -EINVAL; - if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC - &&v.mode!=VIDEO_MODE_SECAM) - return -EOPNOTSUPP; - btv->win.norm = v.mode; - down(&btv->lock); - bt848_set_winsize(btv); - up(&btv->lock); - return 0; - } - case VIDIOCGPICT: - { - struct video_picture p=btv->picture; - if(btv->win.depth==8) - p.palette=VIDEO_PALETTE_HI240; - if(btv->win.depth==15) - p.palette=VIDEO_PALETTE_RGB555; - if(btv->win.depth==16) - p.palette=VIDEO_PALETTE_RGB565; - if(btv->win.depth==24) - p.palette=VIDEO_PALETTE_RGB24; - if(btv->win.depth==32) - p.palette=VIDEO_PALETTE_RGB32; - - if(copy_to_user(arg, &p, sizeof(p))) - return -EFAULT; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture p; - int format; - if(copy_from_user(&p, arg,sizeof(p))) - return -EFAULT; - down(&btv->lock); - /* We want -128 to 127 we get 0-65535 */ - bt848_bright(btv, (p.brightness>>8)-128); - /* 0-511 for the colour */ - bt848_sat_u(btv, p.colour>>7); - bt848_sat_v(btv, ((p.colour>>7)*201L)/237); - /* -128 to 127 */ - bt848_hue(btv, (p.hue>>8)-128); - /* 0-511 */ - bt848_contrast(btv, p.contrast>>7); - btv->picture = p; - - /* set palette if bpp matches */ - if (p.palette < sizeof(palette2fmt)/sizeof(int)) { - format = palette2fmt[p.palette]; - if (fmtbppx2[format&0x0f]/2 == btv->win.bpp) - btv->win.color_fmt = format; - } + if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC + &&v.mode!=VIDEO_MODE_SECAM) + return -EOPNOTSUPP; + call_i2c_clients(btv,cmd,&v); + if (btv->win.norm != v.mode) { + btv->win.norm = v.mode; + down(&btv->lock); + make_vbitab(btv); + bt848_set_winsize(btv); up(&btv->lock); - return 0; } - case VIDIOCSWIN: - { - struct video_window vw; - struct video_clip *vcp = NULL; - int on; - - if(copy_from_user(&vw,arg,sizeof(vw))) - return -EFAULT; - - if(vw.flags || vw.width < 16 || vw.height < 16) - { - down(&btv->lock); - bt848_cap(btv,0); - up(&btv->lock); - return -EINVAL; - } - if (btv->win.bpp < 4) - { /* 32-bit align start and adjust width */ - int i = vw.x; - vw.x = (vw.x + 3) & ~3; - i = vw.x - i; - vw.width -= i; - } + return 0; + } + case VIDIOCGPICT: + { + struct video_picture p=btv->picture; + if(btv->win.depth==8) + p.palette=VIDEO_PALETTE_HI240; + if(btv->win.depth==15) + p.palette=VIDEO_PALETTE_RGB555; + if(btv->win.depth==16) + p.palette=VIDEO_PALETTE_RGB565; + if(btv->win.depth==24) + p.palette=VIDEO_PALETTE_RGB24; + if(btv->win.depth==32) + p.palette=VIDEO_PALETTE_RGB32; - down(&btv->lock); - btv->win.x=vw.x; - btv->win.y=vw.y; - btv->win.width=vw.width; - btv->win.height=vw.height; - - if(btv->win.height>btv->win.cropheight/2) - btv->win.interlace=1; - else - btv->win.interlace=0; - - on=(btv->cap&3); + if(copy_to_user(arg, &p, sizeof(p))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture p; + if(copy_from_user(&p, arg,sizeof(p))) + return -EFAULT; + down(&btv->lock); + /* We want -128 to 127 we get 0-65535 */ + bt848_bright(btv, (p.brightness>>8)-128); + /* 0-511 for the colour */ + bt848_sat_u(btv, p.colour>>7); + bt848_sat_v(btv, ((p.colour>>7)*201L)/237); + /* -128 to 127 */ + bt848_hue(btv, (p.hue>>8)-128); + /* 0-511 */ + bt848_contrast(btv, p.contrast>>7); + btv->picture = p; + up(&btv->lock); + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + struct video_clip *vcp = NULL; + int on; + if(copy_from_user(&vw,arg,sizeof(vw))) + return -EFAULT; + + if(vw.flags || vw.width < 16 || vw.height < 16) + { + down(&btv->lock); bt848_cap(btv,0); - bt848_set_winsize(btv); - up(&btv->lock); + return -EINVAL; + } + if (btv->win.bpp < 4) + { /* adjust and align writes */ + vw.x = (vw.x + 3) & ~3; + vw.width &= ~3; + } + down(&btv->lock); + btv->win.use_yuv=0; + btv->win.x=vw.x; + btv->win.y=vw.y; + btv->win.width=vw.width; + btv->win.height=vw.height; - /* - * Do any clips. - */ - if(vw.clipcount<0) { - if((vcp=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) - return -ENOMEM; - if(copy_from_user(vcp, vw.clips, - VIDEO_CLIPMAP_SIZE)) { - vfree(vcp); - return -EFAULT; - } - } else if (vw.clipcount) { - if((vcp=vmalloc(sizeof(struct video_clip)* - (vw.clipcount))) == NULL) - return -ENOMEM; - if(copy_from_user(vcp,vw.clips, - sizeof(struct video_clip)* - vw.clipcount)) { - vfree(vcp); - return -EFAULT; - } + on=(btv->cap&3); + + bt848_cap(btv,0); + bt848_set_winsize(btv); + up(&btv->lock); + + /* + * Do any clips. + */ + if(vw.clipcount<0) { + if((vcp=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) + return -ENOMEM; + if(copy_from_user(vcp, vw.clips, + VIDEO_CLIPMAP_SIZE)) { + vfree(vcp); + return -EFAULT; } - down(&btv->lock); - make_clip_tab(btv, vcp, vw.clipcount); - if (vw.clipcount != 0) + } else if (vw.clipcount) { + if((vcp=vmalloc(sizeof(struct video_clip)* + (vw.clipcount))) == NULL) + return -ENOMEM; + if(copy_from_user(vcp,vw.clips, + sizeof(struct video_clip)* + vw.clipcount)) { vfree(vcp); - if(on && btv->win.vidadr!=0) - bt848_cap(btv,1); - up(&btv->lock); - return 0; - } - case VIDIOCGWIN: - { - struct video_window vw; - /* Oh for a COBOL move corresponding .. */ - vw.x=btv->win.x; - vw.y=btv->win.y; - vw.width=btv->win.width; - vw.height=btv->win.height; - vw.chromakey=0; - vw.flags=0; - if(btv->win.interlace) - vw.flags|=VIDEO_WINDOW_INTERLACE; - if(copy_to_user(arg,&vw,sizeof(vw))) return -EFAULT; - return 0; + } } - case VIDIOCCAPTURE: - { - int v; - if(copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - if(v!=0 && (btv->win.vidadr==0 || btv->win.width==0 - || btv->win.height==0)) - return -EINVAL; - - down(&btv->lock); - if(v==0) - bt848_cap(btv,0); - else - bt848_cap(btv,1); - up(&btv->lock); + down(&btv->lock); + make_clip_tab(btv, vcp, vw.clipcount); + if (vw.clipcount != 0) + vfree(vcp); + if(on && btv->win.vidadr!=0) + bt848_cap(btv,1); + up(&btv->lock); + return 0; + } + case VIDIOCSWIN2: + { + /* experimental -- right now it handles unclipped yuv data only */ + struct video_window2 vo; + __u32 fbsize; + int on; + + if(copy_from_user(&vo,arg,sizeof(vo))) + return -EFAULT; - return 0; - } - case VIDIOCGFBUF: - { - struct video_buffer v; - v.base=(void *)btv->win.vidadr; - v.height=btv->win.sheight; - v.width=btv->win.swidth; - v.depth=btv->win.depth; - v.bytesperline=btv->win.bpl; - if(copy_to_user(arg, &v,sizeof(v))) - return -EFAULT; - return 0; + fbsize = btv->win.sheight * btv->win.bpl; + if (vo.start + vo.pitch*vo.height > fbsize) + return -EINVAL; + if (vo.palette != VIDEO_PALETTE_YUV422) + return -EINVAL; + + down(&btv->lock); + btv->win.use_yuv=1; + memcpy(&btv->win.win2,&vo,sizeof(vo)); + btv->win.width=vo.width; + btv->win.height=vo.height; + + on=(btv->cap&3); + bt848_cap(btv,0); + bt848_set_winsize(btv); + make_clip_tab(btv, NULL, 0); + if(on && btv->win.vidadr!=0) + bt848_cap(btv,1); + up(&btv->lock); + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + /* Oh for a COBOL move corresponding .. */ + vw.x=btv->win.x; + vw.y=btv->win.y; + vw.width=btv->win.width; + vw.height=btv->win.height; + vw.chromakey=0; + vw.flags=0; + if(btv->win.interlace) + vw.flags|=VIDEO_WINDOW_INTERLACE; + if(copy_to_user(arg,&vw,sizeof(vw))) + return -EFAULT; + return 0; + } + case VIDIOCCAPTURE: + { + int v; + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + if(btv->win.vidadr == 0) + return -EINVAL; + if (0 == btv->win.use_yuv && (btv->win.width==0 || btv->win.height==0)) + return -EINVAL; + down(&btv->lock); + if(v==0) + bt848_cap(btv,0); + else + bt848_cap(btv,1); + up(&btv->lock); + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer v; + v.base=(void *)btv->win.vidadr; + v.height=btv->win.sheight; + v.width=btv->win.swidth; + v.depth=btv->win.depth; + v.bytesperline=btv->win.bpl; + if(copy_to_user(arg, &v,sizeof(v))) + return -EFAULT; + return 0; - } - case VIDIOCSFBUF: - { - struct video_buffer v; -#if LINUX_VERSION_CODE >= 0x020100 - if(!capable(CAP_SYS_ADMIN) - && !capable(CAP_SYS_RAWIO)) -#else - if(!suser()) -#endif - return -EPERM; - if(copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - if(v.depth!=8 && v.depth!=15 && v.depth!=16 && - v.depth!=24 && v.depth!=32 && v.width > 16 && - v.height > 16 && v.bytesperline > 16) - return -EINVAL; - down(&btv->lock); - if (v.base) - btv->win.vidadr=(unsigned long)v.base; - btv->win.sheight=v.height; - btv->win.swidth=v.width; - btv->win.bpp=((v.depth+7)&0x38)/8; - btv->win.depth=v.depth; - btv->win.bpl=v.bytesperline; + } + case VIDIOCSFBUF: + { + struct video_buffer v; + if(!capable(CAP_SYS_ADMIN) && + !capable(CAP_SYS_RAWIO)) + return -EPERM; + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + if(v.depth!=8 && v.depth!=15 && v.depth!=16 && + v.depth!=24 && v.depth!=32 && v.width > 16 && + v.height > 16 && v.bytesperline > 16) + return -EINVAL; + down(&btv->lock); + if (v.base) + btv->win.vidadr=(unsigned long)v.base; + btv->win.sheight=v.height; + btv->win.swidth=v.width; + btv->win.bpp=((v.depth+7)&0x38)/8; + btv->win.depth=v.depth; + btv->win.bpl=v.bytesperline; - DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n", - v.base, v.width,v.height, btv->win.bpp, btv->win.bpl)); - bt848_set_winsize(btv); - up(&btv->lock); - return 0; - } - case VIDIOCKEY: - { - /* Will be handled higher up .. */ - return 0; - } - case VIDIOCGFREQ: - { - unsigned long v=btv->win.freq; - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - return 0; + DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n", + v.base, v.width,v.height, btv->win.bpp, btv->win.bpl)); + bt848_set_winsize(btv); + up(&btv->lock); + return 0; + } + case VIDIOCKEY: + { + /* Will be handled higher up .. */ + return 0; + } + case VIDIOCGFREQ: + { + unsigned long v=btv->win.freq; + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + btv->win.freq=v; + call_i2c_clients(btv,cmd,&v); + return 0; + } + + case VIDIOCGAUDIO: + { + struct video_audio v; + + v=btv->audio_dev; + v.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE); + v.flags|=VIDEO_AUDIO_MUTABLE; + strcpy(v.name,"TV"); + + v.mode = VIDEO_SOUND_MONO; + call_i2c_clients(btv,cmd,&v); + + if (btv->type == BTTV_TERRATV) { + v.mode = VIDEO_SOUND_MONO; + v.mode |= VIDEO_SOUND_STEREO; + v.mode |= VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2; + + } else if (btv->audio_chip == TDA9840) { + /* begin of Horrible Hack */ + v.flags|=VIDEO_AUDIO_VOLUME; + v.mode = VIDEO_SOUND_MONO; + v.mode |= VIDEO_SOUND_STEREO; + v.mode |= VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2; + v.volume = 32768; /* fixme */ + v.step = 4096; } - case VIDIOCSFREQ: - { - unsigned long v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - btv->win.freq=v; - set_freq(btv, btv->win.freq); - return 0; - } - - case VIDIOCGAUDIO: - { - struct video_audio v; - v=btv->audio_dev; - v.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE); - v.flags|=VIDEO_AUDIO_MUTABLE; - strcpy(v.name,"TV"); - if (btv->audio_chip == TDA9850) { - unsigned char ALR1; - ALR1 = I2CRead(&(btv->i2c), I2C_TDA9850|1); - v.mode = VIDEO_SOUND_MONO; - v.mode |= (ALR1 & 32) ? VIDEO_SOUND_STEREO:0; - v.mode |= (ALR1 & 64) ? VIDEO_SOUND_LANG1:0; - } - if (btv->have_msp3400) - { - v.flags|=VIDEO_AUDIO_VOLUME | - VIDEO_AUDIO_BASS | - VIDEO_AUDIO_TREBLE; - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_GET_VOLUME,&(v.volume)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_GET_BASS,&(v.bass)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_GET_TREBLE,&(v.treble)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_GET_STEREO,&(v.mode)); - } - else v.mode = VIDEO_SOUND_MONO; - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - return 0; + +#if 0 +#warning this should be handled by tda9855.c + else if (btv->audio_chip == TDA9850) { + unsigned char ALR1; + v.flags|=VIDEO_AUDIO_VOLUME; + ALR1 = I2CRead(btv, I2C_TDA9850|1); + v.mode = VIDEO_SOUND_MONO; + v.mode |= (ALR1 & 32) ? VIDEO_SOUND_STEREO:0; + v.mode |= (ALR1 & 32) ? VIDEO_SOUND_LANG1:0; + v.volume = 32768; /* fixme */ + v.step = 4096; } - case VIDIOCSAUDIO: - { - struct video_audio v; - if(copy_from_user(&v,arg, sizeof(v))) - return -EFAULT; - down(&btv->lock); - if(v.flags&VIDEO_AUDIO_MUTE) - audio(btv, AUDIO_MUTE); - /* One audio source per tuner */ - /* if(v.audio!=0) */ - /* ADSTech TV card has more than one */ - if(v.audio<0 || v.audio >= tvcards[btv->type].audio_inputs) - { - up(&btv->lock); - return -EINVAL; - } - bt848_muxsel(btv,v.audio); - if(!(v.flags&VIDEO_AUDIO_MUTE)) - audio(btv, AUDIO_UNMUTE); - if (btv->audio_chip == TDA9850) { - unsigned char con3 = 0; - if (v.mode & VIDEO_SOUND_LANG1) - con3 = 0x80; /* sap */ - if (v.mode & VIDEO_SOUND_STEREO) - con3 = 0x40; /* stereo */ - I2CWrite(&(btv->i2c), I2C_TDA9850, - TDA9850_CON3, con3, 1); - } - - /* PT2254A programming Jon Tombs, jon@gte.esi.us.es */ - if (btv->type == BTTV_WINVIEW_601) { - int bits_out, loops, vol, data; - - /* 32 levels logarithmic */ - vol = 32 - ((v.volume>>11)); - /* units */ - bits_out = (PT2254_DBS_IN_2>>(vol%5)); - /* tens */ - bits_out |= (PT2254_DBS_IN_10>>(vol/5)); - bits_out |= PT2254_L_CHANEL | PT2254_R_CHANEL; - data = btread(BT848_GPIO_DATA); - data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA| - WINVIEW_PT2254_STROBE); - for (loops = 17; loops >= 0 ; loops--) { - if (bits_out & (1<audio_chip == TEA6320) - { - int vol; - vol = v.volume >> 11; - if (!(v.flags&VIDEO_AUDIO_MUTE)) - I2CWrite(&(btv->i2c), I2C_TEA6320, - TEA6320_S, TEA6320_S_SB,1); /* at least Raffles card uses input B */ - else - I2CWrite(&(btv->i2c), I2C_TEA6320, - TEA6320_S, TEA6320_S_GMU,1); - I2CWrite(&(btv->i2c), I2C_TEA6320, - TEA6320_V, vol, 1); - } - if (btv->have_msp3400) - { - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_SET_VOLUME,&(v.volume)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_SET_BASS,&(v.bass)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_SET_TREBLE,&(v.treble)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_SET_STEREO,&(v.mode)); - } - btv->audio_dev=v; +#endif + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; + + if(copy_from_user(&v,arg, sizeof(v))) + return -EFAULT; + down(&btv->lock); + if(v.flags&VIDEO_AUDIO_MUTE) + audio(btv, AUDIO_MUTE); + /* One audio source per tuner -- huh? */ + if(v.audio<0 || v.audio >= tvcards[btv->type].audio_inputs) { up(&btv->lock); - return 0; + return -EINVAL; } + /* bt848_muxsel(btv,v.audio); */ + if(!(v.flags&VIDEO_AUDIO_MUTE)) + audio(btv, AUDIO_UNMUTE); - case VIDIOCSYNC: - if(copy_from_user((void *)&i,arg,sizeof(int))) - return -EFAULT; -/* if(i>1 || i<0) - return -EINVAL; -*/ - switch (btv->frame_stat[i]) { - case GBUFFER_UNUSED: - return -EINVAL; - case GBUFFER_GRABBING: - while(btv->frame_stat[i]==GBUFFER_GRABBING) { - interruptible_sleep_on(&btv->capq); - if(signal_pending(current)) - return -EINTR; - } - /* fall */ - case GBUFFER_DONE: - btv->frame_stat[i] = GBUFFER_UNUSED; - break; - } - return 0; + call_i2c_clients(btv,cmd,&v); + + if (btv->type == BTTV_TERRATV) { + unsigned int con = 0; + btor(0x180000, BT848_GPIO_OUT_EN); + if (v.mode & VIDEO_SOUND_LANG2) + con = 0x080000; + if (v.mode & VIDEO_SOUND_STEREO) + con = 0x180000; + btaor(con, ~0x180000, BT848_GPIO_DATA); + + } else if (btv->type == BTTV_WINVIEW_601) { + /* PT2254A programming Jon Tombs, jon@gte.esi.us.es */ + int bits_out, loops, vol, data; + + /* 32 levels logarithmic */ + vol = 32 - ((v.volume>>11)); + /* units */ + bits_out = (PT2254_DBS_IN_2>>(vol%5)); + /* tens */ + bits_out |= (PT2254_DBS_IN_10>>(vol/5)); + bits_out |= PT2254_L_CHANEL | PT2254_R_CHANEL; + data = btread(BT848_GPIO_DATA); + data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA| + WINVIEW_PT2254_STROBE); + for (loops = 17; loops >= 0 ; loops--) { + if (bits_out & (1<lock); - writeee(&(btv->i2c), eedata); - up(&btv->lock); - return 0; +#if 0 +#warning this should be handled by tda9855.c + } else if (btv->audio_chip == TDA9850) { + unsigned char con3 = 0; + if (v.mode & VIDEO_SOUND_LANG1) + con3 = 0x80; /* sap */ + if (v.mode & VIDEO_SOUND_STEREO) + con3 = 0x40; /* stereo */ + I2CWrite(btv, I2C_TDA9850, + TDA9850_CON3, con3, 1); + if (v.flags & VIDEO_AUDIO_VOLUME) + I2CWrite(btv, I2C_TDA9850, + TDA9850_CON4, + (v.volume>>12) & 15, 1); +#endif + } + btv->audio_dev=v; + up(&btv->lock); + return 0; + } - case BTTV_READEE: - if(!capable(CAP_SYS_ADMIN)) - return -EPERM; - down(&btv->lock); - readee(&(btv->i2c), eedata); - up(&btv->lock); - if(copy_to_user((void *) arg, (void *) eedata, 256)) - return -EFAULT; + case VIDIOCSYNC: + if(copy_from_user((void *)&i,arg,sizeof(int))) + return -EFAULT; + switch (btv->frame_stat[i]) { + case GBUFFER_UNUSED: + return -EINVAL; + case GBUFFER_GRABBING: + while(btv->frame_stat[i]==GBUFFER_GRABBING) { + interruptible_sleep_on(&btv->capq); + if(signal_pending(current)) + return -EINTR; + } + /* fall */ + case GBUFFER_DONE: + btv->frame_stat[i] = GBUFFER_UNUSED; break; + } + return 0; - case BTTV_FIELDNR: - if(copy_to_user((void *) arg, (void *) &btv->last_field, - sizeof(btv->last_field))) - return -EFAULT; - break; + case BTTV_FIELDNR: + if(copy_to_user((void *) arg, (void *) &btv->last_field, + sizeof(btv->last_field))) + return -EFAULT; + break; - case BTTV_PLLSET: { - struct bttv_pll_info p; - if(!capable(CAP_SYS_ADMIN)) - return -EPERM; - if(copy_from_user(&p , (void *) arg, sizeof(btv->pll))) - return -EFAULT; - down(&btv->lock); - btv->pll.pll_ifreq = p.pll_ifreq; - btv->pll.pll_ofreq = p.pll_ofreq; - btv->pll.pll_crystal = p.pll_crystal; - up(&btv->lock); - break; - } - case VIDIOCMCAPTURE: - { - struct video_mmap vm; - int v; - if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm))) - return -EFAULT; - if (btv->frame_stat[vm.frame] == GBUFFER_GRABBING) - return -EBUSY; - down(&btv->lock); - v=vgrab(btv, &vm); - up(&btv->lock); - return v; - } + case BTTV_PLLSET: { + struct bttv_pll_info p; + if(!capable(CAP_SYS_ADMIN)) + return -EPERM; + if(copy_from_user(&p , (void *) arg, sizeof(btv->pll))) + return -EFAULT; + down(&btv->lock); + btv->pll.pll_ifreq = p.pll_ifreq; + btv->pll.pll_ofreq = p.pll_ofreq; + btv->pll.pll_crystal = p.pll_crystal; + up(&btv->lock); + + break; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + int ret; + if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm))) + return -EFAULT; + if (btv->frame_stat[vm.frame] == GBUFFER_GRABBING) + return -EBUSY; + down(&btv->lock); + ret = vgrab(btv, &vm); + up(&btv->lock); + return ret; + } - case VIDIOCGMBUF: - { - struct video_mbuf vm; - memset(&vm, 0 , sizeof(vm)); - vm.size=BTTV_MAX_FBUF*2; - vm.frames=2; - vm.offsets[0]=0; - vm.offsets[1]=BTTV_MAX_FBUF; - if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) - return -EFAULT; - return 0; - } + case VIDIOCGMBUF: + { + struct video_mbuf vm; + memset(&vm, 0 , sizeof(vm)); + vm.size=BTTV_MAX_FBUF*MAX_GBUFFERS; + vm.frames=MAX_GBUFFERS; + vm.offsets[0]=0; + vm.offsets[1]=BTTV_MAX_FBUF; + if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) + return -EFAULT; + return 0; + } - case VIDIOCGUNIT: - { - struct video_unit vu; - vu.video=btv->video_dev.minor; - vu.vbi=btv->vbi_dev.minor; - if(btv->radio_dev.minor!=-1) - vu.radio=btv->radio_dev.minor; - else - vu.radio=VIDEO_NO_UNIT; - vu.audio=VIDEO_NO_UNIT; - if(btv->have_msp3400) - { - i2c_control_device(&(btv->i2c), I2C_DRIVERID_MSP3400, - MSP_GET_UNIT, &vu.audio); - } - vu.teletext=VIDEO_NO_UNIT; - if(copy_to_user((void *)arg, (void *)&vu, sizeof(vu))) - return -EFAULT; - return 0; - } + case VIDIOCGUNIT: + { + struct video_unit vu; + vu.video=btv->video_dev.minor; + vu.vbi=btv->vbi_dev.minor; + if(btv->radio_dev.minor!=-1) + vu.radio=btv->radio_dev.minor; + else + vu.radio=VIDEO_NO_UNIT; + vu.audio=VIDEO_NO_UNIT; +#if 0 + AUDIO(AUDC_GET_UNIT, &vu.audio); +#endif + vu.teletext=VIDEO_NO_UNIT; + if(copy_to_user((void *)arg, (void *)&vu, sizeof(vu))) + return -EFAULT; + return 0; + } - case BTTV_BURST_ON: - { - tvnorms[0].scaledtwidth=1135-BURSTOFFSET-2; - tvnorms[0].hdelayx1=186-BURSTOFFSET; - return 0; - } + case BTTV_BURST_ON: + { + tvnorms[0].scaledtwidth=1135-BURSTOFFSET-2; + tvnorms[0].hdelayx1=186-BURSTOFFSET; + tvnorms[2].scaledtwidth=1135-BURSTOFFSET-2; + tvnorms[2].hdelayx1=186-BURSTOFFSET; + return 0; + } - case BTTV_BURST_OFF: - { - tvnorms[0].scaledtwidth=1135; - tvnorms[0].hdelayx1=186; - return 0; - } + case BTTV_BURST_OFF: + { + tvnorms[0].scaledtwidth=1135; + tvnorms[0].hdelayx1=186; + tvnorms[2].scaledtwidth=1135; + tvnorms[2].hdelayx1=186; + return 0; + } - case BTTV_VERSION: - { - return BTTV_VERSION_CODE; - } + case BTTV_VERSION: + { + return BTTV_VERSION_CODE; + } - case BTTV_PICNR: - { - /* return picture;*/ - return 0; - } + case BTTV_PICNR: + { + /* return picture;*/ + return 0; + } - default: - return -ENOIOCTLCMD; + default: + return -ENOIOCTLCMD; } return 0; } @@ -2317,43 +2440,42 @@ * - remap_page_range is kind of inefficient for page by page remapping. * But e.g. pte_alloc() does not work in modules ... :-( */ - + static int do_bttv_mmap(struct bttv *btv, const char *adr, unsigned long size) { unsigned long start=(unsigned long) adr; - unsigned long page,pos; + unsigned long page,pos; - if (size>2*BTTV_MAX_FBUF) - return -EINVAL; - if (!btv->fbuffer) - { - if(fbuffer_alloc(btv)) - return -EINVAL; - } - pos=(unsigned long) btv->fbuffer; - 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; - size-=PAGE_SIZE; - } - return 0; + if (size>2*BTTV_MAX_FBUF) + return -EINVAL; + if (!btv->fbuffer) { + if(fbuffer_alloc(btv)) + return -EINVAL; + } + pos=(unsigned long) btv->fbuffer; + 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; + size-=PAGE_SIZE; + } + return 0; } static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long size) { - struct bttv *btv=(struct bttv *)dev; - int r; - - down(&btv->lock); - r=do_bttv_mmap(btv, adr, size); - up(&btv->lock); - return r; + struct bttv *btv=(struct bttv *)dev; + int r; + + down(&btv->lock); + r=do_bttv_mmap(btv, adr, size); + up(&btv->lock); + return r; } + static struct video_device bttv_template= { "UNSET", @@ -2363,9 +2485,7 @@ bttv_close, bttv_read, bttv_write, -#if LINUX_VERSION_CODE >= 0x020100 - NULL, /* poll */ -#endif + NULL, bttv_ioctl, bttv_mmap, bttv_init_done, @@ -2437,7 +2557,7 @@ { struct bttv *btv=(struct bttv *)(dev-2); - down(&btv->lock); + down(&btv->lock); btv->vbip=VBIBUF_SIZE; btv->cap|=0x0c; bt848_set_risc_jmps(btv); @@ -2451,7 +2571,7 @@ { struct bttv *btv=(struct bttv *)(dev-2); - down(&btv->lock); + down(&btv->lock); btv->cap&=~0x0c; bt848_set_risc_jmps(btv); up(&btv->lock); @@ -2459,10 +2579,36 @@ MOD_DEC_USE_COUNT; } - static int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { - return -EINVAL; + struct bttv *btv=(struct bttv *)dev; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability b; + strcpy(b.name,btv->vbi_dev.name); + b.type = ((tvcards[btv->type].tuner != -1) ? VID_TYPE_TUNER : 0) | + VID_TYPE_TELETEXT; + b.channels = 0; + b.audios = 0; + b.maxwidth = 0; + b.maxheight = 0; + b.minwidth = 0; + b.minheight = 0; + if(copy_to_user(arg,&b,sizeof(b))) + return -EFAULT; + return 0; + } + case VIDIOCGFREQ: + case VIDIOCSFREQ: + return bttv_ioctl((struct video_device *)btv,cmd,arg); + case BTTV_VBISIZE: + /* make alevt happy :-) */ + return VBIBUF_SIZE; + default: + return -EINVAL; + } } static struct video_device vbi_template= @@ -2474,9 +2620,7 @@ vbi_close, vbi_read, bttv_write, -#if LINUX_VERSION_CODE >= 0x020100 vbi_poll, -#endif vbi_ioctl, NULL, /* no mmap yet */ bttv_init_done, @@ -2490,17 +2634,16 @@ { struct bttv *btv = (struct bttv *)(dev-1); - down(&btv->lock); + down(&btv->lock); if (btv->user) goto busy_unlock; btv->user++; - - set_freq(btv,400*16); + btv->radio = 1; + call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type); bt848_muxsel(btv,0); - audio(btv, AUDIO_UNMUTE); up(&btv->lock); - + MOD_INC_USE_COUNT; return 0; @@ -2512,11 +2655,10 @@ static void radio_close(struct video_device *dev) { struct bttv *btv=(struct bttv *)(dev-1); - + down(&btv->lock); btv->user--; btv->radio = 0; - /*audio(btv, AUDIO_MUTE);*/ up(&btv->lock); MOD_DEC_USE_COUNT; } @@ -2595,9 +2737,7 @@ radio_close, radio_read, /* just returns -EINVAL */ bttv_write, /* just returns -EINVAL */ -#if LINUX_VERSION_CODE >= 0x020100 NULL, /* no poll */ -#endif radio_ioctl, NULL, /* no mmap */ bttv_init_done, /* just returns 0 */ @@ -2607,7 +2747,6 @@ }; - #define TRITON_PCON 0x50 #define TRITON_BUS_CONCURRENCY (1<<0) #define TRITON_STREAMING (1<<1) @@ -2623,79 +2762,69 @@ if (triton1) triton1=BT848_INT_ETBF; - - if(pci_pci_problems&PCIPCI_FAIL) - { - printk(KERN_WARNING "bttv: This configuration is known to have PCI to PCI DMA problems\n"); - printk(KERN_WARNING "bttv: You may not be able to use overlay mode.\n"); - } - - while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, dev))) + while ((dev = pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, dev))) { - unsigned char b; - pci_read_config_byte(dev, 0x53, &b); - DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, ")); - DEBUG(printk("bufcon=0x%02x\n",b)); - } + /* Beware the SiS 85C496 my friend - rev 49 don't work with a bttv */ + printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n"); + } + + while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82441, dev))) + { + unsigned char b; + pci_read_config_byte(dev, 0x53, &b); + DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, ")); + DEBUG(printk("bufcon=0x%02x\n",b)); + } while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, dev))) { -/* unsigned char b; - unsigned char bo;*/ - printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n"); triton1=BT848_INT_ETBF; } } -static void init_tea6300(struct i2c_bus *bus) +#if 0 +#warning please use tda8425.c instead +static void init_tda8425(struct bttv *btv) { - I2CWrite(bus, I2C_TEA6300, TEA6300_VL, 0x35, 1); /* volume left 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6300_VR, 0x35, 1); /* volume right 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6300_BA, 0x07, 1); /* bass 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6300_TR, 0x07, 1); /* treble 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6300_FA, 0x0f, 1); /* fader off */ - I2CWrite(bus, I2C_TEA6300, TEA6300_SW, 0x01, 1); /* mute off input A */ + I2CWrite(btv, I2C_TDA8425, TDA8425_VL, 0xFC, 1); /* volume left 0dB */ + I2CWrite(btv, I2C_TDA8425, TDA8425_VR, 0xFC, 1); /* volume right 0dB */ + I2CWrite(btv, I2C_TDA8425, TDA8425_BA, 0xF6, 1); /* bass 0dB */ + I2CWrite(btv, I2C_TDA8425, TDA8425_TR, 0xF6, 1); /* treble 0dB */ + I2CWrite(btv, I2C_TDA8425, TDA8425_S1, 0xCE, 1); /* mute off */ } +#endif -static void init_tea6320(struct i2c_bus *bus) +/* can tda9855.c handle this too maybe? */ +static void init_tda9840(struct bttv *btv) { - I2CWrite(bus, I2C_TEA6300, TEA6320_V, 0x28, 1); /* master volume */ - I2CWrite(bus, I2C_TEA6300, TEA6320_FFL, 0x28, 1); /* volume left 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_FFR, 0x28, 1); /* volume right 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_FRL, 0x28, 1); /* volume rear left 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_FRR, 0x28, 1); /* volume rear right 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_BA, 0x11, 1); /* bass 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_TR, 0x11, 1); /* treble 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_S, TEA6320_S_GMU, 1); /* mute off input A */ + /* Horrible Hack */ + I2CWrite(btv, I2C_TDA9840, TDA9840_SW, 0x2a, 1); /* sound mode switching */ + /* 00 - mute + 10 - mono / averaged stereo + 2a - stereo + 12 - dual A + 1a - dual AB + 16 - dual BA + 1e - dual B + 7a - external */ } -static void init_tda8425(struct i2c_bus *bus) -{ - I2CWrite(bus, I2C_TDA8425, TDA8425_VL, 0xFC, 1); /* volume left 0dB */ - I2CWrite(bus, I2C_TDA8425, TDA8425_VR, 0xFC, 1); /* volume right 0dB */ - I2CWrite(bus, I2C_TDA8425, TDA8425_BA, 0xF6, 1); /* bass 0dB */ - I2CWrite(bus, I2C_TDA8425, TDA8425_TR, 0xF6, 1); /* treble 0dB */ - I2CWrite(bus, I2C_TDA8425, TDA8425_S1, 0xCE, 1); /* mute off */ -} -static void init_tda9840(struct i2c_bus *bus) -{ - I2CWrite(bus, I2C_TDA9840, TDA9840_SW, 0x2A, 1); /* Sound mode switching */ -} - -static void init_tda9850(struct i2c_bus *bus) +#if 0 +#warning this should be handled by tda9855.c +static void init_tda9850(struct bttv *btv) { - I2CWrite(bus, I2C_TDA9850, TDA9850_CON1, 0x08, 1); /* noise threshold st */ - I2CWrite(bus, I2C_TDA9850, TDA9850_CON2, 0x08, 1); /* noise threshold sap */ - I2CWrite(bus, I2C_TDA9850, TDA9850_CON3, 0x40, 1); /* stereo mode */ - I2CWrite(bus, I2C_TDA9850, TDA9850_CON4, 0x07, 1); /* 0 dB input gain?*/ - I2CWrite(bus, I2C_TDA9850, TDA9850_ALI1, 0x10, 1); /* wideband alignment? */ - I2CWrite(bus, I2C_TDA9850, TDA9850_ALI2, 0x10, 1); /* spectral alignment? */ - I2CWrite(bus, I2C_TDA9850, TDA9850_ALI3, 0x03, 1); + I2CWrite(btv, I2C_TDA9850, TDA9850_CON1, 0x08, 1); /* noise threshold st */ + I2CWrite(btv, I2C_TDA9850, TDA9850_CON2, 0x08, 1); /* noise threshold sap */ + I2CWrite(btv, I2C_TDA9850, TDA9850_CON3, 0x40, 1); /* stereo mode */ + I2CWrite(btv, I2C_TDA9850, TDA9850_CON4, 0x07, 1); /* 0 dB input gain?*/ + I2CWrite(btv, I2C_TDA9850, TDA9850_ALI1, 0x10, 1); /* wideband alignment? */ + I2CWrite(btv, I2C_TDA9850, TDA9850_ALI2, 0x10, 1); /* spectral alignment? */ + I2CWrite(btv, I2C_TDA9850, TDA9850_ALI3, 0x03, 1); } - - +#endif /* Figure out card and tuner type */ @@ -2707,180 +2836,119 @@ DEBUG(printk(KERN_DEBUG "bttv%d: GPIO: 0x%08x\n", i, btread(BT848_GPIO_DATA))); /* Default the card to the user-selected one. */ - btv->type=card[i]; - btv->tuner_type=-1; /* use default tuner type */ + if (card[i] >= 0 && card[i] < TVCARDS) + btv->type=card[i]; /* If we were asked to auto-detect, then do so! Right now this will only recognize Miro, Hauppauge or STB */ if (btv->type == BTTV_UNKNOWN) { - if (I2CRead(&(btv->i2c), I2C_HAUPEE)>=0) + if (I2CRead(btv, I2C_HAUPEE)>=0) { if(btv->id>849) btv->type=BTTV_HAUPPAUGE878; else btv->type=BTTV_HAUPPAUGE; - } else if (I2CRead(&(btv->i2c), I2C_STBEE)>=0) { + } else if (I2CRead(btv, I2C_STBEE)>=0) { btv->type=BTTV_STB; + +#if 0 /* bad idea: 0xc0 is used for the tuner on _many_ boards */ + } else if (I2CRead(btv, I2C_VHX)>=0) { + btv->type=BTTV_VHX; +#endif + } else { - if (I2CRead(&(btv->i2c), 0x80)>=0) /* check for msp34xx */ + if (I2CRead(btv, 0x80)>=0) /* check for msp34xx */ btv->type = BTTV_MIROPRO; else - btv->type=BTTV_MIRO; + btv->type = BTTV_MIRO; } } - /* board specific initialisations */ - - switch(btv->type) - { - case BTTV_MIRO: - case BTTV_MIROPRO: - /* auto detect tuner for MIRO cards */ - btv->tuner_type=((btread(BT848_GPIO_DATA)>>10)-1)&7; - break; - - case BTTV_HAUPPAUGE: - case BTTV_HAUPPAUGE878: - hauppauge_msp_reset(btv); - hauppauge_eeprom(&(btv->i2c)); - if (btv->type == BTTV_HAUPPAUGE878) { - /* all bt878 hauppauge boards use this ... */ - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; - } - break; - - case BTTV_CONFERENCETV: - btv->tuner_type = 1; - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; - break; - - case BTTV_PIXVIEWPLAYTV: - case BTTV_AVERMEDIA98: - case BTTV_MODTEC_205: - case BTTV_MAGICTVIEW061: - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; - break; + /* print which board we have found */ + printk(KERN_INFO "bttv%d: model: ",btv->nr); + + sprintf(btv->video_dev.name,"BT%d",btv->id); + if (btv->id==848 && btv->revision==0x12) + strcat(btv->video_dev.name,"A"); + strcat(btv->video_dev.name,"("); + strcat(btv->video_dev.name, tvcards[btv->type].name); + strcat(btv->video_dev.name,")"); + printk("%s\n",btv->video_dev.name); + /* board specific initialisations */ + if (btv->type == BTTV_MIRO || btv->type == BTTV_MIROPRO) { + /* auto detect tuner for MIRO cards */ + btv->tuner_type=((btread(BT848_GPIO_DATA)>>10)-1)&7; + } + if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) { + hauppauge_msp_reset(btv); + hauppauge_eeprom(btv); + } + if (btv->type == BTTV_MAXI) { + /* PHILIPS FI1216MK2 tuner (PAL/SECAM) */ + btv->tuner_type=TUNER_PHILIPS_SECAM; } + + if (btv->type == BTTV_PXC200) + init_PXC200(btv); + + if (btv->type == BTTV_CONFERENCETV) + btv->tuner_type = 1; - if (btv->have_tuner && btv->tuner_type != -1) - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_TUNER, - TUNER_SET_TYPE,&btv->tuner_type); - - - if (I2CRead(&(btv->i2c), I2C_TDA9840) >=0) - { - btv->audio_chip = TDA9840; - printk(KERN_INFO "bttv%d: audio chip: TDA9840\n", btv->nr); - } - - if (I2CRead(&(btv->i2c), I2C_TDA9850) >=0) - { - btv->audio_chip = TDA9850; - printk(KERN_INFO "bttv%d: audio chip: TDA9850\n",btv->nr); + if (btv->type == BTTV_HAUPPAUGE878 || + btv->type == BTTV_CONFERENCETV || + btv->type == BTTV_PIXVIEWPLAYTV || + btv->type == BTTV_AVERMEDIA98) { + btv->pll.pll_ifreq=28636363; + btv->pll.pll_crystal=BT848_IFORM_XT0; } - if (I2CRead(&(btv->i2c), I2C_TDA8425) >=0) - { - btv->audio_chip = TDA8425; - printk(KERN_INFO "bttv%d: audio chip: TDA8425\n",btv->nr); - } - - switch(btv->audio_chip) - { - case TDA9850: - init_tda9850(&(btv->i2c)); - break; - case TDA9840: - init_tda9840(&(btv->i2c)); - break; - case TDA8425: - init_tda8425(&(btv->i2c)); - break; + if (btv->tuner_type != -1) + call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type); + + /* try to detect audio/fader chips */ + if (tvcards[btv->type].msp34xx && + I2CRead(btv, I2C_MSP3400) >=0) { + printk(KERN_INFO "bttv%d: audio chip: MSP34xx\n",i); + request_module("msp3400"); + } + + if (tvcards[btv->type].tda8425 && + I2CRead(btv, I2C_TDA8425) >=0) { + printk(KERN_INFO "bttv%d: audio chip: TDA8425\n",i); + request_module("tda8425"); } - - if (I2CRead(&(btv->i2c), I2C_TEA6300) >=0) - { - if(btv->type==BTTV_AVEC_INTERCAP || btv->type==BTTV_CEI_RAFFLES) - { - printk(KERN_INFO "bttv%d: fader chip: TEA6320\n",btv->nr); - btv->audio_chip = TEA6320; - init_tea6320(&(btv->i2c)); - } else { - printk(KERN_INFO "bttv%d: fader chip: TEA6300\n",btv->nr); - btv->audio_chip = TEA6300; - init_tea6300(&(btv->i2c)); - } - } else - printk(KERN_INFO "bttv%d: NO fader chip: TEA6300\n",btv->nr); - printk(KERN_INFO "bttv%d: model: ",btv->nr); + if (tvcards[btv->type].tda9840 && + I2CRead(btv, I2C_TDA9840) >=0) { + init_tda9840(btv); + printk(KERN_INFO "bttv%d: audio chip: TDA9840\n", i); + btv->audio_chip = TDA9840; + /* move this to a module too? */ + init_tda9840(btv); + } - sprintf(btv->video_dev.name,"BT%d",btv->id); - switch (btv->type) - { - case BTTV_MIRO: - case BTTV_MIROPRO: - strcat(btv->video_dev.name, - (btv->type == BTTV_MIRO) ? "(Miro)" : "(Miro pro)"); - break; - case BTTV_HAUPPAUGE: - strcat(btv->video_dev.name,"(Hauppauge old)"); - break; - case BTTV_HAUPPAUGE878: - strcat(btv->video_dev.name,"(Hauppauge new)"); - break; - case BTTV_STB: - strcat(btv->video_dev.name,"(STB)"); - break; - case BTTV_INTEL: - strcat(btv->video_dev.name,"(Intel)"); - break; - case BTTV_DIAMOND: - strcat(btv->video_dev.name,"(Diamond)"); - break; - case BTTV_AVERMEDIA: - strcat(btv->video_dev.name,"(AVerMedia)"); - break; - case BTTV_MATRIX_VISION: - strcat(btv->video_dev.name,"(MATRIX-Vision)"); - break; - case BTTV_AVERMEDIA98: - strcat(btv->video_dev.name,"(AVerMedia TVCapture 98)"); - break; - case BTTV_VHX: - strcpy(btv->video_dev.name,"(Aimslab-VHX)"); - break; - case BTTV_WINVIEW_601: - strcpy(btv->video_dev.name,"(Leadtek WinView 601)"); - break; - case BTTV_AVEC_INTERCAP: - strcpy(btv->video_dev.name,"(AVEC Intercapture)"); - break; - case BTTV_CEI_RAFFLES: - strcpy(btv->video_dev.name,"(CEI Raffles Card)"); - break; - case BTTV_CONFERENCETV: - strcpy(btv->video_dev.name,"(Image World ConferenceTV)"); - break; - case BTTV_PHOEBE_TVMAS: - strcpy(btv->video_dev.name,"(Phoebe TV Master)"); - break; - case BTTV_MODTEC_205: - strcpy(btv->video_dev.name,"(Modtec MM205)"); - break; + if (tvcards[btv->type].tda985x && + I2CRead(btv, I2C_TDA9850) >=0) { + printk(KERN_INFO "bttv%d: audio chip: TDA985x\n",i); + request_module("tda9855"); } - printk("%s\n",btv->video_dev.name); - audio(btv, AUDIO_INTERN); -} + if (tvcards[btv->type].tea63xx && + I2CRead(btv, I2C_TEA6300)) { + printk(KERN_INFO "bttv%d: fader chip: TEA63xx\n",i); + request_module("tea6300"); + } + + if (tvcards[btv->type].tuner != -1) { + request_module("tuner"); + } + + audio(btv, AUDIO_MUTE); +} static void bt848_set_risc_jmps(struct bttv *btv) @@ -2888,18 +2956,19 @@ int flags=btv->cap; /* Sync to start of odd field */ - btv->risc_jmp[0]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRE); - btv->risc_jmp[1]=0; + btv->risc_jmp[0]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC + |BT848_FIFO_STATUS_VRE); + btv->risc_jmp[1]=cpu_to_le32(0); /* Jump to odd vbi sub */ - btv->risc_jmp[2]=cpu_to_le32(BT848_RISC_JUMP|(0x5<<20)); + btv->risc_jmp[2]=cpu_to_le32(BT848_RISC_JUMP|(0xd<<20)); if (flags&8) btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->vbi_odd)); else btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->risc_jmp+4)); /* Jump to odd sub */ - btv->risc_jmp[4]=cpu_to_le32(BT848_RISC_JUMP|(0x6<<20)); + btv->risc_jmp[4]=cpu_to_le32(BT848_RISC_JUMP|(0xe<<20)); if (flags&2) btv->risc_jmp[5]=cpu_to_le32(virt_to_bus(btv->risc_odd)); else @@ -2907,8 +2976,9 @@ /* Sync to start of even field */ - btv->risc_jmp[6]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRO); - btv->risc_jmp[7]=0; + btv->risc_jmp[6]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC + |BT848_FIFO_STATUS_VRO); + btv->risc_jmp[7]=cpu_to_le32(0); /* Jump to even vbi sub */ btv->risc_jmp[8]=cpu_to_le32(BT848_RISC_JUMP); @@ -2927,7 +2997,7 @@ btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP); btv->risc_jmp[13]=cpu_to_le32(virt_to_bus(btv->risc_jmp)); - /* enable capturing */ + /* enable cpaturing and DMA */ btaor(flags, ~0x0f, BT848_CAP_CTL); if (flags&0x0f) bt848_dma(btv, 3); @@ -2935,13 +3005,52 @@ bt848_dma(btv, 0); } +static int +init_video_dev(struct bttv *btv) +{ + int num = btv - bttvs; + + memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template)); + memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template)); + memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template)); + + idcard(num); + + if(video_register_device(&btv->video_dev,VFL_TYPE_GRABBER)<0) + return -1; + if(video_register_device(&btv->vbi_dev,VFL_TYPE_VBI)<0) + { + video_unregister_device(&btv->video_dev); + return -1; + } + if (radio[num]) + { + if(video_register_device(&btv->radio_dev, VFL_TYPE_RADIO)<0) + { + video_unregister_device(&btv->vbi_dev); + video_unregister_device(&btv->video_dev); + return -1; + } + } + return 1; +} + static int init_bt848(int i) { struct bttv *btv = &bttvs[i]; btv->user=0; - - init_MUTEX(&btv->lock); + init_MUTEX(&btv->lock); + +#if 0 + /* dump current state of the gpio registers before changing them, + * might help to make a new card work */ + printk("bttv%d: gpio: out_enable=0x%x, data=0x%x, in=0x%x\n", + i, + btread(BT848_GPIO_OUT_EN), + btread(BT848_GPIO_DATA), + btread(BT848_GPIO_REG_INP)); +#endif /* reset the bt848 */ btwrite(0, BT848_SRESET); @@ -2964,6 +3073,7 @@ btv->win.bpl=1024*btv->win.bpp; btv->win.swidth=1024; btv->win.sheight=768; + btv->win.vidadr=0; btv->cap=0; btv->gmode=0; @@ -2977,17 +3087,10 @@ btv->grab=0; btv->lastgrab=0; btv->field=btv->last_field=0; - /* cevans - prevents panic if initialization bails due to memory - * alloc failures! - */ - btv->video_dev.minor = -1; - btv->vbi_dev.minor = -1; - btv->radio_dev.minor = -1; /* i2c */ - memcpy(&(btv->i2c),&bttv_i2c_bus_template,sizeof(struct i2c_bus)); - sprintf(btv->i2c.name,"bt848-%d",i); - btv->i2c.data = btv; + btv->tuner_type=-1; + init_bttv_i2c(btv); if (!(btv->risc_odd=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) return -1; @@ -3017,14 +3120,14 @@ bt848_set_winsize(btv); /* btwrite(0, BT848_TDEC); */ - btwrite(0x10, BT848_COLOR_CTL); + btwrite(0x10, BT848_COLOR_CTL); btwrite(0x00, BT848_CAP_CTL); btwrite(0xac, BT848_GPIO_DMA_CTL); /* select direct input */ btwrite(0x00, BT848_GPIO_REG_INP); - btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_PAL_BDGHI, + btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM); btwrite(0xd8, BT848_CONTRAST_LO); @@ -3053,7 +3156,7 @@ btwrite(btv->triton1| /*BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR| BT848_INT_FDSR|BT848_INT_FTRGT|BT848_INT_FBUS|*/ - BT848_INT_VSYNC| + (fieldnr ? BT848_INT_VSYNC : 0)| BT848_INT_SCERR| BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES| BT848_INT_FMTCHG|BT848_INT_HLOCK, @@ -3065,30 +3168,7 @@ /* * Now add the template and register the device unit. */ - - memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template)); - memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template)); - memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template)); - - idcard(i); - - if(video_register_device(&btv->video_dev,VFL_TYPE_GRABBER)<0) - return -1; - if(video_register_device(&btv->vbi_dev,VFL_TYPE_VBI)<0) - { - video_unregister_device(&btv->video_dev); - return -1; - } - if (radio[i]) - { - if(video_register_device(&btv->radio_dev, VFL_TYPE_RADIO)<0) - { - video_unregister_device(&btv->vbi_dev); - video_unregister_device(&btv->video_dev); - return -1; - } - } - i2c_register_bus(&btv->i2c); + init_video_dev(btv); return 0; } @@ -3110,7 +3190,8 @@ if (!astat) return; btwrite(astat,BT848_INT_STAT); - IDEBUG(printk ("bttv%d: astat %08x stat %08x\n", btv->nr, astat, stat)); + IDEBUG(printk ("bttv%d: astat %08x\n", btv->nr, astat)); + IDEBUG(printk ("bttv%d: stat %08x\n", btv->nr, stat)); /* get device status bits */ dstat=btread(BT848_DSTATUS); @@ -3133,7 +3214,7 @@ if (astat&BT848_INT_SCERR) { IDEBUG(printk ("bttv%d: IRQ_SCERR\n", btv->nr)); bt848_dma(btv, 0); - bt848_dma(btv, 3); + bt848_dma(btv, 1); wake_up_interruptible(&btv->vbiq); wake_up_interruptible(&btv->capq); @@ -3154,7 +3235,7 @@ /* captured full frame */ if (stat&(2<<28)) { - wake_up_interruptible(&btv->capq); + /*wake_up_interruptible(&btv->capq);*/ btv->last_field=btv->field; btv->grab++; btv->frame_stat[btv->grf] = GBUFFER_DONE; @@ -3170,14 +3251,14 @@ btv->risc_jmp[11]=cpu_to_le32(btv->gre); bt848_set_geo(btv, btv->gwidth, btv->gheight, - btv->gfmt, 0); + btv->gfmt,0); } else { bt848_set_risc_jmps(btv); btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI); btand(~BT848_VSCALE_COMB, BT848_O_VSCALE_HI); bt848_set_geo(btv, btv->win.width, btv->win.height, - btv->win.color_fmt, 0); + btv->win.color_fmt,0); } wake_up_interruptible(&btv->capq); break; @@ -3188,7 +3269,7 @@ btv->risc_jmp[11]=cpu_to_le32(btv->gre); btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP); bt848_set_geo(btv, btv->gwidth, btv->gheight, - btv->gfmt, 0); + btv->gfmt,0); } } if (astat&BT848_INT_OCERR) @@ -3255,6 +3336,9 @@ int result; unsigned char command; struct bttv *btv; +#if defined(__powerpc__) + unsigned int cmd; +#endif btv=&bttvs[bttv_num]; btv->dev=dev; @@ -3278,6 +3362,7 @@ else btv->i2c_command=(I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA); + btv->bt848_adr&=PCI_BASE_ADDRESS_MEM_MASK; pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision); printk(KERN_INFO "bttv%d: Brooktree Bt%d (rev %d) ", bttv_num,btv->id, btv->revision); @@ -3285,6 +3370,14 @@ printk("irq: %d, ",btv->irq); printk("memory: 0x%lx.\n", btv->bt848_adr); +#if defined(__powerpc__) + /* on OpenFirmware machines (PowerMac at least), PCI memory cycle */ + /* response on cards with no firmware is not enabled by OF */ + pci_read_config_dword(dev, PCI_COMMAND, &cmd); + cmd = (cmd | PCI_COMMAND_MEMORY ); + pci_write_config_dword(dev, PCI_COMMAND, cmd); +#endif + btv->pll.pll_crystal = 0; btv->pll.pll_ifreq = 0; btv->pll.pll_ofreq = 0; @@ -3306,8 +3399,12 @@ break; } } - - btv->bt848_mem = ioremap(btv->bt848_adr, 0x1000); + +#ifdef __sparc__ + btv->bt848_mem=(unsigned char *)btv->bt848_adr; +#else + btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000); +#endif /* clear interrupt mask */ btwrite(0, BT848_INT_MASK); @@ -3393,10 +3490,9 @@ btwrite(0x0, BT848_GPIO_OUT_EN); /* unregister i2c_bus */ - i2c_unregister_bus((&btv->i2c)); + i2c_bit_del_bus(&btv->i2c_adap); /* disable PCI bus-mastering */ - pci_read_config_byte(btv->dev, PCI_COMMAND, &command); /* Should this be &=~ ?? */ command&=~PCI_COMMAND_MASTER; @@ -3436,19 +3532,15 @@ } #ifdef MODULE - -EXPORT_NO_SYMBOLS; - int init_module(void) -{ #else int init_bttv_cards(struct video_init *unused) -{ #endif +{ int i; handle_chipset(); - if (find_bt848()<0) + if (find_bt848()<=0) return -EIO; /* initialize Bt848s */ @@ -3465,7 +3557,6 @@ } - #ifdef MODULE void cleanup_module(void) @@ -3477,14 +3568,6 @@ /* * Local variables: - * c-indent-level: 8 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -8 - * c-argdecl-indent: 8 - * c-label-offset: -8 - * c-continued-statement-offset: 8 - * c-continued-brace-offset: 0 - * indent-tabs-mode: nil - * tab-width: 8 + * c-basic-offset: 8 * End: */ diff -u --recursive --new-file v2.3.33/linux/drivers/char/bttv.h linux/drivers/char/bttv.h --- v2.3.33/linux/drivers/char/bttv.h Tue Nov 23 22:42:20 1999 +++ linux/drivers/char/bttv.h Thu Dec 16 13:59:38 1999 @@ -21,15 +21,34 @@ #ifndef _BTTV_H_ #define _BTTV_H_ -#define BTTV_VERSION_CODE 0x000523 +#define BTTV_VERSION_CODE 0x00070a #include #include -#include -#include "msp3400.h" +#include "audiochip.h" #include "bt848.h" -#include + + +/* experimental, interface might change */ +#ifndef VIDIOCSWIN2 +#define VIDIOCSWIN2 _IOW('v',28,struct video_window2) +struct video_window2 +{ + __u16 palette; /* Palette (aka video format) in use */ + __u32 start; /* start address, relative to video_buffer.base */ + __u32 pitch; + __u32 width; + __u32 height; + __u32 flags; + + struct video_clip *clips; + int clipcount; +}; +#endif + + +#define WAIT_QUEUE wait_queue_head_t #ifndef O_NONCAP #define O_NONCAP O_TRUNC @@ -37,10 +56,10 @@ #define MAX_GBUFFERS 2 #define RISCMEM_LEN (32744*2) -#define VBIBUF_SIZE 65536 +#define VBI_MAXLINES 16 +#define VBIBUF_SIZE (2048*VBI_MAXLINES*2) -/* maximum needed buffer size for extended VBI frame mode capturing */ -#define BTTV_MAX_FBUF 0x190000 +#define BTTV_MAX_FBUF 0x208000 #ifdef __KERNEL__ @@ -58,6 +77,9 @@ int interlace; int color_fmt; ushort depth; + + int use_yuv; + struct video_window2 win2; }; struct bttv_pll_info { @@ -67,16 +89,9 @@ unsigned int pll_current; /* Currently programmed ofreq */ }; -/* Per-open data for handling multiple opens on one device */ -struct device_open -{ - int isopen; - int noncapturing; - struct bttv *dev; -}; -#define MAX_OPENS 3 +#define I2C_CLIENTS_MAX 8 -struct bttv +struct bttv { struct video_device video_dev; struct video_device radio_dev; @@ -84,28 +99,26 @@ struct video_picture picture; /* Current picture params */ struct video_audio audio_dev; /* Current audio params */ - struct semaphore lock; + struct semaphore lock; int user; int capuser; - struct device_open open_data[MAX_OPENS]; - - struct i2c_bus i2c; - int have_msp3400; - int have_tuner; + + /* i2c */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + int i2c_state; + struct i2c_client *i2c_clients[I2C_CLIENTS_MAX]; + int tuner_type; int channel; unsigned int nr; unsigned short id; -#if LINUX_VERSION_CODE < 0x020100 - unsigned char bus; /* PCI bus the Bt848 is on */ - unsigned char devfn; -#else struct pci_dev *dev; -#endif - unsigned int irq; /* IRQ used by Bt848 card */ + unsigned int irq; /* IRQ used by Bt848 card */ unsigned char revision; - unsigned long bt848_adr; /* bus address of IO mem returned by PCI BIOS */ + unsigned long bt848_adr; /* bus address of IO mem returned by PCI BIOS */ unsigned char *bt848_mem; /* pointer to mapped IO memory */ unsigned long busriscmem; u32 *riscmem; @@ -114,8 +127,7 @@ struct bttv_window win; int type; /* card type */ int audio; /* audio mode */ - int audio_chip; - int fader_chip; + int audio_chip; /* set to one of the chips supported by bttv.c */ int radio; u32 *risc_jmp; @@ -123,10 +135,10 @@ u32 *vbi_even; u32 bus_vbi_even; u32 bus_vbi_odd; - wait_queue_head_t vbiq; - wait_queue_head_t capq; - wait_queue_head_t capqo; - wait_queue_head_t capqe; + WAIT_QUEUE vbiq; + WAIT_QUEUE capq; + WAIT_QUEUE capqo; + WAIT_QUEUE capqe; int vbip; u32 *risc_odd; @@ -170,6 +182,17 @@ /*The following should be done in more portable way. It depends on define of _ALPHA_BTTV in the Makefile.*/ +#if defined(__powerpc__) /* big-endian */ +extern __inline__ void io_st_le32(volatile unsigned *addr, unsigned val) +{ + __asm__ __volatile__ ("stwbrx %1,0,%2" : \ + "=m" (*addr) : "r" (val), "r" (addr)); + __asm__ __volatile__ ("eieio" : : : "memory"); +} + +#define btwrite(dat,adr) io_st_le32((unsigned *)(btv->bt848_mem+(adr)),(dat)) +#define btread(adr) ld_le32((unsigned *)(btv->bt848_mem+(adr))) +#else #ifdef _ALPHA_BTTV #define btwrite(dat,adr) writel((dat),(char *) (btv->bt848_adr+(adr))) #define btread(adr) readl(btv->bt848_adr+(adr)) @@ -177,6 +200,7 @@ #define btwrite(dat,adr) writel((dat), (char *) (btv->bt848_mem+(adr))) #define btread(adr) readl(btv->bt848_mem+(adr)) #endif +#endif #define btand(dat,adr) btwrite((dat) & btread(adr), adr) #define btor(dat,adr) btwrite((dat) | btread(adr), adr) @@ -192,7 +216,7 @@ #define BTTV_BURST_OFF _IOR('v' , BASE_VIDIOCPRIVATE+5, int) #define BTTV_VERSION _IOR('v' , BASE_VIDIOCPRIVATE+6, int) #define BTTV_PICNR _IOR('v' , BASE_VIDIOCPRIVATE+7, int) - +#define BTTV_VBISIZE _IOR('v' , BASE_VIDIOCPRIVATE+8, int) #define BTTV_UNKNOWN 0x00 #define BTTV_MIRO 0x01 @@ -220,6 +244,37 @@ #define BTTV_MODTEC_205 0x17 #define BTTV_MAGICTVIEW061 0x18 +#define BTTV_MAXI 0x1b +#define BTTV_TERRATV 0x1c +#define BTTV_PXC200 0x1d + +#if 0 +#define BTTV_UNKNOWN 0x00 +#define BTTV_MIRO 0x01 +#define BTTV_HAUPPAUGE 0x02 +#define BTTV_STB 0x03 +#define BTTV_INTEL 0x04 +#define BTTV_DIAMOND 0x05 +#define BTTV_AVERMEDIA 0x06 +#define BTTV_MATRIX_VISION 0x07 +#define BTTV_FLYVIDEO 0x08 +#define BTTV_TURBOTV 0x09 +#define BTTV_HAUPPAUGE878 0x0a +#define BTTV_MIROPRO 0x0b +#define BTTV_TVBOOSTAR 0x0c +#define BTTV_WINCAM 0x0d +#define BTTV_MAXI 0x0e +#define BTTV_VHX 0x10 +#define BTTV_PXC200 0x11 +#define BTTV_AVERMEDIA98 0x12 +#define BTTV_FLYVIDEO98 0x13 + +#define BTTV_PIXVIEWPLAYTV 0x17 +#define BTTV_WINVIEW_601 0x18 +#define BTTV_CONFERENCETV 0x1c +#endif + + #define AUDIO_TUNER 0x00 #define AUDIO_RADIO 0x01 #define AUDIO_EXTERN 0x02 @@ -230,25 +285,25 @@ #define AUDIO_UNMUTE 0x81 #define TDA9850 0x01 -#define TDA8425 0x02 -#define TDA9840 0x03 +#define TDA9840 0x02 +#define TDA8425 0x03 #define TEA6300 0x04 -#define TEA6320 0x05 #define I2C_TSA5522 0xc2 -#define I2C_TDA9840 0x84 +#define I2C_TDA9840 0x84 #define I2C_TDA9850 0xb6 #define I2C_TDA8425 0x82 #define I2C_HAUPEE 0xa0 #define I2C_STBEE 0xae -#define I2C_VHX 0xc0 -#define I2C_TEA6300 0x80 /* same as TEA6320 */ -#define I2C_TEA6320 0x80 - -#define TDA9840_SW 0x00 -#define TDA9840_LVADJ 0x02 -#define TDA9840_STADJ 0x03 -#define TDA9840_TEST 0x04 +#define I2C_VHX 0xc0 +#define I2C_MSP3400 0x80 +#define I2C_TEA6300 0x80 +#define I2C_DPL3518 0x84 + +#define TDA9840_SW 0x00 +#define TDA9840_LVADJ 0x02 +#define TDA9840_STADJ 0x03 +#define TDA9840_TEST 0x04 #define TDA9850_CON1 0x04 #define TDA9850_CON2 0x05 @@ -263,29 +318,13 @@ #define TDA8425_BA 0x02 #define TDA8425_TR 0x03 #define TDA8425_S1 0x08 - + #define TEA6300_VL 0x00 /* volume control left */ #define TEA6300_VR 0x01 /* volume control right */ #define TEA6300_BA 0x02 /* bass control */ #define TEA6300_TR 0x03 /* treble control */ #define TEA6300_FA 0x04 /* fader control */ #define TEA6300_SW 0x05 /* mute and source switch */ - - -#define TEA6320_V 0x00 -#define TEA6320_FFR 0x01 /* volume front right */ -#define TEA6320_FFL 0x02 /* volume front left */ -#define TEA6320_FRR 0x03 /* volume rear right */ -#define TEA6320_FRL 0x04 /* volume rear left */ -#define TEA6320_BA 0x05 /* bass */ -#define TEA6320_TR 0x06 /* treble */ -#define TEA6320_S 0x07 /* switch register */ - /* values for those registers: */ -#define TEA6320_S_SA 0x01 /* stereo A input */ -#define TEA6320_S_SB 0x07 /* stereo B -- databook wrong? this works */ -#define TEA6320_S_SC 0x04 /* stereo C */ -#define TEA6320_S_GMU 0x80 /* general mute */ - #define PT2254_L_CHANEL 0x10 #define PT2254_R_CHANEL 0x08 diff -u --recursive --new-file v2.3.33/linux/drivers/char/buz.c linux/drivers/char/buz.c --- v2.3.33/linux/drivers/char/buz.c Tue Dec 7 09:32:43 1999 +++ linux/drivers/char/buz.c Thu Dec 16 13:59:38 1999 @@ -54,7 +54,7 @@ #include #include -#include +#include #include "buz.h" #include #include diff -u --recursive --new-file v2.3.33/linux/drivers/char/console.c linux/drivers/char/console.c --- v2.3.33/linux/drivers/char/console.c Thu Nov 11 20:11:33 1999 +++ linux/drivers/char/console.c Mon Dec 20 15:43:01 1999 @@ -91,9 +91,6 @@ #include #include #include -#ifdef CONFIG_APM -#include -#endif #include #include @@ -190,6 +187,12 @@ static int scrollback_delta = 0; /* + * Hook so that the power management routines can (un)blank + * the console on our behalf. + */ +int (*console_blank_hook)(int) = NULL; + +/* * Low-Level Functions */ @@ -2551,10 +2554,8 @@ if (i) set_origin(currcons); -#ifdef CONFIG_APM - if (apm_display_blank()) + if (console_blank_hook && console_blank_hook(1)) return; -#endif if (vesa_blank_mode) sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1); } @@ -2578,9 +2579,8 @@ currcons = fg_console; console_blanked = 0; -#ifdef CONFIG_APM - apm_display_unblank(); -#endif + if (console_blank_hook) + console_blank_hook(0); if (sw->con_blank(vc_cons[currcons].d, 0)) /* Low-level driver cannot restore -> do it ourselves */ update_screen(fg_console); diff -u --recursive --new-file v2.3.33/linux/drivers/char/i2c-old.c linux/drivers/char/i2c-old.c --- v2.3.33/linux/drivers/char/i2c-old.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/i2c-old.c Thu Dec 16 13:59:38 1999 @@ -0,0 +1,458 @@ +/* + * Generic i2c interface for linux + * + * (c) 1998 Gerd Knorr + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REGPRINT(x) if (verbose) (x) +#define I2C_DEBUG(x) if (i2c_debug) (x) + +static int scan = 0; +static int verbose = 0; +static int i2c_debug = 0; + +#if LINUX_VERSION_CODE >= 0x020117 +MODULE_PARM(scan,"i"); +MODULE_PARM(verbose,"i"); +MODULE_PARM(i2c_debug,"i"); +#endif + +/* ----------------------------------------------------------------------- */ + +static struct i2c_bus *busses[I2C_BUS_MAX]; +static struct i2c_driver *drivers[I2C_DRIVER_MAX]; +static int bus_count = 0, driver_count = 0; + +#ifdef CONFIG_VIDEO_BT848 +extern int i2c_tuner_init(void); +extern int msp3400c_init(void); +#endif +#ifdef CONFIG_VIDEO_BUZ +extern int saa7111_init(void); +extern int saa7185_init(void); +#endif +#ifdef CONFIG_VIDEO_LML33 +extern int bt819_init(void); +extern int bt856_init(void); +#endif + +int i2c_init(void) +{ + printk(KERN_INFO "i2c: initialized%s\n", + scan ? " (i2c bus scan enabled)" : ""); + /* anything to do here ? */ +#ifdef CONFIG_VIDEO_BT848 + i2c_tuner_init(); + msp3400c_init(); +#endif +#ifdef CONFIG_VIDEO_BUZ + saa7111_init(); + saa7185_init(); +#endif +#ifdef CONFIG_VIDEO_LML33 + bt819_init(); + bt856_init(); +#endif + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static void i2c_attach_device(struct i2c_bus *bus, struct i2c_driver *driver) +{ + struct i2c_device *device; + int i,j,ack=1; + unsigned char addr; + LOCK_FLAGS; + + /* probe for device */ + LOCK_I2C_BUS(bus); + for (addr = driver->addr_l; addr <= driver->addr_h; addr += 2) + { + i2c_start(bus); + ack = i2c_sendbyte(bus,addr,0); + i2c_stop(bus); + if (!ack) + break; + } + UNLOCK_I2C_BUS(bus); + if (ack) + return; + + /* got answer */ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (NULL == driver->devices[i]) + break; + if (I2C_DEVICE_MAX == i) + return; + + for (j = 0; j < I2C_DEVICE_MAX; j++) + if (NULL == bus->devices[j]) + break; + if (I2C_DEVICE_MAX == j) + return; + + if (NULL == (device = kmalloc(sizeof(struct i2c_device),GFP_KERNEL))) + return; + device->bus = bus; + device->driver = driver; + device->addr = addr; + + /* Attach */ + + if (driver->attach(device)!=0) + { + kfree(device); + return; + } + driver->devices[i] = device; + driver->devcount++; + bus->devices[j] = device; + bus->devcount++; + + if (bus->attach_inform) + bus->attach_inform(bus,driver->id); + REGPRINT(printk("i2c: device attached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,addr,bus->name,driver->name)); +} + +static void i2c_detach_device(struct i2c_device *device) +{ + int i; + + if (device->bus->detach_inform) + device->bus->detach_inform(device->bus,device->driver->id); + device->driver->detach(device); + + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (device == device->driver->devices[i]) + break; + if (I2C_DEVICE_MAX == i) + { + printk(KERN_WARNING "i2c: detach_device #1: device not found: %s\n", + device->name); + return; + } + device->driver->devices[i] = NULL; + device->driver->devcount--; + + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (device == device->bus->devices[i]) + break; + if (I2C_DEVICE_MAX == i) + { + printk(KERN_WARNING "i2c: detach_device #2: device not found: %s\n", + device->name); + return; + } + device->bus->devices[i] = NULL; + device->bus->devcount--; + + REGPRINT(printk("i2c: device detached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,device->addr,device->bus->name,device->driver->name)); + kfree(device); +} + +/* ----------------------------------------------------------------------- */ + +int i2c_register_bus(struct i2c_bus *bus) +{ + int i,ack; + LOCK_FLAGS; + + memset(bus->devices,0,sizeof(bus->devices)); + bus->devcount = 0; + + for (i = 0; i < I2C_BUS_MAX; i++) + if (NULL == busses[i]) + break; + if (I2C_BUS_MAX == i) + return -ENOMEM; + + busses[i] = bus; + bus_count++; + REGPRINT(printk("i2c: bus registered: %s\n",bus->name)); + + MOD_INC_USE_COUNT; + + if (scan) + { + /* scan whole i2c bus */ + LOCK_I2C_BUS(bus); + for (i = 0; i < 256; i+=2) + { + i2c_start(bus); + ack = i2c_sendbyte(bus,i,0); + i2c_stop(bus); + if (!ack) + { + printk(KERN_INFO "i2c: scanning bus %s: found device at addr=0x%02x\n", + bus->name,i); + } + } + UNLOCK_I2C_BUS(bus); + } + + /* probe available drivers */ + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (drivers[i]) + i2c_attach_device(bus,drivers[i]); + return 0; +} + +int i2c_unregister_bus(struct i2c_bus *bus) +{ + int i; + + /* detach devices */ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (bus->devices[i]) + i2c_detach_device(bus->devices[i]); + + for (i = 0; i < I2C_BUS_MAX; i++) + if (bus == busses[i]) + break; + if (I2C_BUS_MAX == i) + { + printk(KERN_WARNING "i2c: unregister_bus #1: bus not found: %s\n", + bus->name); + return -ENODEV; + } + + MOD_DEC_USE_COUNT; + + busses[i] = NULL; + bus_count--; + REGPRINT(printk("i2c: bus unregistered: %s\n",bus->name)); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +int i2c_register_driver(struct i2c_driver *driver) +{ + int i; + + memset(driver->devices,0,sizeof(driver->devices)); + driver->devcount = 0; + + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (NULL == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) + return -ENOMEM; + + drivers[i] = driver; + driver_count++; + + MOD_INC_USE_COUNT; + + REGPRINT(printk("i2c: driver registered: %s\n",driver->name)); + + /* Probe available busses */ + for (i = 0; i < I2C_BUS_MAX; i++) + if (busses[i]) + i2c_attach_device(busses[i],driver); + + return 0; +} + +int i2c_unregister_driver(struct i2c_driver *driver) +{ + int i; + + /* detach devices */ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (driver->devices[i]) + i2c_detach_device(driver->devices[i]); + + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (driver == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) + { + printk(KERN_WARNING "i2c: unregister_driver: driver not found: %s\n", + driver->name); + return -ENODEV; + } + + MOD_DEC_USE_COUNT; + + drivers[i] = NULL; + driver_count--; + REGPRINT(printk("i2c: driver unregistered: %s\n",driver->name)); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +int i2c_control_device(struct i2c_bus *bus, int id, + unsigned int cmd, void *arg) +{ + int i; + + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (bus->devices[i] && bus->devices[i]->driver->id == id) + break; + if (i == I2C_DEVICE_MAX) + return -ENODEV; + if (NULL == bus->devices[i]->driver->command) + return -ENODEV; + return bus->devices[i]->driver->command(bus->devices[i],cmd,arg); +} + +/* ----------------------------------------------------------------------- */ + +#define I2C_SET(bus,ctrl,data) (bus->i2c_setlines(bus,ctrl,data)) +#define I2C_GET(bus) (bus->i2c_getdataline(bus)) + +void i2c_start(struct i2c_bus *bus) +{ + I2C_SET(bus,0,1); + I2C_SET(bus,1,1); + I2C_SET(bus,1,0); + I2C_SET(bus,0,0); + I2C_DEBUG(printk("%s: < ",bus->name)); +} + +void i2c_stop(struct i2c_bus *bus) +{ + I2C_SET(bus,0,0); + I2C_SET(bus,1,0); + I2C_SET(bus,1,1); + I2C_DEBUG(printk(">\n")); +} + +void i2c_one(struct i2c_bus *bus) +{ + I2C_SET(bus,0,1); + I2C_SET(bus,1,1); + I2C_SET(bus,0,1); +} + +void i2c_zero(struct i2c_bus *bus) +{ + I2C_SET(bus,0,0); + I2C_SET(bus,1,0); + I2C_SET(bus,0,0); +} + +int i2c_ack(struct i2c_bus *bus) +{ + int ack; + + I2C_SET(bus,0,1); + I2C_SET(bus,1,1); + ack = I2C_GET(bus); + I2C_SET(bus,0,1); + return ack; +} + +int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack) +{ + int i, ack; + + I2C_SET(bus,0,0); + for (i=7; i>=0; i--) + (data&(1<=0; i--) + { + I2C_SET(bus,1,1); + if (I2C_GET(bus)) + data |= (1<i2c_read) + return bus->i2c_read(bus, addr); + + i2c_start(bus); + i2c_sendbyte(bus,addr,0); + ret = i2c_readbyte(bus,1); + i2c_stop(bus); + return ret; +} + +int i2c_write(struct i2c_bus *bus, unsigned char addr, + unsigned char data1, unsigned char data2, int both) +{ + int ack; + + if (bus->i2c_write) + return bus->i2c_write(bus, addr, data1, data2, both); + + i2c_start(bus); + i2c_sendbyte(bus,addr,0); + ack = i2c_sendbyte(bus,data1,0); + if (both) + ack = i2c_sendbyte(bus,data2,0); + i2c_stop(bus); + return ack ? -1 : 0 ; +} + +/* ----------------------------------------------------------------------- */ + +#ifdef MODULE + +#if LINUX_VERSION_CODE >= 0x020100 +EXPORT_SYMBOL(i2c_register_bus); +EXPORT_SYMBOL(i2c_unregister_bus); +EXPORT_SYMBOL(i2c_register_driver); +EXPORT_SYMBOL(i2c_unregister_driver); +EXPORT_SYMBOL(i2c_control_device); +EXPORT_SYMBOL(i2c_start); +EXPORT_SYMBOL(i2c_stop); +EXPORT_SYMBOL(i2c_one); +EXPORT_SYMBOL(i2c_zero); +EXPORT_SYMBOL(i2c_ack); +EXPORT_SYMBOL(i2c_sendbyte); +EXPORT_SYMBOL(i2c_readbyte); +EXPORT_SYMBOL(i2c_read); +EXPORT_SYMBOL(i2c_write); +#endif + +int init_module(void) +{ + return i2c_init(); +} + +void cleanup_module(void) +{ +} +#endif diff -u --recursive --new-file v2.3.33/linux/drivers/char/i2c-parport.c linux/drivers/char/i2c-parport.c --- v2.3.33/linux/drivers/char/i2c-parport.c Fri Sep 10 23:57:29 1999 +++ linux/drivers/char/i2c-parport.c Thu Dec 16 13:59:38 1999 @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include diff -u --recursive --new-file v2.3.33/linux/drivers/char/i2c.c linux/drivers/char/i2c.c --- v2.3.33/linux/drivers/char/i2c.c Mon Jul 5 20:07:02 1999 +++ linux/drivers/char/i2c.c Wed Dec 31 16:00:00 1969 @@ -1,458 +0,0 @@ -/* - * Generic i2c interface for linux - * - * (c) 1998 Gerd Knorr - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define REGPRINT(x) if (verbose) (x) -#define I2C_DEBUG(x) if (i2c_debug) (x) - -static int scan = 0; -static int verbose = 0; -static int i2c_debug = 0; - -#if LINUX_VERSION_CODE >= 0x020117 -MODULE_PARM(scan,"i"); -MODULE_PARM(verbose,"i"); -MODULE_PARM(i2c_debug,"i"); -#endif - -/* ----------------------------------------------------------------------- */ - -static struct i2c_bus *busses[I2C_BUS_MAX]; -static struct i2c_driver *drivers[I2C_DRIVER_MAX]; -static int bus_count = 0, driver_count = 0; - -#ifdef CONFIG_VIDEO_BT848 -extern int i2c_tuner_init(void); -extern int msp3400c_init(void); -#endif -#ifdef CONFIG_VIDEO_BUZ -extern int saa7111_init(void); -extern int saa7185_init(void); -#endif -#ifdef CONFIG_VIDEO_LML33 -extern int bt819_init(void); -extern int bt856_init(void); -#endif - -int i2c_init(void) -{ - printk(KERN_INFO "i2c: initialized%s\n", - scan ? " (i2c bus scan enabled)" : ""); - /* anything to do here ? */ -#ifdef CONFIG_VIDEO_BT848 - i2c_tuner_init(); - msp3400c_init(); -#endif -#ifdef CONFIG_VIDEO_BUZ - saa7111_init(); - saa7185_init(); -#endif -#ifdef CONFIG_VIDEO_LML33 - bt819_init(); - bt856_init(); -#endif - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static void i2c_attach_device(struct i2c_bus *bus, struct i2c_driver *driver) -{ - struct i2c_device *device; - int i,j,ack=1; - unsigned char addr; - LOCK_FLAGS; - - /* probe for device */ - LOCK_I2C_BUS(bus); - for (addr = driver->addr_l; addr <= driver->addr_h; addr += 2) - { - i2c_start(bus); - ack = i2c_sendbyte(bus,addr,0); - i2c_stop(bus); - if (!ack) - break; - } - UNLOCK_I2C_BUS(bus); - if (ack) - return; - - /* got answer */ - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (NULL == driver->devices[i]) - break; - if (I2C_DEVICE_MAX == i) - return; - - for (j = 0; j < I2C_DEVICE_MAX; j++) - if (NULL == bus->devices[j]) - break; - if (I2C_DEVICE_MAX == j) - return; - - if (NULL == (device = kmalloc(sizeof(struct i2c_device),GFP_KERNEL))) - return; - device->bus = bus; - device->driver = driver; - device->addr = addr; - - /* Attach */ - - if (driver->attach(device)!=0) - { - kfree(device); - return; - } - driver->devices[i] = device; - driver->devcount++; - bus->devices[j] = device; - bus->devcount++; - - if (bus->attach_inform) - bus->attach_inform(bus,driver->id); - REGPRINT(printk("i2c: device attached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,addr,bus->name,driver->name)); -} - -static void i2c_detach_device(struct i2c_device *device) -{ - int i; - - if (device->bus->detach_inform) - device->bus->detach_inform(device->bus,device->driver->id); - device->driver->detach(device); - - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (device == device->driver->devices[i]) - break; - if (I2C_DEVICE_MAX == i) - { - printk(KERN_WARNING "i2c: detach_device #1: device not found: %s\n", - device->name); - return; - } - device->driver->devices[i] = NULL; - device->driver->devcount--; - - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (device == device->bus->devices[i]) - break; - if (I2C_DEVICE_MAX == i) - { - printk(KERN_WARNING "i2c: detach_device #2: device not found: %s\n", - device->name); - return; - } - device->bus->devices[i] = NULL; - device->bus->devcount--; - - REGPRINT(printk("i2c: device detached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,device->addr,device->bus->name,device->driver->name)); - kfree(device); -} - -/* ----------------------------------------------------------------------- */ - -int i2c_register_bus(struct i2c_bus *bus) -{ - int i,ack; - LOCK_FLAGS; - - memset(bus->devices,0,sizeof(bus->devices)); - bus->devcount = 0; - - for (i = 0; i < I2C_BUS_MAX; i++) - if (NULL == busses[i]) - break; - if (I2C_BUS_MAX == i) - return -ENOMEM; - - busses[i] = bus; - bus_count++; - REGPRINT(printk("i2c: bus registered: %s\n",bus->name)); - - MOD_INC_USE_COUNT; - - if (scan) - { - /* scan whole i2c bus */ - LOCK_I2C_BUS(bus); - for (i = 0; i < 256; i+=2) - { - i2c_start(bus); - ack = i2c_sendbyte(bus,i,0); - i2c_stop(bus); - if (!ack) - { - printk(KERN_INFO "i2c: scanning bus %s: found device at addr=0x%02x\n", - bus->name,i); - } - } - UNLOCK_I2C_BUS(bus); - } - - /* probe available drivers */ - for (i = 0; i < I2C_DRIVER_MAX; i++) - if (drivers[i]) - i2c_attach_device(bus,drivers[i]); - return 0; -} - -int i2c_unregister_bus(struct i2c_bus *bus) -{ - int i; - - /* detach devices */ - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (bus->devices[i]) - i2c_detach_device(bus->devices[i]); - - for (i = 0; i < I2C_BUS_MAX; i++) - if (bus == busses[i]) - break; - if (I2C_BUS_MAX == i) - { - printk(KERN_WARNING "i2c: unregister_bus #1: bus not found: %s\n", - bus->name); - return -ENODEV; - } - - MOD_DEC_USE_COUNT; - - busses[i] = NULL; - bus_count--; - REGPRINT(printk("i2c: bus unregistered: %s\n",bus->name)); - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -int i2c_register_driver(struct i2c_driver *driver) -{ - int i; - - memset(driver->devices,0,sizeof(driver->devices)); - driver->devcount = 0; - - for (i = 0; i < I2C_DRIVER_MAX; i++) - if (NULL == drivers[i]) - break; - if (I2C_DRIVER_MAX == i) - return -ENOMEM; - - drivers[i] = driver; - driver_count++; - - MOD_INC_USE_COUNT; - - REGPRINT(printk("i2c: driver registered: %s\n",driver->name)); - - /* Probe available busses */ - for (i = 0; i < I2C_BUS_MAX; i++) - if (busses[i]) - i2c_attach_device(busses[i],driver); - - return 0; -} - -int i2c_unregister_driver(struct i2c_driver *driver) -{ - int i; - - /* detach devices */ - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (driver->devices[i]) - i2c_detach_device(driver->devices[i]); - - for (i = 0; i < I2C_DRIVER_MAX; i++) - if (driver == drivers[i]) - break; - if (I2C_DRIVER_MAX == i) - { - printk(KERN_WARNING "i2c: unregister_driver: driver not found: %s\n", - driver->name); - return -ENODEV; - } - - MOD_DEC_USE_COUNT; - - drivers[i] = NULL; - driver_count--; - REGPRINT(printk("i2c: driver unregistered: %s\n",driver->name)); - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -int i2c_control_device(struct i2c_bus *bus, int id, - unsigned int cmd, void *arg) -{ - int i; - - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (bus->devices[i] && bus->devices[i]->driver->id == id) - break; - if (i == I2C_DEVICE_MAX) - return -ENODEV; - if (NULL == bus->devices[i]->driver->command) - return -ENODEV; - return bus->devices[i]->driver->command(bus->devices[i],cmd,arg); -} - -/* ----------------------------------------------------------------------- */ - -#define I2C_SET(bus,ctrl,data) (bus->i2c_setlines(bus,ctrl,data)) -#define I2C_GET(bus) (bus->i2c_getdataline(bus)) - -void i2c_start(struct i2c_bus *bus) -{ - I2C_SET(bus,0,1); - I2C_SET(bus,1,1); - I2C_SET(bus,1,0); - I2C_SET(bus,0,0); - I2C_DEBUG(printk("%s: < ",bus->name)); -} - -void i2c_stop(struct i2c_bus *bus) -{ - I2C_SET(bus,0,0); - I2C_SET(bus,1,0); - I2C_SET(bus,1,1); - I2C_DEBUG(printk(">\n")); -} - -void i2c_one(struct i2c_bus *bus) -{ - I2C_SET(bus,0,1); - I2C_SET(bus,1,1); - I2C_SET(bus,0,1); -} - -void i2c_zero(struct i2c_bus *bus) -{ - I2C_SET(bus,0,0); - I2C_SET(bus,1,0); - I2C_SET(bus,0,0); -} - -int i2c_ack(struct i2c_bus *bus) -{ - int ack; - - I2C_SET(bus,0,1); - I2C_SET(bus,1,1); - ack = I2C_GET(bus); - I2C_SET(bus,0,1); - return ack; -} - -int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack) -{ - int i, ack; - - I2C_SET(bus,0,0); - for (i=7; i>=0; i--) - (data&(1<=0; i--) - { - I2C_SET(bus,1,1); - if (I2C_GET(bus)) - data |= (1<i2c_read) - return bus->i2c_read(bus, addr); - - i2c_start(bus); - i2c_sendbyte(bus,addr,0); - ret = i2c_readbyte(bus,1); - i2c_stop(bus); - return ret; -} - -int i2c_write(struct i2c_bus *bus, unsigned char addr, - unsigned char data1, unsigned char data2, int both) -{ - int ack; - - if (bus->i2c_write) - return bus->i2c_write(bus, addr, data1, data2, both); - - i2c_start(bus); - i2c_sendbyte(bus,addr,0); - ack = i2c_sendbyte(bus,data1,0); - if (both) - ack = i2c_sendbyte(bus,data2,0); - i2c_stop(bus); - return ack ? -1 : 0 ; -} - -/* ----------------------------------------------------------------------- */ - -#ifdef MODULE - -#if LINUX_VERSION_CODE >= 0x020100 -EXPORT_SYMBOL(i2c_register_bus); -EXPORT_SYMBOL(i2c_unregister_bus); -EXPORT_SYMBOL(i2c_register_driver); -EXPORT_SYMBOL(i2c_unregister_driver); -EXPORT_SYMBOL(i2c_control_device); -EXPORT_SYMBOL(i2c_start); -EXPORT_SYMBOL(i2c_stop); -EXPORT_SYMBOL(i2c_one); -EXPORT_SYMBOL(i2c_zero); -EXPORT_SYMBOL(i2c_ack); -EXPORT_SYMBOL(i2c_sendbyte); -EXPORT_SYMBOL(i2c_readbyte); -EXPORT_SYMBOL(i2c_read); -EXPORT_SYMBOL(i2c_write); -#endif - -int init_module(void) -{ - return i2c_init(); -} - -void cleanup_module(void) -{ -} -#endif diff -u --recursive --new-file v2.3.33/linux/drivers/char/lp.c linux/drivers/char/lp.c --- v2.3.33/linux/drivers/char/lp.c Tue Dec 7 09:32:43 1999 +++ linux/drivers/char/lp.c Mon Dec 20 14:54:38 1999 @@ -679,7 +679,7 @@ } static struct console lpcons = { - "lp", + "lp0", lp_console_write, NULL, lp_console_device, diff -u --recursive --new-file v2.3.33/linux/drivers/char/mem.c linux/drivers/char/mem.c --- v2.3.33/linux/drivers/char/mem.c Tue Dec 7 09:32:43 1999 +++ linux/drivers/char/mem.c Thu Dec 16 13:59:38 1999 @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -23,6 +22,12 @@ #include #include +#ifdef CONFIG_VIDEO_BT848 +extern int i2c_init(void); +#endif +#ifdef CONFIG_I2C +extern int i2c_init_all(void); +#endif #ifdef CONFIG_SOUND void soundcore_init(void); #ifdef CONFIG_SOUND_OSS @@ -664,6 +669,10 @@ #ifdef CONFIG_FTAPE ftape_init(); #endif +#ifdef CONFIG_I2C + i2c_init_all(); +#endif + #ifdef CONFIG_VIDEO_BT848 i2c_init(); #endif diff -u --recursive --new-file v2.3.33/linux/drivers/char/msp3400.c linux/drivers/char/msp3400.c --- v2.3.33/linux/drivers/char/msp3400.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/char/msp3400.c Thu Dec 16 13:59:38 1999 @@ -34,7 +34,6 @@ */ #include -#include #include #include #include @@ -42,6 +41,10 @@ #include #include #include +#include +#include +#include + #ifdef __SMP__ #include #include @@ -51,27 +54,43 @@ #define __KERNEL_SYSCALLS__ #include -#include -#include - -#include "msp3400.h" +#include "audiochip.h" + +#define WAIT_QUEUE wait_queue_head_t /* sound mixer stuff */ #include - -#if LINUX_VERSION_CODE > 0x020140 /* need modular sound driver */ -# if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE) -# define REGISTER_MIXER 1 -# endif +#if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE) +# define REGISTER_MIXER 1 #endif +/* Addresses to scan */ +static unsigned short normal_i2c[] = {I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {0x40,0x44,I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; -static int debug = 0; /* insmod parameter */ +/* insmod parameters */ +static int debug = 0; /* debug output */ +static int once = 0; /* no continous stereo monitoring */ +static int amsound = 0; /* hard-wire AM sound at 6.5 Hz (france), + the autoscan seems work well only with FM... */ +static int simple = -1; /* use short programming (>= msp3410 only) */ +static int dolby = 0; +static int mixer = -1; struct msp3400c { - struct i2c_bus *bus; - + int simple; int nicam; int mode; int norm; @@ -84,36 +103,34 @@ /* thread */ struct task_struct *thread; - wait_queue_head_t wq; + WAIT_QUEUE wq; + struct semaphore *notify; int active,restart,rmmod; int watch_stereo; struct timer_list wake_stereo; + + /* mixer */ + int mixer_modcnt; + int mixer_num; }; +#define MSP3400_MAX 4 +static struct i2c_client *msps[MSP3400_MAX]; + #define VIDEO_MODE_RADIO 16 /* norm magic for radio mode */ /* ---------------------------------------------------------------------- */ #define dprintk if (debug) printk -#if LINUX_VERSION_CODE < 0x020100 -/* 2.0.x */ -#define signal_pending(current) (current->signal & ~current->blocked) -#define sigfillset(set) -#define mdelay(x) udelay(1000*x) -#else +MODULE_PARM(once,"i"); MODULE_PARM(debug,"i"); -#endif - -#if LINUX_VERSION_CODE < 0x02017f -void schedule_timeout(int j) -{ - current->timeout = jiffies + j; - schedule(); -} -#endif +MODULE_PARM(simple,"i"); +MODULE_PARM(amsound,"i"); +MODULE_PARM(dolby,"i"); +MODULE_PARM(mixer,"i"); /* ---------------------------------------------------------------------- */ @@ -124,103 +141,78 @@ /* ----------------------------------------------------------------------- */ /* functions for talking to the MSP3400C Sound processor */ -static int msp3400c_reset(struct i2c_bus *bus) +static int msp3400c_reset(struct i2c_client *client) { - int ret = 0; - - mdelay(2); - i2c_start(bus); - i2c_sendbyte(bus, I2C_MSP3400C,2000); - i2c_sendbyte(bus, 0x00,0); - i2c_sendbyte(bus, 0x80,0); - i2c_sendbyte(bus, 0x00,0); - i2c_stop(bus); - mdelay(2); - i2c_start(bus); - if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) || - 0 != i2c_sendbyte(bus, 0x00,0) || - 0 != i2c_sendbyte(bus, 0x00,0) || - 0 != i2c_sendbyte(bus, 0x00,0)) { - ret = -1; + static char reset_off[3] = { 0x00, 0x80, 0x00 }; + static char reset_on[3] = { 0x00, 0x00, 0x00 }; + + i2c_master_send(client,reset_off,3); /* XXX ignore errors here */ + if (3 != i2c_master_send(client,reset_on, 3)) { printk(KERN_ERR "msp3400: chip reset failed, penguin on i2c bus?\n"); + return -1; } - i2c_stop(bus); - mdelay(2); - return ret; + return 0; } static int -msp3400c_read(struct i2c_bus *bus, int dev, int addr) +msp3400c_read(struct i2c_client *client, int dev, int addr) { - int err,ret; - short val=0; + int err; + + unsigned char write[3]; + unsigned char read[2]; + struct i2c_msg msgs[2] = { + { client->addr, 0, 3, write }, + { client->addr, I2C_M_RD, 2, read } + }; + write[0] = dev+1; + write[1] = addr >> 8; + write[2] = addr & 0xff; for (err = 0; err < 3;) { - ret = 0; - i2c_start(bus); - if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) || - 0 != i2c_sendbyte(bus, dev+1, 0) || - 0 != i2c_sendbyte(bus, addr >> 8, 0) || - 0 != i2c_sendbyte(bus, addr & 0xff, 0)) { - ret = -1; - } else { - i2c_start(bus); - if (0 != i2c_sendbyte(bus, I2C_MSP3400C+1,2000)) { - ret = -1; - } else { - val |= (int)i2c_readbyte(bus,0) << 8; - val |= (int)i2c_readbyte(bus,1); - } - } - i2c_stop(bus); - if (0 == ret) + if (2 == i2c_transfer(client->adapter,msgs,2)) break; - - /* some I/O error */ err++; printk(KERN_WARNING "msp34xx: I/O error #%d (read 0x%02x/0x%02x)\n", err, dev, addr); current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ/10); } - if (-1 == ret) { + if (3 == err) { printk(KERN_WARNING "msp34xx: giving up, reseting chip. Sound will go off, sorry folks :-|\n"); - msp3400c_reset(bus); + msp3400c_reset(client); + return -1; } - return val; + return read[0] << 8 | read[1]; } static int -msp3400c_write(struct i2c_bus *bus, int dev, int addr, int val) +msp3400c_write(struct i2c_client *client, int dev, int addr, int val) { - int ret,err; - + int err; + unsigned char buffer[5]; + + buffer[0] = dev; + buffer[1] = addr >> 8; + buffer[2] = addr & 0xff; + buffer[3] = val >> 8; + buffer[4] = val & 0xff; + for (err = 0; err < 3;) { - ret = 0; - i2c_start(bus); - if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) || - 0 != i2c_sendbyte(bus, dev, 0) || - 0 != i2c_sendbyte(bus, addr >> 8, 0) || - 0 != i2c_sendbyte(bus, addr & 0xff, 0) || - 0 != i2c_sendbyte(bus, val >> 8, 0) || - 0 != i2c_sendbyte(bus, val & 0xff, 0)) - ret = -1; - i2c_stop(bus); - if (0 == ret) + if (5 == i2c_master_send(client, buffer, 5)) break; - - /* some I/O error */ err++; printk(KERN_WARNING "msp34xx: I/O error #%d (write 0x%02x/0x%02x)\n", err, dev, addr); current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ/10); } - if (-1 == ret) { + if (3 == err) { printk(KERN_WARNING "msp34xx: giving up, reseting chip. Sound will go off, sorry folks :-|\n"); - msp3400c_reset(bus); + msp3400c_reset(client); + return -1; } - return ret; + return 0; } /* ------------------------------------------------------------------------ */ @@ -235,6 +227,8 @@ #define MSP_MODE_FM_SAT 4 #define MSP_MODE_FM_NICAM1 5 #define MSP_MODE_FM_NICAM2 6 +#define MSP_MODE_AM_NICAM 7 +#define MSP_MODE_BTSC 8 static struct MSP_INIT_DATA_DEM { int fir1[6]; @@ -271,15 +265,20 @@ MSP_CARRIER(6.5), MSP_CARRIER(6.5), 0x00c6, 0x0480, 0x0000, 0x3000}, - /* NICAM B/G, D/K */ + /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ { { -2, -8, -10, 10, 50, 86 }, { 3, 18, 27, 48, 66, 72 }, MSP_CARRIER(5.5), MSP_CARRIER(5.5), 0x00d0, 0x0040, 0x0120, 0x3000}, - /* NICAM I */ + /* NICAM/FM -- I (6.0/6.552) */ { { 2, 4, -6, -4, 40, 94 }, { 3, 18, 27, 48, 66, 72 }, MSP_CARRIER(6.0), MSP_CARRIER(6.0), 0x00d0, 0x0040, 0x0120, 0x3000}, + + /* NICAM/AM -- L (6.5/5.85) */ + { { -2, -8, -10, 10, 50, 86 }, { -4, -12, -9, 23, 79, 126 }, + MSP_CARRIER(6.5), MSP_CARRIER(6.5), + 0x00c6, 0x0140, 0x0120, 0x7000}, }; struct CARRIER_DETECT { @@ -315,16 +314,16 @@ /* ------------------------------------------------------------------------ */ -static void msp3400c_setcarrier(struct i2c_bus *bus, int cdo1, int cdo2) +static void msp3400c_setcarrier(struct i2c_client *client, int cdo1, int cdo2) { - msp3400c_write(bus,I2C_MSP3400C_DEM, 0x0093, cdo1 & 0xfff); - msp3400c_write(bus,I2C_MSP3400C_DEM, 0x009b, cdo1 >> 12); - msp3400c_write(bus,I2C_MSP3400C_DEM, 0x00a3, cdo2 & 0xfff); - msp3400c_write(bus,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12); - msp3400c_write(bus,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0093, cdo1 & 0xfff); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x009b, cdo1 >> 12); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x00a3, cdo2 & 0xfff); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ } -static void msp3400c_setvolume(struct i2c_bus *bus, int left, int right) +static void msp3400c_setvolume(struct i2c_client *client, int left, int right) { int vol,val,balance; @@ -334,94 +333,106 @@ if (vol > 0) balance = ((right-left) * 127) / vol; - dprintk("msp3400: setvolume: %d:%d 0x%02x 0x%02x\n", + dprintk("msp34xx: setvolume: %d:%d 0x%02x 0x%02x\n", left,right,val>>8,balance); - msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ - msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ /* scart - on/off only */ - msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0); - msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0001, balance << 8); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0001, balance << 8); } -static void msp3400c_setbass(struct i2c_bus *bus, int bass) +static void msp3400c_setbass(struct i2c_client *client, int bass) { int val = ((bass-32768) * 0x60 / 65535) << 8; - dprintk("msp3400: setbass: %d 0x%02x\n",bass, val>>8); - msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0002, val); /* loudspeaker */ + dprintk("msp34xx: setbass: %d 0x%02x\n",bass, val>>8); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0002, val); /* loudspeaker */ } -static void msp3400c_settreble(struct i2c_bus *bus, int treble) +static void msp3400c_settreble(struct i2c_client *client, int treble) { int val = ((treble-32768) * 0x60 / 65535) << 8; - dprintk("msp3400: settreble: %d 0x%02x\n",treble, val>>8); - msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0003, val); /* loudspeaker */ + dprintk("msp34xx: settreble: %d 0x%02x\n",treble, val>>8); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0003, val); /* loudspeaker */ } -static void msp3400c_setmode(struct msp3400c *msp, int type) +static void msp3400c_setmode(struct i2c_client *client, int type) { + struct msp3400c *msp = client->data; int i; dprintk("msp3400: setmode: %d\n",type); msp->mode = type; msp->stereo = VIDEO_SOUND_MONO; - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x00bb, /* ad_cv */ + msp3400c_write(client,I2C_MSP3400C_DEM, 0x00bb, /* ad_cv */ msp_init_data[type].ad_cv); for (i = 5; i >= 0; i--) /* fir 1 */ - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0001, + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0001, msp_init_data[type].fir1[i]); - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0004); /* fir 2 */ - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0040); - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0000); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0004); /* fir 2 */ + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0040); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0000); for (i = 5; i >= 0; i--) - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, msp_init_data[type].fir2[i]); - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0083, /* MODE_REG */ + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0083, /* MODE_REG */ msp_init_data[type].mode_reg); - msp3400c_setcarrier(msp->bus, msp_init_data[type].cdo1, + msp3400c_setcarrier(client, msp_init_data[type].cdo1, msp_init_data[type].cdo2); - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008, - msp_init_data[type].dfp_src); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009, - msp_init_data[type].dfp_src); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a, + if (dolby) { + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, + 0x0520); /* I2S1 */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, + 0x0620); /* I2S2 */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b, + msp_init_data[type].dfp_src); + } else { + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, + msp_init_data[type].dfp_src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, + msp_init_data[type].dfp_src); + } + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a, msp_init_data[type].dfp_src); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, msp_init_data[type].dfp_matrix); if (msp->nicam) { - /* msp3410 needs some more initialization */ - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0010, 0x3000); + /* nicam prescale */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0010, 0x3000); } } -static void msp3400c_setstereo(struct msp3400c *msp, int mode) +/* turn on/off nicam + stereo */ +static void msp3400c_setstereo(struct i2c_client *client, int mode) { + struct msp3400c *msp = client->data; int nicam=0; /* channel source: FM/AM or nicam */ int src=0; - + /* switch demodulator */ switch (msp->mode) { case MSP_MODE_FM_TERRA: dprintk("msp3400: FM setstereo: %d\n",mode); - msp3400c_setcarrier(msp->bus,msp->second,msp->main); + msp3400c_setcarrier(client,msp->second,msp->main); switch (mode) { case VIDEO_SOUND_STEREO: - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3001); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3001); break; case VIDEO_SOUND_MONO: case VIDEO_SOUND_LANG1: case VIDEO_SOUND_LANG2: - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3000); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3000); break; } break; @@ -429,29 +440,33 @@ dprintk("msp3400: SAT setstereo: %d\n",mode); switch (mode) { case VIDEO_SOUND_MONO: - msp3400c_setcarrier(msp->bus, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); + msp3400c_setcarrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); break; case VIDEO_SOUND_STEREO: - msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); + msp3400c_setcarrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); break; case VIDEO_SOUND_LANG1: - msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); break; case VIDEO_SOUND_LANG2: - msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); break; } break; case MSP_MODE_FM_NICAM1: case MSP_MODE_FM_NICAM2: + case MSP_MODE_AM_NICAM: dprintk("msp3400: NICAM setstereo: %d\n",mode); - msp->stereo = mode; - msp3400c_setcarrier(msp->bus,msp->second,msp->main); + msp3400c_setcarrier(client,msp->second,msp->main); if (msp->nicam_on) nicam=0x0100; break; + case MSP_MODE_BTSC: + dprintk("msp3400: BTSC setstereo: %d\n",mode); + nicam=0x0300; + break; default: - /* can't do stereo - abort here */ + dprintk("msp3400: mono setstereo\n"); return; } @@ -459,8 +474,8 @@ switch (mode) { case VIDEO_SOUND_STEREO: src = 0x0020 | nicam; -#if 0 - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0005,0x4000); +#if 0 + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0005,0x4000); #endif break; case VIDEO_SOUND_MONO: @@ -471,9 +486,16 @@ src = 0x0010 | nicam; break; } - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008,src); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009,src); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a,src); + if (dolby) { + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,0x0520); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,0x0620); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src); + } else { + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); + } } static void @@ -488,7 +510,10 @@ } if (msp->mode == MSP_MODE_FM_NICAM1 || msp->mode == MSP_MODE_FM_NICAM2) - printk("msp3400: NICAM carrier : %d.%03d MHz\n", + printk("msp3400: NICAM/FM carrier : %d.%03d MHz\n", + msp->second/910000,(msp->second/910)%1000); + if (msp->mode == MSP_MODE_AM_NICAM) + printk("msp3400: NICAM/AM carrier : %d.%03d MHz\n", msp->second/910000,(msp->second/910)%1000); if (msp->mode == MSP_MODE_FM_TERRA && msp->main != msp->second) { @@ -513,8 +538,9 @@ }; static int -autodetect_stereo(struct msp3400c *msp) +autodetect_stereo(struct i2c_client *client) { + struct msp3400c *msp = client->data; int val; int newstereo = msp->stereo; int newnicam = msp->nicam_on; @@ -522,7 +548,9 @@ switch (msp->mode) { case MSP_MODE_FM_TERRA: - val = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x18); + val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x18); + if (val > 32768) + val -= 65536; dprintk("msp3400: stereo detect register: %d\n",val); if (val > 4096) { @@ -536,7 +564,8 @@ break; case MSP_MODE_FM_NICAM1: case MSP_MODE_FM_NICAM2: - val = msp3400c_read(msp->bus, I2C_MSP3400C_DEM, 0x23); + case MSP_MODE_AM_NICAM: + val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x23); dprintk("msp3400: nicam sync=%d, mode=%d\n",val & 1, (val & 0x1e) >> 1); if (val & 1) { @@ -564,6 +593,16 @@ newnicam=1; } else { newnicam=0; +#if 0 /* fixme: quick & dirty for testing */ + if (msp->main == MSP_CARRIER(6.5)) { + /* This is a french mono channel => AM */ + printk("msp3400: switching to AM mono\n"); + msp3400c_setstereo(msp, VIDEO_SOUND_MONO); + msp->second = msp->main; + msp3400c_setmode(msp, MSP_MODE_AM_DETECT); + msp3400c_setcarrier(client, msp->second, msp->main); + } +#endif newstereo = VIDEO_SOUND_MONO; } break; @@ -596,20 +635,20 @@ } /* stereo/multilang monitoring */ -static void watch_stereo(struct msp3400c *msp) +static void watch_stereo(struct i2c_client *client) { - LOCK_FLAGS; + struct msp3400c *msp = client->data; - LOCK_I2C_BUS(msp->bus); - if (autodetect_stereo(msp)) { + if (autodetect_stereo(client)) { if (msp->stereo & VIDEO_SOUND_STEREO) - msp3400c_setstereo(msp,VIDEO_SOUND_STEREO); + msp3400c_setstereo(client,VIDEO_SOUND_STEREO); else if (msp->stereo & VIDEO_SOUND_LANG1) - msp3400c_setstereo(msp,VIDEO_SOUND_LANG1); + msp3400c_setstereo(client,VIDEO_SOUND_LANG1); else - msp3400c_setstereo(msp,VIDEO_SOUND_MONO); + msp3400c_setstereo(client,VIDEO_SOUND_MONO); } - UNLOCK_I2C_BUS(msp->bus); + if (once) + msp->watch_stereo = 0; if (msp->watch_stereo) { del_timer(&msp->wake_stereo); msp->wake_stereo.expires = jiffies + 5*HZ; @@ -619,11 +658,11 @@ static int msp3400c_thread(void *data) { - struct msp3400c *msp = data; + struct i2c_client *client = data; + struct msp3400c *msp = client->data; struct CARRIER_DETECT *cd; int count, max1,max2,val1,val2, val,this; - LOCK_FLAGS; #ifdef __SMP__ lock_kernel(); @@ -663,15 +702,14 @@ msp->active = 1; if (msp->watch_stereo) { - watch_stereo(msp); + watch_stereo(client); msp->active = 0; continue; } restart: - LOCK_I2C_BUS(msp->bus); - msp3400c_setvolume(msp->bus, 0, 0); - msp3400c_setmode(msp, MSP_MODE_AM_DETECT /* +1 */ ); + msp3400c_setvolume(client, 0, 0); + msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ ); val1 = val2 = 0; max1 = max2 = -1; del_timer(&msp->wake_stereo); @@ -680,8 +718,7 @@ /* carrier detect pass #1 -- main carrier */ cd = carrier_detect_main; count = CARRIER_COUNT(carrier_detect_main); for (this = 0; this < count; this++) { - msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ/25); @@ -692,12 +729,17 @@ goto restart; } - LOCK_I2C_BUS(msp->bus); - val = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b); + val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); if (val1 < val) val1 = val, max1 = this; dprintk("msp3400: carrier1 val: %5d / %s\n", val,cd[this].name); } + + if (amsound) { + /* autodetect does'nt work well with AM ... */ + max1 = 3; + dprintk("msp3400: AM sound override\n"); + } /* carrier detect pass #2 -- second (stereo) carrier */ switch (max1) { @@ -714,8 +756,7 @@ break; } for (this = 0; this < count; this++) { - msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ/25); @@ -726,8 +767,7 @@ goto restart; } - LOCK_I2C_BUS(msp->bus); - val = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b); + val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); if (val2 < val) val2 = val, max2 = this; dprintk("msp3400: carrier2 val: %5d / %s\n", val,cd[this].name); @@ -740,16 +780,16 @@ if (max2 == 0) { /* B/G FM-stereo */ msp->second = carrier_detect_55[max2].cdo; - msp3400c_setmode(msp, MSP_MODE_FM_TERRA); + msp3400c_setmode(client, MSP_MODE_FM_TERRA); msp->nicam_on = 0; - msp3400c_setstereo(msp, VIDEO_SOUND_MONO); + msp3400c_setstereo(client, VIDEO_SOUND_MONO); msp->watch_stereo = 1; } else if (max2 == 1 && msp->nicam) { /* B/G NICAM */ msp->second = carrier_detect_55[max2].cdo; - msp3400c_setmode(msp, MSP_MODE_FM_NICAM1); + msp3400c_setmode(client, MSP_MODE_FM_NICAM1); msp->nicam_on = 1; - msp3400c_setcarrier(msp->bus, msp->second, msp->main); + msp3400c_setcarrier(client, msp->second, msp->main); msp->watch_stereo = 1; } else { goto no_second; @@ -758,25 +798,33 @@ case 2: /* 6.0 */ /* PAL I NICAM */ msp->second = MSP_CARRIER(6.552); - msp3400c_setmode(msp, MSP_MODE_FM_NICAM2); + msp3400c_setmode(client, MSP_MODE_FM_NICAM2); msp->nicam_on = 1; - msp3400c_setcarrier(msp->bus, msp->second, msp->main); + msp3400c_setcarrier(client, msp->second, msp->main); msp->watch_stereo = 1; break; case 3: /* 6.5 */ if (max2 == 1 || max2 == 2) { /* D/K FM-stereo */ msp->second = carrier_detect_65[max2].cdo; - msp3400c_setmode(msp, MSP_MODE_FM_TERRA); + msp3400c_setmode(client, MSP_MODE_FM_TERRA); msp->nicam_on = 0; - msp3400c_setstereo(msp, VIDEO_SOUND_MONO); + msp3400c_setstereo(client, VIDEO_SOUND_MONO); + msp->watch_stereo = 1; + } else if (max2 == 0 && msp->nicam && + msp->norm == VIDEO_MODE_SECAM) { + /* L NICAM */ + msp->second = carrier_detect_65[max2].cdo; + msp3400c_setmode(client, MSP_MODE_AM_NICAM); + msp->nicam_on = 1; + msp3400c_setcarrier(client, msp->second, msp->main); msp->watch_stereo = 1; } else if (max2 == 0 && msp->nicam) { /* D/K NICAM */ msp->second = carrier_detect_65[max2].cdo; - msp3400c_setmode(msp, MSP_MODE_FM_NICAM1); + msp3400c_setmode(client, MSP_MODE_FM_NICAM1); msp->nicam_on = 1; - msp3400c_setcarrier(msp->bus, msp->second, msp->main); + msp3400c_setcarrier(client, msp->second, msp->main); msp->watch_stereo = 1; } else { goto no_second; @@ -786,17 +834,16 @@ default: no_second: msp->second = carrier_detect_main[max1].cdo; - msp3400c_setmode(msp, MSP_MODE_FM_TERRA); + msp3400c_setmode(client, MSP_MODE_FM_TERRA); msp->nicam_on = 0; - msp3400c_setcarrier(msp->bus, msp->second, msp->main); + msp3400c_setcarrier(client, msp->second, msp->main); msp->stereo = VIDEO_SOUND_MONO; - msp3400c_setstereo(msp, VIDEO_SOUND_MONO); + msp3400c_setstereo(client, VIDEO_SOUND_MONO); break; } /* unmute */ - msp3400c_setvolume(msp->bus, msp->left, msp->right); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_setvolume(client, msp->left, msp->right); if (msp->watch_stereo) { del_timer(&msp->wake_stereo); @@ -820,85 +867,208 @@ return 0; } -#if 0 /* not finished yet */ +/* ----------------------------------------------------------------------- */ +/* this one uses the automatic sound standard detection of newer */ +/* msp34xx chip versions */ +static struct MODES { + int retval; + int main, second; + char *name; +} modelist[] = { + { 0x0000, 0, 0, "ERROR" }, + { 0x0001, 0, 0, "autodetect start" }, + { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72 M Dual FM-Stereo" }, + { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74 B/G Dual FM-Stereo" }, + { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25 D/K1 Dual FM-Stereo" }, + { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74 D/K2 Dual FM-Stereo" }, + { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 D/K FM-Mono (HDEV3)" }, + { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85 B/G NICAM FM" }, + { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 L NICAM AM" }, + { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55 I NICAM FM" }, + { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM" }, + { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV2)" }, + { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Stereo" }, + { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Mono + SAP" }, + { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M EIA-J Japan Stereo" }, + { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7 FM-Stereo Radio" }, + { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 SAT-Mono" }, + { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20 SAT-Stereo" }, + { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" }, + { -1, 0, 0, NULL }, /* EOF */ +}; + static int msp3410d_thread(void *data) { - unsigned long flags; - struct msp3400c *msp = data; - DECLARE_MUTEX_LOCKED(sem); - int i, val; - - /* lock_kernel(); */ + struct i2c_client *client = data; + struct msp3400c *msp = client->data; + int mode,val,i,std; + +#ifdef __SMP__ + lock_kernel(); +#endif exit_mm(current); current->session = 1; current->pgrp = 1; sigfillset(¤t->blocked); current->fs->umask = 0; - strcpy(current->comm,"msp3410 (nicam)"); + strcpy(current->comm,"msp3410 [auto]"); - msp->wait = &sem; msp->thread = current; - /* unlock_kernel(); */ +#ifdef __SMP__ + unlock_kernel(); +#endif - dprintk("msp3410: thread: start\n"); + printk("msp3410: daemon started\n"); if(msp->notify != NULL) up(msp->notify); for (;;) { if (msp->rmmod) goto done; - dprintk("msp3410: thread: sleep\n"); - down_interruptible(&sem); - dprintk("msp3410: thread: wakeup\n"); - if (msp->rmmod) + if (debug > 1) + printk("msp3410: thread: sleep\n"); + interruptible_sleep_on(&msp->wq); + if (debug > 1) + printk("msp3410: thread: wakeup\n"); + if (msp->rmmod || signal_pending(current)) goto done; - + if (VIDEO_MODE_RADIO == msp->norm) continue; /* nothing to do */ msp->active = 1; - restart: - LOCK_I2C_BUS(msp->bus); - /* mute */ - msp3400c_setvolume(msp->bus, 0); - /* quick & dirty hack: - get the audio proccessor into some useful state */ - msp3400c_setmode(msp, MSP_MODE_FM_NICAM1); - /* kick autodetect */ - msp3400c_write(msp->bus, I2C_MSP3400C_DFP, 0x20, 0x01); - msp3400c_write(msp->bus, I2C_MSP3400C_DFP, 0x21, 0x01); - UNLOCK_I2C_BUS(msp->bus); - - /* wait 1 sec */ - __set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); - if (signal_pending(current)) - goto done; - if (msp->restart) { - msp->restart = 0; - goto restart; + if (msp->watch_stereo) { +#if 1 /* dump status register */ + val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x200); + printk("msp3410: status=0x%x (pri=%s, sec=%s, %s%s%s)\n", + val, + (val & 0x0002) ? "no" : "yes", + (val & 0x0004) ? "no" : "yes", + (val & 0x0040) ? "stereo" : "mono", + (val & 0x0080) ? ", nicam 2nd mono" : "", + (val & 0x0100) ? ", bilingual/SAP" : ""); + msp->watch_stereo = 0; +#else + watch_stereo(msp); +#endif + msp->active = 0; + continue; } - LOCK_I2C_BUS(msp->bus); - /* debug register dump */ - for (i = 0; i < sizeof(d1)/sizeof(struct REGISTER_DUMP); i++) { - val = msp3400c_read(msp->bus,I2C_MSP3400C_DEM,d1[i].addr); - printk(KERN_DEBUG "msp3400: %s = 0x%x\n",d1[i].name,val); - } + restart: + del_timer(&msp->wake_stereo); + msp->watch_stereo = 0; + + /* put into sane state (and mute) */ + msp3400c_reset(client); + + /* start autodetect */ + switch (msp->norm) { + case VIDEO_MODE_PAL: + mode = 0x1003; + std = 1; + break; + case VIDEO_MODE_NTSC: /* BTSC */ + mode = 0x2003; + std = 0x0020; + break; + case VIDEO_MODE_SECAM: + default: + mode = 0x0003; + std = 1; + break; + } + msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode); + msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, std); + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); + msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); + if (debug) { + int i; + for (i = 0; modelist[i].name != NULL; i++) + if (modelist[i].retval == std) + break; + printk("msp3410: setting mode: %s (0x%04x)\n", + modelist[i].name ? modelist[i].name : "unknown",std); + } + + if (std != 1) { + /* programmed some specific mode */ + val = std; + } else { + /* triggered autodetect */ + for (;;) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); + if (signal_pending(current)) + goto done; + if (msp->restart) { + msp->restart = 0; + goto restart; + } + + /* check results */ + val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); + if (val < 0x07ff) + break; + dprintk("msp3410: detection still in progress\n"); + } + } + for (i = 0; modelist[i].name != NULL; i++) + if (modelist[i].retval == val) + break; + if (debug) + printk("msp3410: current mode: %s (0x%04x)\n", + modelist[i].name ? modelist[i].name : "unknown", + val); + msp->main = modelist[i].main; + msp->second = modelist[i].second; + + /* set prescale / stereo */ + switch (val) { + case 0x0009: + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x7c03); /* AM */ + msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* NICAM */ + break; + case 0x0020: /* BTSC */ + /* just turn on stereo */ + msp->mode = MSP_MODE_BTSC; + msp->stereo = VIDEO_SOUND_STEREO; + msp->watch_stereo = 1; + msp3400c_setstereo(client,VIDEO_SOUND_STEREO); + /* set prescale */ + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */ + break; + case 0x0003: + msp->mode = MSP_MODE_FM_TERRA; + msp->stereo = VIDEO_SOUND_MONO; + msp->watch_stereo = 1; + /* fall */ + default: + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */ + msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* NICAM */ + break; + } + /* unmute */ - msp3400c_setvolume(msp->bus, msp->volume); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_setbass(client, msp->bass); + msp3400c_settreble(client, msp->treble); + msp3400c_setvolume(client, msp->left, msp->right); + + if (msp->watch_stereo) { + del_timer(&msp->wake_stereo); + msp->wake_stereo.expires = jiffies + HZ; + add_timer(&msp->wake_stereo); + } msp->active = 0; } done: dprintk("msp3410: thread: exit\n"); - msp->wait = NULL; msp->active = 0; msp->thread = NULL; @@ -906,23 +1076,17 @@ up(msp->notify); return 0; } -#endif /* ----------------------------------------------------------------------- */ /* mixer stuff -- with the modular sound driver in 2.1.x we can easily */ /* register the msp3400 as mixer device */ -#ifdef REGISTER_MIXER +#ifdef REGISTER_MIXER #include #include #include -static struct msp3400c *mspmix = NULL; /* ugly hack, should do something more sensible */ -static int mixer_num; -static int mixer_modcnt = 0; -static DECLARE_MUTEX(mixer_sem); - static int mix_to_v4l(int i) { int r; @@ -957,14 +1121,22 @@ static int msp3400c_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + struct i2c_client *client; + struct msp3400c *msp; int ret,val = 0; - LOCK_FLAGS; + client = file->private_data; + if (!client) + return -ENODEV; + msp = client->data; + if (!msp) + return -ENODEV; + if (cmd == SOUND_MIXER_INFO) { mixer_info info; strncpy(info.id, "MSP3400", sizeof(info.id)); strncpy(info.name, "MSP 3400", sizeof(info.name)); - info.modify_counter = mixer_modcnt; + info.modify_counter = msp->mixer_modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; @@ -984,12 +1156,6 @@ if (get_user(val, (int *)arg)) return -EFAULT; - down(&mixer_sem); - if (!mspmix) { - up(&mixer_sem); - return -ENODEV; - } - switch (cmd) { case MIXER_READ(SOUND_MIXER_RECMASK): case MIXER_READ(SOUND_MIXER_CAPS): @@ -1006,44 +1172,36 @@ break; case MIXER_WRITE(SOUND_MIXER_VOLUME): - mspmix->left = mix_to_v4l(val); - mspmix->right = mix_to_v4l(val >> 8); - LOCK_I2C_BUS(mspmix->bus); - msp3400c_setvolume(mspmix->bus,mspmix->left,mspmix->right); - UNLOCK_I2C_BUS(mspmix->bus); - mixer_modcnt++; + msp->left = mix_to_v4l(val); + msp->right = mix_to_v4l(val >> 8); + msp3400c_setvolume(client,msp->left,msp->right); + msp->mixer_modcnt++; /* fall */ case MIXER_READ(SOUND_MIXER_VOLUME): - ret = v4l_to_mix2(mspmix->left, mspmix->right); + ret = v4l_to_mix2(msp->left, msp->right); break; case MIXER_WRITE(SOUND_MIXER_BASS): - mspmix->bass = mix_to_v4l(val); - LOCK_I2C_BUS(mspmix->bus); - msp3400c_setbass(mspmix->bus,mspmix->bass); - UNLOCK_I2C_BUS(mspmix->bus); - mixer_modcnt++; + msp->bass = mix_to_v4l(val); + msp3400c_setbass(client,msp->bass); + msp->mixer_modcnt++; /* fall */ case MIXER_READ(SOUND_MIXER_BASS): - ret = v4l_to_mix(mspmix->bass); + ret = v4l_to_mix(msp->bass); break; case MIXER_WRITE(SOUND_MIXER_TREBLE): - mspmix->treble = mix_to_v4l(val); - LOCK_I2C_BUS(mspmix->bus); - msp3400c_settreble(mspmix->bus,mspmix->treble); - UNLOCK_I2C_BUS(mspmix->bus); - mixer_modcnt++; + msp->treble = mix_to_v4l(val); + msp3400c_settreble(client,msp->treble); + msp->mixer_modcnt++; /* fall */ case MIXER_READ(SOUND_MIXER_TREBLE): - ret = v4l_to_mix(mspmix->treble); + ret = v4l_to_mix(msp->treble); break; default: - up(&mixer_sem); return -EINVAL; } - up(&mixer_sem); if (put_user(ret, (int *)arg)) return -EFAULT; return 0; @@ -1052,6 +1210,21 @@ static int msp3400c_mixer_open(struct inode *inode, struct file *file) { + int minor = MINOR(inode->i_rdev); + struct msp3400c *msp; + int i; + + /* search for the right one... */ + for (i = 0; i < MSP3400_MAX; i++) { + msp = msps[i]->data; + if (msp->mixer_num == minor) { + file->private_data = msps[i]; + break; + } + } + if (MSP3400_MAX == i) + return -ENODEV; + MOD_INC_USE_COUNT; return 0; } @@ -1069,7 +1242,7 @@ return -ESPIPE; } -static /*const*/ struct file_operations msp3400c_mixer_fops = { +static struct file_operations msp3400c_mixer_fops = { msp3400c_mixer_llseek, NULL, /* read */ NULL, /* write */ @@ -1091,36 +1264,72 @@ /* ----------------------------------------------------------------------- */ -static int msp3400c_attach(struct i2c_device *device) +static int msp_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind); +static int msp_detach(struct i2c_client *client); +static int msp_probe(struct i2c_adapter *adap); +static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg); + +static struct i2c_driver driver = { + "i2c msp3400 driver", + I2C_DRIVERID_MSP3400, + I2C_DF_NOTIFY, + msp_probe, + msp_detach, + msp_command, +}; + +static struct i2c_client client_template = +{ + "unset", + -1, + 0, + 0, + NULL, + &driver +}; + +static int msp_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) { DECLARE_MUTEX_LOCKED(sem); struct msp3400c *msp; - int rev1,rev2; - LOCK_FLAGS; + struct i2c_client *c; + int rev1,rev2,i; + + client_template.adapter = adap; + client_template.addr = addr; - device->data = msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL); - if (NULL == msp) + if (-1 == msp3400c_reset(&client_template)) { + dprintk("msp3400: no chip found\n"); + return -1; + } + + if (NULL == (c = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) + return -ENOMEM; + memcpy(c,&client_template,sizeof(struct i2c_client)); + if (NULL == (msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL))) { + kfree(c); return -ENOMEM; + } + memset(msp,0,sizeof(struct msp3400c)); - msp->bus = device->bus; msp->left = 65535; msp->right = 65535; msp->bass = 32768; msp->treble = 32768; + c->data = msp; init_waitqueue_head(&msp->wq); - LOCK_I2C_BUS(msp->bus); - if (-1 == msp3400c_reset(msp->bus)) { - UNLOCK_I2C_BUS(msp->bus); + if (-1 == msp3400c_reset(c)) { kfree(msp); dprintk("msp3400: no chip found\n"); return -1; } - rev1 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1e); - rev2 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1f); + rev1 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1e); + rev2 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1f); if (0 == rev1 && 0 == rev2) { - UNLOCK_I2C_BUS(msp->bus); kfree(msp); printk("msp3400: error while reading chip version\n"); return -1; @@ -1128,48 +1337,67 @@ #if 0 /* this will turn on a 1kHz beep - might be useful for debugging... */ - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0014, 0x1040); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0014, 0x1040); #endif - UNLOCK_I2C_BUS(msp->bus); - sprintf(device->name,"MSP34%02d%c-%c%d", + sprintf(c->name,"MSP34%02d%c-%c%d", (rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f); msp->nicam = (((rev2>>8)&0xff) != 00) ? 1 : 0; + if (simple == -1) { + /* default mode */ + msp->simple = 0; + } else { + /* use insmod option */ + msp->simple = simple; + } + /* timer for stereo checking */ msp->wake_stereo.function = msp3400c_stereo_wake; msp->wake_stereo.data = (unsigned long)msp; + /* hello world :-) */ + printk(KERN_INFO "msp3400: init: chip=%s",c->name); + if (msp->nicam) + printk(", has NICAM support"); + printk("\n"); + /* startup control thread */ MOD_INC_USE_COUNT; msp->notify = &sem; - kernel_thread(msp3400c_thread, (void *)msp, 0); + kernel_thread(msp->simple ? msp3410d_thread : msp3400c_thread, + (void *)c, 0); down(&sem); msp->notify = NULL; wake_up_interruptible(&msp->wq); - printk(KERN_INFO "msp3400: init: chip=%s",device->name); - if (msp->nicam) - printk(", has NICAM support"); #ifdef REGISTER_MIXER - down(&mixer_sem); - mspmix = msp; - up(&mixer_sem); + if ((msp->mixer_num = register_sound_mixer(&msp3400c_mixer_fops,mixer)) < 0) + printk(KERN_ERR "msp3400c: cannot allocate mixer device\n"); #endif - printk("\n"); + + /* update our own array */ + for (i = 0; i < MSP3400_MAX; i++) { + if (NULL == msps[i]) { + msps[i] = c; + break; + } + } + + /* done */ + i2c_attach_client(c); return 0; } -static int msp3400c_detach(struct i2c_device *device) +static int msp_detach(struct i2c_client *client) { DECLARE_MUTEX_LOCKED(sem); - struct msp3400c *msp = (struct msp3400c*)device->data; - LOCK_FLAGS; - + struct msp3400c *msp = (struct msp3400c*)client->data; + int i; + #ifdef REGISTER_MIXER - down(&mixer_sem); - mspmix = NULL; - up(&mixer_sem); + if (msp->mixer_num >= 0) + unregister_sound_mixer(msp->mixer_num); #endif /* shutdown control thread */ @@ -1182,47 +1410,143 @@ down(&sem); msp->notify = NULL; } - - LOCK_I2C_BUS(msp->bus); - msp3400c_reset(msp->bus); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_reset(client); + + /* update our own array */ + for (i = 0; i < MSP3400_MAX; i++) { + if (client == msps[i]) { + msps[i] = NULL; + break; + } + } + i2c_detach_client(client); kfree(msp); + kfree(client); MOD_DEC_USE_COUNT; return 0; } -static int msp3400c_command(struct i2c_device *device, - unsigned int cmd, void *arg) +static int msp_probe(struct i2c_adapter *adap) { - struct msp3400c *msp = (struct msp3400c*)device->data; + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, msp_attach); + return 0; +} + +static int msp_command(struct i2c_client *client,unsigned int cmd, void *arg) +{ + struct msp3400c *msp = (struct msp3400c*)client->data; +#if 0 int *iarg = (int*)arg; __u16 *sarg = arg; - LOCK_FLAGS; +#endif switch (cmd) { - case MSP_SET_RADIO: + + case AUDC_SET_RADIO: msp->norm = VIDEO_MODE_RADIO; msp->watch_stereo=0; del_timer(&msp->wake_stereo); - LOCK_I2C_BUS(msp->bus); - msp3400c_setmode(msp,MSP_MODE_FM_RADIO); - msp3400c_setcarrier(msp->bus, MSP_CARRIER(10.7),MSP_CARRIER(10.7)); - msp3400c_setvolume(msp->bus,msp->left, msp->right); - UNLOCK_I2C_BUS(msp->bus); + if (msp->simple) { + msp3400c_reset(client); + msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, 0x0003); /* automatic */ + msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, 0x0040); /* FM Radio */ + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM prescale */ + msp3400c_setbass(client, msp->bass); + msp3400c_settreble(client, msp->treble); + msp3400c_setvolume(client, msp->left, msp->right); + } else { + msp3400c_setmode(client,MSP_MODE_FM_RADIO); + msp3400c_setcarrier(client, MSP_CARRIER(10.7),MSP_CARRIER(10.7)); + msp3400c_setvolume(client,msp->left, msp->right); + } + break; + + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + va->volume=MAX(msp->left,msp->right); + va->balance=(32768*MIN(msp->left,msp->right))/ + (va->volume ? va->volume : 1); + va->balance=(msp->leftright)? + (65535-va->balance) : va->balance; + va->bass = msp->bass; + va->treble = msp->treble; + + if (msp->simple) { + /* fixme */ + } else { + autodetect_stereo(client); + va->mode = msp->stereo; + } + break; + } + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + + msp->left = (MIN(65536 - va->balance,32768) * + va->volume) / 32768; + msp->right = (MIN(va->balance,32768) * + va->volume) / 32768; + msp->bass = va->bass; + msp->treble = va->treble; + msp3400c_setvolume(client,msp->left, msp->right); + msp3400c_setbass(client,msp->bass); + msp3400c_settreble(client,msp->treble); + + if (msp->simple) { + /* fixme */ + } else if (va->mode != 0) { + msp->watch_stereo=0; + del_timer(&msp->wake_stereo); + msp->stereo = va->mode; + msp3400c_setstereo(client,va->mode); + } + break; + } + case VIDIOCSCHAN: + { + struct video_channel *vc = arg; + + msp->norm = vc->norm; + break; + } + case VIDIOCSFREQ: + { + /* new channel -- kick audio carrier scan */ + msp3400c_setvolume(client,0,0); + msp->watch_stereo=0; + del_timer(&msp->wake_stereo); + if (msp->active) + msp->restart = 1; + wake_up_interruptible(&msp->wq); break; - case MSP_SET_TVNORM: + } + + /* --- v4l2 ioctls --- */ + /* NOT YET */ + +#if 0 + /* --- old, obsolete interface --- */ + case AUDC_SET_TVNORM: msp->norm = *iarg; break; - case MSP_SWITCH_MUTE: + case AUDC_SWITCH_MUTE: /* channels switching step one -- mute */ msp->watch_stereo=0; del_timer(&msp->wake_stereo); - LOCK_I2C_BUS(msp->bus); - msp3400c_setvolume(msp->bus,0,0); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_setvolume(client,0,0); break; - case MSP_NEWCHANNEL: + case AUDC_NEWCHANNEL: /* channels switching step two -- trigger sound carrier scan */ msp->watch_stereo=0; del_timer(&msp->wake_stereo); @@ -1231,96 +1555,85 @@ wake_up_interruptible(&msp->wq); break; - case MSP_GET_VOLUME: - *sarg = (msp->left > msp->right) ? msp->left : msp->right; + case AUDC_GET_VOLUME_LEFT: + *sarg = msp->left; + break; + case AUDC_GET_VOLUME_RIGHT: + *sarg = msp->right; break; - case MSP_SET_VOLUME: - msp->left = msp->right = *sarg; - LOCK_I2C_BUS(msp->bus); - msp3400c_setvolume(msp->bus,msp->left, msp->right); - UNLOCK_I2C_BUS(msp->bus); + case AUDC_SET_VOLUME_LEFT: + msp->left = *sarg; + msp3400c_setvolume(client,msp->left, msp->right); + break; + case AUDC_SET_VOLUME_RIGHT: + msp->right = *sarg; + msp3400c_setvolume(client,msp->left, msp->right); break; - case MSP_GET_BASS: + case AUDC_GET_BASS: *sarg = msp->bass; break; - case MSP_SET_BASS: + case AUDC_SET_BASS: msp->bass = *sarg; - LOCK_I2C_BUS(msp->bus); - msp3400c_setbass(msp->bus,msp->bass); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_setbass(client,msp->bass); break; - case MSP_GET_TREBLE: + case AUDC_GET_TREBLE: *sarg = msp->treble; break; - case MSP_SET_TREBLE: + case AUDC_SET_TREBLE: msp->treble = *sarg; - LOCK_I2C_BUS(msp->bus); - msp3400c_settreble(msp->bus,msp->treble); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_settreble(client,msp->treble); break; - case MSP_GET_STEREO: - *sarg = msp->stereo; + case AUDC_GET_STEREO: + if (msp->simple) { + *sarg = 0; /* fixme */ + } else { + autodetect_stereo(client); + *sarg = msp->stereo; + } break; - case MSP_SET_STEREO: + case AUDC_SET_STEREO: + if (msp->simple) + break; /* fixme */ if (*sarg) { msp->watch_stereo=0; del_timer(&msp->wake_stereo); - LOCK_I2C_BUS(msp->bus); - msp3400c_setstereo(msp,*sarg); - UNLOCK_I2C_BUS(msp->bus); + msp->stereo = *sarg; + msp3400c_setstereo(client,*sarg); } break; - case MSP_GET_DC: - LOCK_I2C_BUS(msp->bus); - *sarg = ((int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b) + - (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c)); - UNLOCK_I2C_BUS(msp->bus); + case AUDC_GET_DC: + if (msp->simple) + break; /* fixme */ + *sarg = ((int)msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b) + + (int)msp3400c_read(client, I2C_MSP3400C_DFP, 0x1c)); break; - +#endif default: - return -EINVAL; + /* nothing */ } return 0; } /* ----------------------------------------------------------------------- */ -struct i2c_driver i2c_driver_msp = { - "msp3400", /* name */ - I2C_DRIVERID_MSP3400, /* ID */ - I2C_MSP3400C, I2C_MSP3400C, /* addr range */ - - msp3400c_attach, - msp3400c_detach, - msp3400c_command -}; - #ifdef MODULE int init_module(void) #else - int msp3400c_init(void) +int msp3400c_init(void) #endif { - i2c_register_driver(&i2c_driver_msp); -#ifdef REGISTER_MIXER - if ((mixer_num = register_sound_mixer(&msp3400c_mixer_fops, -1)) < 0) - printk(KERN_ERR "msp3400c: cannot allocate mixer device\n"); -#endif + i2c_add_driver(&driver); return 0; } #ifdef MODULE void cleanup_module(void) { - i2c_unregister_driver(&i2c_driver_msp); -#ifdef REGISTER_MIXER - if (mixer_num >= 0) - unregister_sound_mixer(mixer_num); -#endif + i2c_del_driver(&driver); } #endif @@ -1331,3 +1644,4 @@ * c-basic-offset: 8 * End: */ + diff -u --recursive --new-file v2.3.33/linux/drivers/char/msp3400.h linux/drivers/char/msp3400.h --- v2.3.33/linux/drivers/char/msp3400.h Thu Jan 14 22:53:02 1999 +++ linux/drivers/char/msp3400.h Wed Dec 31 16:00:00 1969 @@ -1,25 +0,0 @@ -#ifndef MSP3400_H -#define MSP3400_H - -/* ---------------------------------------------------------------------- */ - -#define MSP_SET_TVNORM _IOW('m',1,int) /* TV mode + PAL/SECAM/NTSC */ -#define MSP_SET_RADIO _IO('m',2) /* Radio mode */ -#define MSP_NEWCHANNEL _IO('m',3) /* indicate new channel */ - -#define MSP_GET_VOLUME _IOR('m',4,__u16) -#define MSP_SET_VOLUME _IOW('m',5,__u16) - -#define MSP_GET_STEREO _IOR('m',6,__u16) -#define MSP_SET_STEREO _IOW('m',7,__u16) - -#define MSP_GET_DC _IOW('m',8,__u16) - -#define MSP_GET_BASS _IOR('m', 9,__u16) -#define MSP_SET_BASS _IOW('m',10,__u16) -#define MSP_GET_TREBLE _IOR('m',11,__u16) -#define MSP_SET_TREBLE _IOW('m',12,__u16) - -#define MSP_GET_UNIT _IOR('m',13,int) -#define MSP_SWITCH_MUTE _IO('m',14) -#endif /* MSP3400_H */ diff -u --recursive --new-file v2.3.33/linux/drivers/char/pcmcia/Config.in linux/drivers/char/pcmcia/Config.in --- v2.3.33/linux/drivers/char/pcmcia/Config.in Thu Nov 11 20:11:34 1999 +++ linux/drivers/char/pcmcia/Config.in Tue Dec 14 20:43:56 1999 @@ -6,19 +6,19 @@ comment 'PCMCIA character device support' if [ "$CONFIG_SERIAL" = "y" ]; then - dep_tristate ' PCMCIA serial device support' \ + dep_tristate 'PCMCIA serial device support' \ CONFIG_PCMCIA_SERIAL_CS $CONFIG_PCMCIA if [ "$CONFIG_CARDBUS" = "y" ]; then - dep_tristate ' CardBus serial device support' \ + dep_tristate 'CardBus serial device support' \ CONFIG_PCMCIA_SERIAL_CB $CONFIG_PCMCIA fi fi if [ "$CONFIG_SERIAL" = "m" ]; then - dep_tristate ' PCMCIA serial device support' \ + dep_tristate 'PCMCIA serial device support' \ CONFIG_PCMCIA_SERIAL_CS m if [ "$CONFIG_CARDBUS" = "y" ]; then - dep_tristate ' CardBus serial device support' \ + dep_tristate 'CardBus serial device support' \ CONFIG_PCMCIA_SERIAL_CB m fi fi diff -u --recursive --new-file v2.3.33/linux/drivers/char/saa5249.c linux/drivers/char/saa5249.c --- v2.3.33/linux/drivers/char/saa5249.c Sun Nov 7 16:37:34 1999 +++ linux/drivers/char/saa5249.c Thu Dec 16 13:59:38 1999 @@ -50,7 +50,7 @@ #include #include #include -#include +#include #include #include diff -u --recursive --new-file v2.3.33/linux/drivers/char/saa7110.c linux/drivers/char/saa7110.c --- v2.3.33/linux/drivers/char/saa7110.c Tue Dec 7 09:32:43 1999 +++ linux/drivers/char/saa7110.c Thu Dec 16 13:59:38 1999 @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include "linux/video_decoder.h" diff -u --recursive --new-file v2.3.33/linux/drivers/char/saa7111.c linux/drivers/char/saa7111.c --- v2.3.33/linux/drivers/char/saa7111.c Mon Jul 5 20:07:02 1999 +++ linux/drivers/char/saa7111.c Thu Dec 16 13:59:38 1999 @@ -40,7 +40,7 @@ #include #include -#include +#include #include #define DEBUG(x) /* Debug driver */ diff -u --recursive --new-file v2.3.33/linux/drivers/char/saa7185.c linux/drivers/char/saa7185.c --- v2.3.33/linux/drivers/char/saa7185.c Mon Jul 5 20:07:02 1999 +++ linux/drivers/char/saa7185.c Thu Dec 16 13:59:38 1999 @@ -40,7 +40,7 @@ #include #include -#include +#include #include #define DEBUG(x) x /* Debug driver */ diff -u --recursive --new-file v2.3.33/linux/drivers/char/serial.c linux/drivers/char/serial.c --- v2.3.33/linux/drivers/char/serial.c Tue Dec 7 09:32:43 1999 +++ linux/drivers/char/serial.c Thu Dec 16 13:57:05 1999 @@ -198,6 +198,8 @@ #include #endif +#include + /* * All of the compatibilty code so we can compile serial.c against * older kernels is hidden in serial_compat.h diff -u --recursive --new-file v2.3.33/linux/drivers/char/stradis.c linux/drivers/char/stradis.c --- v2.3.33/linux/drivers/char/stradis.c Thu Nov 11 20:11:34 1999 +++ linux/drivers/char/stradis.c Thu Dec 16 13:59:38 1999 @@ -44,7 +44,7 @@ #include #include #include -#include +#include #include "saa7146.h" #include "saa7146reg.h" diff -u --recursive --new-file v2.3.33/linux/drivers/char/tda8425.c linux/drivers/char/tda8425.c --- v2.3.33/linux/drivers/char/tda8425.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/tda8425.c Thu Dec 16 13:59:38 1999 @@ -0,0 +1,325 @@ +/* + * for the TDA8425 chip (I don't know which cards have this) + * WARNING: THIS DRIVER WILL LOAD WITHOUT COMPLAINTS EVEN IF A DIFFERENT + * CHIP IS AT ADDRESS 0x82 (it relies on i2c to make sure that there is a + * device acknowledging that address) + * + * Copyright (c) 1998 Greg Alexander + * This code is placed under the terms of the GNU General Public License + * Code liberally copied from msp3400.c, which is by Gerd Knorr + * + * All of this should work, though it would be nice to eventually support + * balance (different left,right values). Also, the chip seems (?) to have + * two stereo inputs, so if someone has this card, could they tell me if the + * second one can be used for anything (i.e., does it have an external input + * that you can't hear even if you set input to composite?) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bttv.h" +#include "audiochip.h" + +/* Addresses to scan */ +#define I2C_TDA8425 0x82 +static unsigned short normal_i2c[] = { + I2C_TDA8425 >> 1, + I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + +MODULE_PARM(debug,"i"); +static int debug = 0; /* insmod parameter */ +#define dprintk if (debug) printk + + +struct tda8425 { + int mode; /* set to AUDIO_{OFF,TUNER,RADIO,EXTERN} */ + int stereo; + __u16 left,right; + __u16 bass,treble; +}; + +static struct i2c_driver driver; +static struct i2c_client client_template; + + +#define TDA8425_VL 0x00 /* volume left */ +#define TDA8425_VR 0x01 /* volume right */ +#define TDA8425_BA 0x02 /* bass */ +#define TDA8425_TR 0x03 /* treble */ +#define TDA8425_S1 0x08 /* switch functions */ + /* values for those registers: */ +#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */ +#define TDA8425_S1_ON 0xCE /* audio on (mute off) - "linear stereo" mode */ + + +/* ******************************** * + * functions for talking to TDA8425 * + * ******************************** */ + +static int tda8425_write(struct i2c_client *client, int addr, int val) +{ + unsigned char buffer[2]; + + buffer[0] = addr; + buffer[1] = val; + if (2 != i2c_master_send(client,buffer,2)) { + printk(KERN_WARNING "tda8425: I/O error, trying (write %d 0x%x)\n", + addr, val); + return -1; + } + return 0; +} + +static void tda8425_set(struct i2c_client *client) +{ + struct tda8425 *tda = client->data; + + /* mode is ignored today */ + dprintk(KERN_DEBUG "tda8425_set(%04x,%04x,%04x,%04x)\n",tda->left>>10,tda->right>>10,tda->bass>>12,tda->treble>>12); + tda8425_write(client, TDA8425_VL, tda->left>>10 |0xC0); + tda8425_write(client, TDA8425_VR, tda->right>>10 |0xC0); + tda8425_write(client, TDA8425_BA, tda->bass>>12 |0xF0); + tda8425_write(client, TDA8425_TR, tda->treble>>12|0xF0); +} + +static void tda8425_init(struct i2c_client *client) +{ + struct tda8425 *tda = client->data; + + tda->left=tda->right =61440; /* 0dB */ + tda->bass=tda->treble=24576; /* 0dB */ + tda->mode=AUDIO_OFF; + tda->stereo=1; + /* left=right=0x27<<10, bass=treble=0x07<<12 */ + tda8425_write(client, TDA8425_S1, TDA8425_S1_OFF); /* mute */ + tda8425_set(client); +} + +static void tda8425_audio(struct i2c_client *client, int mode) +{ + struct tda8425 *tda = client->data; + + /* valid for AUDIO_TUNER, RADIO, EXTERN, OFF */ + dprintk(KERN_DEBUG "tda8425_audio:%d (T,R,E,I,O)\n",mode); + tda->mode=mode; + tda8425_write(client, TDA8425_S1, + (mode==AUDIO_OFF)?TDA8425_S1_OFF:TDA8425_S1_ON); + /* this is the function we'll need to change if it turns out the + * input-selecting capabilities should be used. */ +} + + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda8425_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct tda8425 *tda; + struct i2c_client *client; + + client = kmalloc(sizeof *client,GFP_KERNEL); + if (!client) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->adapter = adap; + client->addr = addr; + + client->data = tda = kmalloc(sizeof *tda,GFP_KERNEL); + if (!tda) + return -ENOMEM; + memset(tda,0,sizeof *tda); + tda8425_init(client); + MOD_INC_USE_COUNT; + strcpy(client->name,"TDA8425"); + printk(KERN_INFO "tda8425: init\n"); + + i2c_attach_client(client); + return 0; +} + +static int tda8425_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tda8425_attach); + return 0; +} + + +static int tda8425_detach(struct i2c_client *client) +{ + struct tda8425 *tda = client->data; + + tda8425_init(client); + i2c_detach_client(client); + + kfree(tda); + kfree(client); + MOD_DEC_USE_COUNT; + return 0; +} + +static int tda8425_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct tda8425 *tda = client->data; + __u16 *sarg = arg; + + switch (cmd) { + case AUDC_SET_RADIO: + tda8425_audio(client,AUDIO_RADIO); + break; + case AUDC_SET_INPUT: + tda8425_audio(client,*sarg); + break; + + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + va->volume=MAX(tda->left,tda->right); + va->balance=(32768*MIN(tda->left,tda->right))/ + (va->volume ? va->volume : 1); + va->balance=(tda->leftright)? + (65535-va->balance) : va->balance; + va->bass = tda->bass; + va->treble = tda->treble; + break; + } + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + + tda->left = (MIN(65536 - va->balance,32768) * + va->volume) / 32768; + tda->right = (MIN(va->balance,32768) * + va->volume) / 32768; + tda->bass = va->bass; + tda->treble = va->treble; + tda8425_set(client); + break; + } + +#if 0 + /* --- old, obsolete interface --- */ + case AUDC_GET_VOLUME_LEFT: + *sarg = tda->left; + break; + case AUDC_GET_VOLUME_RIGHT: + *sarg = tda->right; + break; + case AUDC_SET_VOLUME_LEFT: + tda->left = *sarg; + tda8425_set(client); + break; + case AUDC_SET_VOLUME_RIGHT: + tda->right = *sarg; + tda8425_set(client); + break; + + case AUDC_GET_BASS: + *sarg = tda->bass; + break; + case AUDC_SET_BASS: + tda->bass = *sarg; + tda8425_set(client); + break; + + case AUDC_GET_TREBLE: + *sarg = tda->treble; + break; + case AUDC_SET_TREBLE: + tda->treble = *sarg; + tda8425_set(client); + break; + + case AUDC_GET_STEREO: + *sarg = tda->stereo?VIDEO_SOUND_STEREO:VIDEO_SOUND_MONO; + break; + case AUDC_SET_STEREO: + tda->stereo=(*sarg==VIDEO_SOUND_MONO)?0:1; + /* TODO: make this write to the TDA9850? */ + break; + +/* case AUDC_SWITCH_MUTE: someday, maybe -- not a lot of point to + case AUDC_NEWCHANNEL: it and it would require preserving state + case AUDC_GET_DC: huh?? (not used by bttv.c) +*/ +#endif + default: + /* nothing */ + } + return 0; +} + + +static struct i2c_driver driver = { + "i2c tda8424 driver", + I2C_DRIVERID_TDA8425, + I2C_DF_NOTIFY, + tda8425_probe, + tda8425_detach, + tda8425_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +#ifdef MODULE +int init_module(void) +#else +int tda8425_init(void) +#endif +{ + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -u --recursive --new-file v2.3.33/linux/drivers/char/tda9855.c linux/drivers/char/tda9855.c --- v2.3.33/linux/drivers/char/tda9855.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/tda9855.c Thu Dec 16 13:59:38 1999 @@ -0,0 +1,456 @@ +/* + * For the TDA9855 chip (afaik, only the Diamond DTV2000 has this) + * This driver will not complain if used with a TDA9850 or any + * other i2c device with the same address. + * + * Copyright (c) 1999 Steve VanDeBogart (vandebo@uclink.berkeley.edu) + * This code is placed under the terms of the GNU General Public License + * Based on tda8425.c by Greg Alexander (c) 1998 + * + * TODO: + * Fix channel change bug - sound goes out when changeing channels, mute + * and unmote to fix. + * Fine tune sound + * Get rest of capabilities into video_audio struct... + * + * Revision: 0.1 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bttv.h" +#include "audiochip.h" + + +MODULE_PARM(debug,"i"); +static int debug = 0; /* insmod parameter */ + +/* Addresses to scan */ +#define I2C_TDA9855_L 0xb4 +#define I2C_TDA9855_H 0xb6 +static unsigned short normal_i2c[] = {I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = { + I2C_TDA9855_L >> 1, + I2C_TDA9855_H >> 1, + I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + +struct tda9855 { + int addr; + int rvol, lvol; + int bass, treble, sub; + int c1, c2, c3; + int a1, a2, a3; +}; + +static struct i2c_driver driver; +static struct i2c_client client_template; + + +#define dprintk if (debug) printk + + /* subaddresses */ +#define TDA9855_VR 0x00 /* Volume, right */ +#define TDA9855_VL 0x01 /* Volume, left */ +#define TDA9855_BA 0x02 /* Bass */ +#define TDA9855_TR 0x03 /* Treble */ +#define TDA9855_SW 0x04 /* Subwoofer - not connected on DTV2000 */ +#define TDA9855_C1 0x05 /* Control 1 */ +#define TDA9855_C2 0x06 /* Control 2 */ +#define TDA9855_C3 0x07 /* Control 3 */ +#define TDA9855_A1 0x08 /* Alignmnet 1*/ +#define TDA9855_A2 0x09 /* Alignmnet 2*/ +#define TDA9855_A3 0x0a /* Alignmnet 3*/ + /* Masks for bits in subaddresses */ +/* VR */ /* VL */ +/* lower 7 bits control gain from -71dB (0x28) to 16dB (0x7f) + * in 1dB steps - mute is 0x27 */ + +/* BA */ +/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19) + * in .5dB steps - 0 is 0x0E */ + +/* TR */ +/* 4 bits << 1 control treble gain from -12dB (0x3) to 12dB (0xb) + * in 3dB steps - 0 is 0x7 */ + +/* SW */ +/* 4 bits << 2 control subwoofer/surraound gain from -14db (0x1) to 14db (0xf) + * in 3dB steps - mute is 0x0 */ + +/* C1 */ +#define TDA9855_MUTE 1<<7 /* GMU, Mute at outputs */ +#define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */ +#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */ +#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */ + /* Bits 0 to 3 select various combinations + * of line in and line out, only the + * interesting ones are defined */ +#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */ +#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */ + +/* C2 */ +#define TDA9855_SAP 3<<6 /* Selects SAP output, mute if not received */ +#define TDA9855_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */ +#define TDA9855_MONO 0 /* Forces Mono output */ +#define TDA9855_TZCM 1<<5 /* If set, don't mute till zero crossing */ +#define TDA9855_VZCM 1<<4 /* If set, don't change volume till zero crossing*/ +#define TDA9855_LMU 1<<3 /* Mute at LOR and LOL */ +#define TDA9855_LINEAR 0 /* Linear Stereo */ +#define TDA9855_PSEUDO 1 /* Pseudo Stereo */ +#define TDA9855_SPAT_30 2 /* Spatial Stereo, 30% anti-phase crosstalk */ +#define TDA9855_SPAT_50 3 /* Spatial Stereo, 52% anti-phase crosstalk */ +#define TDA9855_E_MONO 7 /* Forced mono - mono select elseware, so useless*/ + +/* C3 */ +/* lower 4 bits control input gain from -3.5dB (0x0) to 4dB (0xF) + * in .5dB steps - 0 is 0x7 */ + +/* A1 and A2 (read/write) */ +/* lower 5 bites are wideband and spectral expander alignment + * from 0x00 to 0x1f - nominal at 0x0f and 0x10 (read/write) */ +#define TDA9855_STP 1<<5 /* Stereo Pilot/detect (read-only) */ +#define TDA9855_SAPP 1<<6 /* SAP Pilot/detect (read-only) */ +#define TDA9855_STS 1<<7 /* Stereo trigger 1= <35mV 0= <30mV (write-only)*/ + +/* A3 */ +/* lower 3 bits control timing current for alignment: -30% (0x0), -20% (0x1), + * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */ +/* 2 bits << 5 control AVL attack time: 420ohm (0x0), 730ohm (0x2), + * 1200ohm (0x1), 2100ohm (0x3) */ +#define TDA9855_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral) */ + + +/* Begin code */ + +static int tda9855_write(struct i2c_client *client, int subaddr, int val) +{ + unsigned char buffer[2]; + + buffer[0] = subaddr; + buffer[1] = val; + if (2 != i2c_master_send(client,buffer,2)) { + printk(KERN_WARNING "tda9855: I/O error, trying (write %d 0x%x)\n", + subaddr, val); + return -1; + } + return 0; +} + +static int tda9855_read(struct i2c_client *client) +{ + unsigned char buffer; + + if (1 != i2c_master_recv(client,&buffer,1)) { + printk(KERN_WARNING "tda9855: I/O error, trying (read)\n"); + return -1; + } + return buffer; +} + +static int tda9855_set(struct i2c_client *client) +{ + struct tda9855 *t = client->data; + unsigned char buf[16]; + + dprintk(KERN_INFO "tda9855_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n",t->rvol,t->lvol,t->bass,t->treble,t->sub, + t->c1,t->c2,t->c3,t->a1,t->a2,t->a3); + buf[0] = TDA9855_VR; + buf[1] = t->rvol; + buf[2] = t->lvol; + buf[3] = t->bass; + buf[4] = t->treble; + buf[5] = t->sub; + buf[6] = t->c1; + buf[7] = t->c2; + buf[8] = t->c3; + buf[9] = t->a1; + buf[10] = t->a2; + buf[11] = t->a3; + if (12 != i2c_master_send(client,buf,12)) { + printk(KERN_WARNING "tda9855: I/O error, trying tda9855_set\n"); + return -1; + } + return 0; +} + +static void tda9855_init(struct i2c_client *client) +{ + struct tda9855 *t = client->data; + + t->rvol=0x6f; /* 0dB */ + t->lvol=0x6f; /* 0dB */ + t->bass=0x0e; /* 0dB */ + t->treble=(0x07 << 1); /* 0dB */ + t->sub=0x8 << 2; /* 0dB */ + t->c1=TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT; + /* Set Mute, AVL, Loudness off, Internal sound */ + t->c2=TDA9855_STEREO | TDA9855_LINEAR; /* Set Stereo liner mode */ + t->c3=0x07; /* 0dB input gain */ + t->a1=0x10; /* Select nominal wideband expander */ + t->a2=0x10; /* Select nominal spectral expander and 30mV trigger */ + t->a3=0x3; /* Set: nominal timinig current, 420ohm AVL attack */ + tda9855_write(client, TDA9855_C1, TDA9855_MUTE); /* mute */ + tda9855_set(client); +} + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda9855_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct tda9855 *t; + struct i2c_client *client; + + client = kmalloc(sizeof *client,GFP_KERNEL); + if (!client) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->adapter = adap; + client->addr = addr; + + client->data = t = kmalloc(sizeof *t,GFP_KERNEL); + if (!t) + return -ENOMEM; + memset(t,0,sizeof *t); + tda9855_init(client); + MOD_INC_USE_COUNT; + strcpy(client->name,"TDA9855"); + printk(KERN_INFO "tda9855: init\n"); + + i2c_attach_client(client); + return 0; +} + +static int tda9855_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tda9855_attach); + return 0; +} + +static int tda9855_detach(struct i2c_client *client) +{ + struct tda9855 *t = client->data; + + tda9855_init(client); + i2c_detach_client(client); + + kfree(t); + kfree(client); + MOD_DEC_USE_COUNT; + return 0; +} + +static int tda9855_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct tda9855 *t = client->data; +#if 0 + __u16 *sarg = arg; +#endif + + switch (cmd) { + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + int left,right; + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + + /* min is 0x27 max is 0x7f, vstep is 2e8 */ + left = (t->lvol-0x27)*0x2e8; + right = (t->rvol-0x27)*0x2e8; + va->volume=MAX(left,right); + va->balance=(32768*MIN(left,right))/ + (va->volume ? va->volume : 1); + va->balance=(leftbalance) : va->balance; + va->bass = (t->bass-0x6)*0xccc; /* min 0x6 max is 0x19 */ + va->treble = ((t->treble>>1)-0x3)*0x1c71; + + va->mode = ((TDA9855_STP | TDA9855_SAPP) & + tda9855_read(client)) >> 4; + if (0 == va->mode) + va->mode = VIDEO_SOUND_MONO; + break; + } + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + int left,right; + + left = (MIN(65536 - va->balance,32768) * + va->volume) / 32768; + right = (MIN(va->balance,32768) * + va->volume) / 32768; + t->lvol = left/0x2e8+0x27; + t->rvol = right/0x2e8+0x27; + t->bass = va->bass/0xccc+0x6; + t->treble = (va->treble/0x1c71+0x3)<<1; + tda9855_write(client,TDA9855_VL,t->lvol); + tda9855_write(client,TDA9855_VR,t->rvol); + tda9855_write(client,TDA9855_BA, t->bass); + tda9855_write(client,TDA9855_TR,t->treble); + + switch (va->mode) { + case VIDEO_SOUND_MONO: + t->c2= TDA9855_MONO | (t->c2 & 0x3f); + break; + case VIDEO_SOUND_STEREO: + t->c2= TDA9855_STEREO | (t->c2 & 0x3f); + break; + case VIDEO_SOUND_LANG2: + t->c2= TDA9855_SAP | (t->c2 & 0x3f); + break; + } + tda9855_write(client,TDA9855_C2,t->c2); + break; + } + +#if 0 + /* --- old, obsolete interface --- */ + case AUDC_GET_VOLUME_LEFT: + *sarg = (t->lvol-0x27)*0x2e8; /* min is 0x27 max is 0x7f, vstep is 2e8 */ + break; + case AUDC_GET_VOLUME_RIGHT: + *sarg = (t->rvol-0x27)*0x2e8; + break; + case AUDC_SET_VOLUME_LEFT: + t->lvol = *sarg/0x2e8+0x27; + break; + case AUDC_SET_VOLUME_RIGHT: + t->rvol = *sarg/0x2e8+0x27; + break; + case AUDC_GET_BASS: + *sarg = (t->bass-0x6)*0xccc; /* min 0x6 max is 0x19 */ + break; + case AUDC_SET_BASS: + t->bass = *sarg/0xccc+0x6; + tda9855_write(client,TDA9855_BA, t->bass); + break; + case AUDC_GET_TREBLE: + *sarg = ((t->treble>>1)-0x3)*0x1c71; + break; + case AUDC_SET_TREBLE: + t->treble = (*sarg/0x1c71+0x3)<<1; + tda9855_write(client,TDA9855_TR,t->treble); + break; + case AUDC_GET_STEREO: + *sarg = ((TDA9855_STP | TDA9855_SAPP) & + tda9855_read(client)) >> 4; + if(*sarg==0) *sarg=VIDEO_SOUND_MONO; + break; + case AUDC_SET_STEREO: + if(*sarg==VIDEO_SOUND_MONO) + t->c2= TDA9855_MONO | (t->c2 & 0x3f); + /* Mask out the sap and stereo bits and set mono */ + else if(*sarg==VIDEO_SOUND_STEREO) + t->c2= TDA9855_STEREO | (t->c2 & 0x3f); + /* Mask out the sap and stereo bits and set stereo */ + else if(*sarg==VIDEO_SOUND_LANG2) + t->c2= TDA9855_SAP | (t->c2 & 0x3f); + /* Mask out the sap and stereo bits and set sap */ + tda9855_write(client,TDA9855_C2,t->c2); + break; + case AUDC_SET_INPUT: + dprintk(KERN_INFO "tda9855: SET_INPUT with 0x%04x\n",*sarg); + if((*sarg & (AUDIO_MUTE | AUDIO_OFF))!=0) + t->c1|=TDA9855_MUTE; + else + t->c1= t->c1 & 0x7f; /* won't work --> (~TDA9855_MUTE); */ + if((*sarg & AUDIO_INTERN) == AUDIO_INTERN) + t->c1=(t->c1 & ~0x7) | TDA9855_INT; /* 0x7 is a mask for the int/ext */ + if((*sarg & AUDIO_EXTERN) == AUDIO_EXTERN) + t->c1=(t->c1 & ~0x7) | TDA9855_EXT; /* 0x7 is a mask for the int/ext */ + tda9855_write(client,TDA9855_C1,t->c1); + break; + case AUDC_SWITCH_MUTE: + if((t->c1 & ~TDA9855_MUTE) == 0) + t->c1|=TDA9855_MUTE; + else + t->c1&=~TDA9855_MUTE; + tda9855_write(client,TDA9855_C1,t->c1); + break; + +/* TDA9855 unsupported: */ +/* case AUDC_NEWCHANNEL: + case AUDC_SET_RADIO: + case AUDC_GET_DC: +*/ +#endif + default: + /* nothing */ + } + return 0; +} + + +static struct i2c_driver driver = { + "i2c tda9855 driver", + I2C_DRIVERID_TDA9855, /* FIXME */ + I2C_DF_NOTIFY, + tda9855_probe, + tda9855_detach, + tda9855_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +#ifdef MODULE +int init_module(void) +#else +int tda9855_init(void) +#endif +{ + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_add_driver(&driver); +} +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -u --recursive --new-file v2.3.33/linux/drivers/char/tea6300.c linux/drivers/char/tea6300.c --- v2.3.33/linux/drivers/char/tea6300.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/tea6300.c Thu Dec 16 13:59:38 1999 @@ -0,0 +1,344 @@ +/* + * for the TEA6300 chip (only found on Gateway STB TV/FM cards tho the best + * of my knowledge) + * WARNING: THIS DRIVER WILL LOAD WITHOUT COMPLAINTS EVEN IF THE WRONG + * CHIP (i.e., an MSP3400) IS ON I2C ADDRESS 0x80 (it relies on i2c to + * make sure that there is a device acknowledging that address). This + * is a potential problem because the MSP3400 is very popular and does + * use this address! You have been warned! + * + * Copyright (c) 1998 Greg Alexander + * This code is placed under the terms of the GNU General Public License + * Code liberally copied from msp3400.c, which is by Gerd Knorr + * + * All of this should work, though it would be nice to eventually support + * balance (different left,right values) and, if someone ever finds a card + * with the support (or if you're careful with a soldering iron), fade + * (front/back). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bttv.h" +#include "audiochip.h" + + +/* Addresses to scan */ +#define I2C_TEA6300 0x80 +static unsigned short normal_i2c[] = { + I2C_TEA6300 >> 1, + I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + + +MODULE_PARM(debug,"i"); +static int debug = 0; /* insmod parameter */ + +#define dprintk if (debug) printk + + +struct tea6300 { + int mode; /* set to AUDIO_{OFF,TUNER,RADIO,EXTERN} */ + int stereo; + __u16 left,right; + __u16 bass,treble; +}; + +static struct i2c_driver driver; +static struct i2c_client client_template; + +#define TEA6300_VL 0x00 /* volume left */ +#define TEA6300_VR 0x01 /* volume right */ +#define TEA6300_BA 0x02 /* bass */ +#define TEA6300_TR 0x03 /* treble */ +#define TEA6300_FA 0x04 /* fader control */ +#define TEA6300_S 0x05 /* switch register */ + /* values for those registers: */ +#define TEA6300_S_SA 0x01 /* stereo A input */ +#define TEA6300_S_SB 0x02 /* stereo B */ +#define TEA6300_S_SC 0x04 /* stereo C */ +#define TEA6300_S_GMU 0x80 /* general mute */ + + +/* ******************************** * + * functions for talking to TEA6300 * + * ******************************** */ + +static int tea6300_write(struct i2c_client *client, int addr, int val) +{ + unsigned char buffer[2]; + + buffer[0] = addr; + buffer[1] = val; + if (2 != i2c_master_send(client,buffer,2)) { + printk(KERN_WARNING "tea6300: I/O error, trying (write %d 0x%x)\n", + addr, val); + return -1; + } + return 0; +} + +static void tea6300_set(struct i2c_client *client) +{ + struct tea6300 *tea = client->data; + + /* mode is ignored today */ + dprintk(KERN_DEBUG "tea6300_set(%04x,%04x,%04x,%04x)\n",tea->left>>10,tea->right>>10,tea->bass>>12,tea->treble>>12); + tea6300_write(client, TEA6300_VL, tea->left>>10 ); + tea6300_write(client, TEA6300_VR, tea->right>>10 ); + tea6300_write(client, TEA6300_BA, tea->bass>>12 ); + tea6300_write(client, TEA6300_TR, tea->treble>>12); +} + +static void tea6300_init(struct i2c_client *client) +{ + struct tea6300 *tea = client->data; + + tea->left=tea->right =49152; /* -10dB (loud enough, but not beyond + normal line levels - so as to avoid + clipping */ + tea->bass=tea->treble=28672; /* 0dB */ + tea->mode=AUDIO_OFF; + tea->stereo=1; + /* left=right=0x27<<10, bass=treble=0x07<<12 */ + tea6300_write(client, TEA6300_FA, 0x3f ); /* fader off */ + tea6300_write(client, TEA6300_S , TEA6300_S_GMU); /* mute */ + tea6300_set(client); +} + +static void tea6300_audio(struct i2c_client *client, int mode) +{ + struct tea6300 *tea = client->data; + + /* valid for AUDIO_TUNER, RADIO, EXTERN, OFF */ + dprintk(KERN_DEBUG "tea6300_audio:%d (T,R,E,I,O)\n",mode); + tea->mode=mode; + if (mode==AUDIO_OFF) { /* just mute it */ + tea6300_write(client, TEA6300_S, TEA6300_S_GMU); + return; + } + switch(mode) { + case AUDIO_TUNER: + tea6300_write(client, TEA6300_S, TEA6300_S_SA); + break; + case AUDIO_RADIO: + tea6300_write(client, TEA6300_S, TEA6300_S_SB); + break; + case AUDIO_EXTERN: + tea6300_write(client, TEA6300_S, TEA6300_S_SC); + break; + } +} + + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tea6300_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct tea6300 *tea; + struct i2c_client *client; + + client = kmalloc(sizeof *client,GFP_KERNEL); + if (!client) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->adapter = adap; + client->addr = addr; + + client->data = tea = kmalloc(sizeof *tea,GFP_KERNEL); + if (!tea) + return -ENOMEM; + memset(tea,0,sizeof *tea); + tea6300_init(client); + + MOD_INC_USE_COUNT; + strcpy(client->name,"TEA6300T"); + printk(KERN_INFO "tea6300: initialized\n"); + + i2c_attach_client(client); + return 0; +} + +static int tea6300_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tea6300_attach); + return 0; +} + +static int tea6300_detach(struct i2c_client *client) +{ + struct tea6300 *tea = client->data; + + tea6300_init(client); + i2c_detach_client(client); + + kfree(tea); + kfree(client); + MOD_DEC_USE_COUNT; + return 0; +} + +static int +tea6300_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct tea6300 *tea = client->data; + __u16 *sarg = arg; + + switch (cmd) { + case AUDC_SET_RADIO: + tea6300_audio(client,AUDIO_RADIO); + break; + case AUDC_SET_INPUT: + tea6300_audio(client,*sarg); + break; + + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + va->volume=MAX(tea->left,tea->right); + va->balance=(32768*MIN(tea->left,tea->right))/ + (va->volume ? va->volume : 1); + va->balance=(tea->leftright)? + (65535-va->balance) : va->balance; + va->bass = tea->bass; + va->treble = tea->treble; + break; + } + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + + tea->left = (MIN(65536 - va->balance,32768) * + va->volume) / 32768; + tea->right = (MIN(va->balance,32768) * + va->volume) / 32768; + tea->bass = va->bass; + tea->treble = va->treble; + tea6300_set(client); + break; + } +#if 0 + /* --- old, obsolete interface --- */ + case AUDC_GET_VOLUME_LEFT: + *sarg = tea->left; + break; + case AUDC_GET_VOLUME_RIGHT: + *sarg = tea->right; + break; + case AUDC_SET_VOLUME_LEFT: + tea->left = *sarg; + tea6300_set(client); + break; + case AUDC_SET_VOLUME_RIGHT: + tea->right = *sarg; + tea6300_set(client); + break; + + case AUDC_GET_BASS: + *sarg = tea->bass; + break; + case AUDC_SET_BASS: + tea->bass = *sarg; + tea6300_set(client); + break; + + case AUDC_GET_TREBLE: + *sarg = tea->treble; + break; + case AUDC_SET_TREBLE: + tea->treble = *sarg; + tea6300_set(client); + break; + + case AUDC_GET_STEREO: + *sarg = tea->stereo?VIDEO_SOUND_STEREO:VIDEO_SOUND_MONO; + break; + case AUDC_SET_STEREO: + tea->stereo=(*sarg==VIDEO_SOUND_MONO)?0:1; + /* TODO: make this write to the TDA9850? */ + break; + +/* case AUDC_SWITCH_MUTE: someday, maybe -- not a lot of point to + case AUDC_NEWCHANNEL: it and it would require preserving state + case AUDC_GET_DC: huh?? (not used by bttv.c) +*/ +#endif + default: + /* nothing */ + } + return 0; +} + +static struct i2c_driver driver = { + "i2c tea6300 driver", + I2C_DRIVERID_TEA6300, /* FIXME */ + I2C_DF_NOTIFY, + tea6300_probe, + tea6300_detach, + tea6300_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +#ifdef MODULE +int init_module(void) +#else +int tea6300_init(void) +#endif +{ + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -u --recursive --new-file v2.3.33/linux/drivers/char/tuner.c linux/drivers/char/tuner.c --- v2.3.33/linux/drivers/char/tuner.c Tue Nov 23 22:42:20 1999 +++ linux/drivers/char/tuner.c Thu Dec 16 13:59:38 1999 @@ -6,44 +6,49 @@ #include #include #include -#include - +#include #include +#include #include #include "tuner.h" +/* Addresses to scan */ +static unsigned short normal_i2c[] = {I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {0x60,0x6f,I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + static int debug = 0; /* insmod parameter */ -static int type = -1; /* tuner type */ +static int type = -1; /* insmod parameter */ #define dprintk if (debug) printk -#if LINUX_VERSION_CODE > 0x020100 MODULE_PARM(debug,"i"); MODULE_PARM(type,"i"); -#endif - -#if LINUX_VERSION_CODE < 0x02017f -void schedule_timeout(int j) -{ - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + j; - schedule(); -} -#endif -struct tuner +struct tuner { - struct i2c_bus *bus; /* where is our chip */ - int addr; - int type; /* chip type */ int freq; /* keep track of the current settings */ - int radio; + int std; + int radio; int mode; /* PAL(0)/SECAM(1) mode (PHILIPS_SECAM only) */ }; +static struct i2c_driver driver; +static struct i2c_client client_template; + /* ---------------------------------------------------------------------- */ struct tunertype @@ -58,13 +63,12 @@ unsigned char VHF_H; unsigned char UHF; unsigned char config; - unsigned char I2C; unsigned short IFPCoff; - unsigned char mode; /* mode change value (tested PHILIPS_SECAM only) */ /* 0x01 -> ??? no change ??? */ /* 0x02 -> PAL BDGHI / SECAM L */ /* 0x04 -> ??? PAL others / SECAM others ??? */ + int capability; }; /* @@ -73,42 +77,47 @@ * "no float in kernel" rule. */ static struct tunertype tuners[] = { - {"Temic PAL", TEMIC, PAL, - 16*140.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2,623}, - {"Philips PAL_I", Philips, PAL_I, - 16*140.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc0,623}, - {"Philips NTSC", Philips, NTSC, - 16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,0xc0,732}, - {"Philips SECAM", Philips, SECAM, - 16*168.25,16*447.25,0xA7,0x97,0x37,0x8e,0xc0,623,0x02}, - {"NoTuner", NoTuner, NOTUNER, - 0 ,0 ,0x00,0x00,0x00,0x00,0x00,000}, - {"Philips PAL", Philips, PAL, - 16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,0xc0,623}, - {"Temic NTSC", TEMIC, NTSC, - 16*157.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2,732}, - {"TEMIC PAL_I", TEMIC, PAL_I, - // 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,0xc2,623}, - 16*170.00,16*450.00,0x02,0x04,0x01,0x8e,0xc2,623}, - {"Temic 4036 FY5 NTSC", TEMIC, NTSC, - 16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc2,732}, - {"Alps TSBH1",TEMIC,NTSC, - 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,0xc2,732}, - {"Alps TSBE1",TEMIC,PAL, - 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,0xc2,732}, - {"Alps TSBB5", Alps, PAL_I, /* tested (UK UHF) with Modtec MM205 */ - 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,0xc0,632}, - {"Alps TSBE5", Alps, PAL,/* untested - data sheet guess. Only IF differs. */ - 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,0xc0,622}, - {"Alps TSBC5", Alps, PAL,/* untested - data sheet guess. Only IF differs. */ - 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,0xc0,608}, + { "Temic PAL", TEMIC, PAL, + 16*140.25,16*463.25,0x02,0x04,0x01,0x8e,623}, + { "Philips PAL_I", Philips, PAL_I, + 16*140.25,16*463.25,0xa0,0x90,0x30,0x8e,623}, + { "Philips NTSC", Philips, NTSC, + 16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,732}, + { "Philips SECAM", Philips, SECAM, + 16*168.25,16*447.25,0xA7,0x97,0x37,0x8e,623,0x02}, + { "NoTuner", NoTuner, NOTUNER, + 0,0,0x00,0x00,0x00,0x00,0x00,000}, + { "Philips PAL", Philips, PAL, + 16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,623}, + { "Temic NTSC", TEMIC, NTSC, + 16*157.25,16*463.25,0x02,0x04,0x01,0x8e,732}, + { "Temic PAL_I", TEMIC, PAL_I, + // 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623}, + 16*170.00,16*450.00,0x02,0x04,0x01,0x8e,623}, + { "Temic 4036 FY5 NTSC", TEMIC, NTSC, + 16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,732}, + { "Alps HSBH1", TEMIC, NTSC, + 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,732}, + { "Alps TSBE1",TEMIC,PAL, + 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,732}, + { "Alps TSBB5", Alps, PAL_I, /* tested (UK UHF) with Modtec MM205 */ + 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,632}, + { "Alps TSBE5", Alps, PAL, /* untested - data sheet guess. Only IF differs. */ + 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,622}, + { "Alps TSBC5", Alps, PAL, /* untested - data sheet guess. Only IF differs. */ + 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,608}, }; +#define TUNERS (sizeof(tuners)/sizeof(struct tunertype)) /* ---------------------------------------------------------------------- */ -static int tuner_getstatus (struct tuner *t) +static int tuner_getstatus(struct i2c_client *c) { - return i2c_read(t->bus,t->addr+1); + unsigned char byte; + + if (1 != i2c_master_recv(c,&byte,1)) + return 0; + return byte; } #define TUNER_POR 0x80 @@ -116,22 +125,31 @@ #define TUNER_MODE 0x38 #define TUNER_AFC 0x07 -static int tuner_islocked (struct tuner *t) +static int tuner_islocked (struct i2c_client *c) { - return (tuner_getstatus (t) & TUNER_FL); + return (tuner_getstatus (c) & TUNER_FL); } -static int tuner_afcstatus (struct tuner *t) +static int tuner_afcstatus (struct i2c_client *c) { - return (tuner_getstatus (t) & TUNER_AFC) - 2; + return (tuner_getstatus (c) & TUNER_AFC) - 2; } -static void set_tv_freq(struct tuner *t, int freq) +#if 0 /* unused */ +static int tuner_mode (struct i2c_client *c) +{ + return (tuner_getstatus (c) & TUNER_MODE) >> 3; +} +#endif + +static void set_tv_freq(struct i2c_client *c, int freq) { u8 config; u16 div; struct tunertype *tun; - LOCK_FLAGS; + struct tuner *t = c->data; + unsigned char buffer[4]; + int rc; if (t->type == -1) { printk("tuner: tuner type not set\n"); @@ -146,53 +164,58 @@ else config = tun->UHF; - if (t->type == TUNER_PHILIPS_SECAM && t->mode) +#if 0 // Fix colorstandard mode change + if (t->type == TUNER_PHILIPS_SECAM + && t->std == V4L2_STANDARD_DDD ) config |= tun->mode; else config &= ~tun->mode; +#else + config &= ~tun->mode; +#endif div=freq + tun->IFPCoff; - div&=0x7fff; - LOCK_I2C_BUS(t->bus); + /* + * Philips FI1216MK2 remark from specification : + * for channel selection involving band switching, and to ensure + * smooth tuning to the desired channel without causing + * unnecessary charge pump action, it is recommended to consider + * the difference between wanted channel frequency and the + * current channel frequency. Unnecessary charge pump action + * will result in very low tuning voltage which may drive the + * oscillator to extreme conditions. + */ + /* + * Progfou: specification says to send config data before + * frequency in case (wanted frequency < current frequency). + */ + if (t->type == TUNER_PHILIPS_SECAM && freq < t->freq) { - /* - * Philips FI1216MK2 remark from specification : - * for channel selection involving band switching, and to ensure - * smooth tuning to the desired channel without causing - * unnecessary charge pump action, it is recommended to consider - * the difference between wanted channel frequency and the - * current channel frequency. Unnecessary charge pump action - * will result in very low tuning voltage which may drive the - * oscillator to extreme conditions. - */ - /* - * Progfou: specification says to send config data before - * frequency in case (wanted frequency < current frequency). - */ - if (i2c_write(t->bus, t->addr, tun->config, config, 1)) { - printk("tuner: i2c i/o error #1\n"); - } else { - if (i2c_write(t->bus, t->addr, (div>>8)&0x7f, div&0xff, 1)<0) - printk("tuner: i2c i/o error #2\n"); - } + buffer[0] = tun->config; + buffer[1] = config; + buffer[2] = (div>>8) & 0x7f; + buffer[3] = div & 0xff; } else { - if (i2c_write(t->bus, t->addr, (div>>8)&0x7f, div&0xff, 1)<0) { - printk("tuner: i2c i/o error #1\n"); - } else { - if (i2c_write(t->bus, t->addr, tun->config, config, 1)) - printk("tuner: i2c i/o error #2\n"); - } + buffer[0] = (div>>8) & 0x7f; + buffer[1] = div & 0xff; + buffer[2] = tun->config; + buffer[3] = config; } - UNLOCK_I2C_BUS(t->bus); + + if (4 != (rc = i2c_master_send(c,buffer,4))) + printk("tuner: i2c i/o error: rc == %d (should be 4)\n",rc); + } -static void set_radio_freq(struct tuner *t, int freq) +static void set_radio_freq(struct i2c_client *c, int freq) { u8 config; u16 div; struct tunertype *tun; - LOCK_FLAGS; + struct tuner *t = (struct tuner*)c->data; + unsigned char buffer[4]; + int rc; if (t->type == -1) { printk("tuner: tuner type not set\n"); @@ -204,128 +227,184 @@ div=freq + (int)(16*10.7); div&=0x7fff; - LOCK_I2C_BUS(t->bus); - if (i2c_write(t->bus, t->addr, (div>>8)&0x7f, div&0xff, 1)<0) { - printk("tuner: i2c i/o error #1\n"); - } else { - if (i2c_write(t->bus, t->addr, tun->config, config, 1)) - printk("tuner: i2c i/o error #2\n"); - } + buffer[0] = (div>>8) & 0x7f; + buffer[1] = div & 0xff; + buffer[2] = tun->config; + buffer[3] = config; + if (4 != (rc = i2c_master_send(c,buffer,4))) + printk("tuner: i2c i/o error: rc == %d (should be 4)\n",rc); + if (debug) { - UNLOCK_I2C_BUS(t->bus); current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ/10); - LOCK_I2C_BUS(t->bus); - if (tuner_islocked (t)) + if (tuner_islocked (c)) printk ("tuner: PLL locked\n"); else printk ("tuner: PLL not locked\n"); - printk ("tuner: AFC: %d\n", tuner_afcstatus (t)); + printk ("tuner: AFC: %d\n", tuner_afcstatus (c)); } - UNLOCK_I2C_BUS(t->bus); } - /* ---------------------------------------------------------------------- */ -static int tuner_attach(struct i2c_device *device) + +static int tuner_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) { struct tuner *t; + struct i2c_client *client; - /* - * For now we only try and attach these tuners to the BT848 - * or ZORAN bus. This same module will however work different - * species of card using these chips. Just change the constraints - * (i2c doesn't have a totally clash free 'address' space) - */ - - if(device->bus->id!=I2C_BUSID_BT848 && - device->bus->id!=I2C_BUSID_ZORAN) - return -EINVAL; - - device->data = t = kmalloc(sizeof(struct tuner),GFP_KERNEL); - if (NULL == t) - return -ENOMEM; - memset(t,0,sizeof(struct tuner)); - strcpy(device->name,"tuner"); - t->bus = device->bus; - t->addr = device->addr; - t->type = type; - dprintk("tuner: type is %d (%s)\n",t->type, - (t->type == -1 ) ? "autodetect" : tuners[t->type].name); - + client_template.adapter = adap; + client_template.addr = addr; + + printk("tuner: chip found @ 0x%x\n",addr); + + if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->data = t = kmalloc(sizeof(struct tuner),GFP_KERNEL); + if (NULL == t) { + kfree(client); + return -ENOMEM; + } + memset(t,0,sizeof(struct tuner)); + if (type >= 0 && type < TUNERS) { + t->type = type; + strncpy(client->name, tuners[t->type].name, sizeof(client->name)); + } else { + t->type = -1; + } + i2c_attach_client(client); MOD_INC_USE_COUNT; + return 0; } -static int tuner_detach(struct i2c_device *device) +static int tuner_probe(struct i2c_adapter *adap) { - struct tuner *t = (struct tuner*)device->data; + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tuner_attach); + return 0; +} + +static int tuner_detach(struct i2c_client *client) +{ + struct tuner *t = (struct tuner*)client->data; + + i2c_detach_client(client); kfree(t); + kfree(client); MOD_DEC_USE_COUNT; return 0; } -static int tuner_command(struct i2c_device *device, - unsigned int cmd, void *arg) +static int +tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { - struct tuner *t = (struct tuner*)device->data; - int *iarg = (int*)arg; + struct tuner *t = (struct tuner*)client->data; + int *iarg = (int*)arg; +#if 0 + __u16 *sarg = (__u16*)arg; +#endif + + switch (cmd) { - switch (cmd) + /* --- configuration --- */ + case TUNER_SET_TYPE: + if (t->type != -1) + return 0; + if (*iarg < 0 || *iarg >= TUNERS) + return 0; + t->type = *iarg; + dprintk("tuner: type set to %d (%s)\n", + t->type,tuners[t->type].name); + strncpy(client->name, tuners[t->type].name, sizeof(client->name)); + break; + + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCSCHAN: + { + struct video_channel *vc = arg; + + if (t->type == TUNER_PHILIPS_SECAM) { + t->mode = (vc->norm == VIDEO_MODE_SECAM) ? 1 : 0; + set_tv_freq(client,t->freq); + } + return 0; + } + case VIDIOCSFREQ: { - case TUNER_SET_TYPE: - if (t->type != -1) - return 0; - t->type = *iarg; - dprintk("tuner: type set to %d (%s)\n", - t->type,tuners[t->type].name); - break; + unsigned long *v = arg; - case TUNER_SET_TVFREQ: - dprintk("tuner: tv freq set to %d.%02d\n", - (*iarg)/16,(*iarg)%16*100/16); - set_tv_freq(t,*iarg); - t->radio = 0; - t->freq = *iarg; - break; - - case TUNER_SET_RADIOFREQ: + t->freq = *v; + if (t->radio) { dprintk("tuner: radio freq set to %d.%02d\n", (*iarg)/16,(*iarg)%16*100/16); - set_radio_freq(t,*iarg); - t->radio = 1; - t->freq = *iarg; - break; - - case TUNER_SET_MODE: - if (t->type != TUNER_PHILIPS_SECAM) { - dprintk("tuner: trying to change mode for other than TUNER_PHILIPS_SECAM\n"); - } else { - dprintk("tuner: mode set to %d\n", *iarg); - t->mode = *iarg; - set_tv_freq(t,t->freq); - } - break; - - default: - return -EINVAL; + set_radio_freq(client,t->freq); + } else { + dprintk("tuner: tv freq set to %d.%02d\n", + (*iarg)/16,(*iarg)%16*100/16); + set_tv_freq(client,t->freq); + } + return 0; + } +#if 0 + /* --- old, obsolete interface --- */ + case TUNER_SET_TVFREQ: + dprintk("tuner: tv freq set to %d.%02d\n", + (*iarg)/16,(*iarg)%16*100/16); + set_tv_freq(client,*iarg); + t->radio = 0; + t->freq = *iarg; + break; + + case TUNER_SET_RADIOFREQ: + dprintk("tuner: radio freq set to %d.%02d\n", + (*iarg)/16,(*iarg)%16*100/16); + set_radio_freq(client,*iarg); + t->radio = 1; + t->freq = *iarg; + break; + case TUNER_SET_MODE: + if (t->type != TUNER_PHILIPS_SECAM) { + dprintk("tuner: trying to change mode for other than TUNER_PHILIPS_SECAM\n"); + } else { + int mode=(*sarg==VIDEO_MODE_SECAM)?1:0; + dprintk("tuner: mode set to %d\n", *sarg); + t->mode = mode; + set_tv_freq(client,t->freq); + } + break; +#endif + default: + /* nothing */ } + return 0; } /* ----------------------------------------------------------------------- */ -struct i2c_driver i2c_driver_tuner = +static struct i2c_driver driver = { + "i2c TV tuner driver", + I2C_DRIVERID_TUNER, + I2C_DF_NOTIFY, + tuner_probe, + tuner_detach, + tuner_command, +}; + +static struct i2c_client client_template = { - "tuner", /* name */ - I2C_DRIVERID_TUNER, /* ID */ - 0xc0, 0xce, /* addr range */ - - tuner_attach, - tuner_detach, - tuner_command + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver }; EXPORT_NO_SYMBOLS; @@ -336,14 +415,14 @@ int i2c_tuner_init(void) #endif { - i2c_register_driver(&i2c_driver_tuner); + i2c_add_driver(&driver); return 0; } #ifdef MODULE void cleanup_module(void) { - i2c_unregister_driver(&i2c_driver_tuner); + i2c_del_driver(&driver); } #endif diff -u --recursive --new-file v2.3.33/linux/drivers/char/zr36120.h linux/drivers/char/zr36120.h --- v2.3.33/linux/drivers/char/zr36120.h Tue Dec 7 09:32:43 1999 +++ linux/drivers/char/zr36120.h Thu Dec 16 13:59:38 1999 @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include diff -u --recursive --new-file v2.3.33/linux/drivers/i2c/Config.in linux/drivers/i2c/Config.in --- v2.3.33/linux/drivers/i2c/Config.in Wed Dec 31 16:00:00 1969 +++ linux/drivers/i2c/Config.in Thu Dec 16 13:59:38 1999 @@ -0,0 +1,29 @@ +# +# Character device configuration +# +mainmenu_option next_comment +comment 'I2C support' + +tristate 'I2C support' CONFIG_I2C + +if [ "$CONFIG_I2C" != "n" ]; then + + dep_tristate 'I2C bit-banging interfaces' CONFIG_I2C_ALGOBIT $CONFIG_I2C + if [ "$CONFIG_I2C_ALGOBIT" != "n" ]; then + dep_tristate ' Philips style parallel port adapter' CONFIG_I2C_PHILIPSPAR $CONFIG_I2C_ALGOBIT + dep_tristate ' ELV adapter' CONFIG_I2C_ELV $CONFIG_I2C_ALGOBIT + dep_tristate ' Velleman K9000 adapter' CONFIG_I2C_VELLEMAN $CONFIG_I2C_ALGOBIT + fi + + dep_tristate 'I2C PCF 8584 interfaces' CONFIG_I2C_ALGOPCF $CONFIG_I2C + if [ "$CONFIG_I2C_ALGOPCF" != "n" ]; then + dep_tristate ' Elektor ISA card' CONFIG_I2C_ELEKTOR $CONFIG_I2C_ALGOPCF + fi + +# This is needed for automatic patch generation: sensors code starts here +# This is needed for automatic patch generation: sensors code ends here + + dep_tristate 'I2C device interface' CONFIG_I2C_CHARDEV $CONFIG_I2C + +fi +endmenu diff -u --recursive --new-file v2.3.33/linux/drivers/i2c/Makefile linux/drivers/i2c/Makefile --- v2.3.33/linux/drivers/i2c/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/i2c/Makefile Thu Dec 16 13:59:38 1999 @@ -0,0 +1,98 @@ +# +# Makefile for the kernel i2c bus driver. +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) +MOD_LIST_NAME := I2C_MODULES + +L_TARGET := i2c.a +MX_OBJS := +M_OBJS := +LX_OBJS := +L_OBJS := + +# ----- +# i2c core components +# ----- + +ifeq ($(CONFIG_I2C),y) + LX_OBJS += i2c-core.o +else + ifeq ($(CONFIG_I2C),m) + MX_OBJS += i2c-core.o + endif +endif + +ifeq ($(CONFIG_I2C_CHARDEV),y) + L_OBJS += i2c-dev.o +else + ifeq ($(CONFIG_I2C_CHARDEV),m) + M_OBJS += i2c-dev.o + endif +endif + +# ----- +# Bit banging adapters... +# ----- + +ifeq ($(CONFIG_I2C_ALGOBIT),y) + LX_OBJS += i2c-algo-bit.o +else + ifeq ($(CONFIG_I2C_ALGOBIT),m) + MX_OBJS += i2c-algo-bit.o + endif +endif + +ifeq ($(CONFIG_I2C_PHILIPSPAR),y) + L_OBJS += i2c-philips-par.o +else + ifeq ($(CONFIG_I2C_PHILIPSPAR),m) + M_OBJS += i2c-philips-par.o + endif +endif + +ifeq ($(CONFIG_I2C_ELV),y) + L_OBJS += i2c-elv.o +else + ifeq ($(CONFIG_I2C_ELV),m) + M_OBJS += i2c-elv.o + endif +endif + +ifeq ($(CONFIG_I2C_VELLEMAN),y) + L_OBJS += i2c-velleman.o +else + ifeq ($(CONFIG_I2C_VELLEMAN),m) + M_OBJS += i2c-velleman.o + endif +endif + + + +# ----- +# PCF components +# ----- + +ifeq ($(CONFIG_I2C_ALGOPCF),y) + LX_OBJS += i2c-algo-pcf.o +else + ifeq ($(CONFIG_I2C_ALGOPCF),m) + MX_OBJS += i2c-algo-pcf.o + endif +endif + +ifeq ($(CONFIG_I2C_ELEKTOR),y) + L_OBJS += i2c-elektor.o +else + ifeq ($(CONFIG_I2C_ELEKTOR),m) + M_OBJS += i2c-elektor.o + endif +endif + +# This is needed for automatic patch generation: sensors code starts here +# This is needed for automatic patch generation: sensors code ends here + +include $(TOPDIR)/Rules.make + diff -u --recursive --new-file v2.3.33/linux/drivers/i2c/i2c-algo-bit.c linux/drivers/i2c/i2c-algo-bit.c --- v2.3.33/linux/drivers/i2c/i2c-algo-bit.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/i2c/i2c-algo-bit.c Thu Dec 16 13:59:38 1999 @@ -0,0 +1,658 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-algo-bit.c i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + + +/* +static char alg_rcsid[] = "$Id: i2c-algo-bit.c,v 1.20 1999/11/12 11:26:20 frodo Exp $"; +*/ + +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020135 +#include +#else +#define __init +#endif + +#if LINUX_VERSION_CODE >= 0x020100 +# include +#else +# include +#endif + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#include +#include + +#include +#include + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x; +#define DEB2(x) if (i2c_debug>=2) x; +#define DEBSTAT(x) if (i2c_debug>=3) x; /* print several statistical values*/ +#define DEBPROTO(x) if (i2c_debug>=9) { x; } + /* debug the protocol by showing transferred bits */ + +/* debugging - slow down transfer to have a look at the data .. */ +/* I use this with two leds&resistors, each one connected to sda,scl */ +/* respectively. This makes sure that the algorithm works. Some chips */ +/* might not like this, as they have an internal timeout of some mils */ +/* +#if LINUX_VERSION_CODE >= 0x02016e +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#else +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#endif +*/ + + +/* ----- global variables --------------------------------------------- */ + +#ifdef SLO_IO + int jif; +#endif + +/* module parameters: + */ +static int i2c_debug=0; +static int bit_test=0; /* see if the line-setting functions work */ +static int bit_scan=0; /* have a look at what's hanging 'round */ + +/* --- setting states on the bus with the right timing: --------------- */ + +#define setsda(adap,val) adap->setsda(adap->data, val) +#define setscl(adap,val) adap->setscl(adap->data, val) +#define getsda(adap) adap->getsda(adap->data) +#define getscl(adap) adap->getscl(adap->data) + +static inline void sdalo(struct i2c_algo_bit_data *adap) +{ + setsda(adap,0); + udelay(adap->udelay); +} + +static inline void sdahi(struct i2c_algo_bit_data *adap) +{ + setsda(adap,1); + udelay(adap->udelay); +} + +static inline void scllo(struct i2c_algo_bit_data *adap) +{ + setscl(adap,0); + udelay(adap->udelay); +#ifdef SLO_IO + SLO_IO +#endif +} + +/* + * Raise scl line, and do checking for delays. This is necessary for slower + * devices. + */ +static inline int sclhi(struct i2c_algo_bit_data *adap) +{ + int start=jiffies; + + setscl(adap,1); + + udelay(adap->udelay); + + /* Not all adapters have scl sense line... */ + if (adap->getscl == NULL ) + return 0; + + while (! getscl(adap) ) { + /* the hw knows how to read the clock line, + * so we wait until it actually gets high. + * This is safer as some chips may hold it low + * while they are processing data internally. + */ + setscl(adap,1); + if (start+adap->timeout <= jiffies) { + return -ETIMEDOUT; + } +#if LINUX_VERSION_CODE >= 0x02016e + if (current->need_resched) + schedule(); +#else + if (need_resched) + schedule(); +#endif + } + DEBSTAT(printk("needed %ld jiffies\n", jiffies-start)); +#ifdef SLO_IO + SLO_IO +#endif + return 0; +} + + +/* --- other auxiliary functions -------------------------------------- */ +static void i2c_start(struct i2c_algo_bit_data *adap) +{ + /* assert: scl, sda are high */ + DEBPROTO(printk("S ")); + sdalo(adap); + scllo(adap); +} + +static void i2c_repstart(struct i2c_algo_bit_data *adap) +{ + /* scl, sda may not be high */ + DEBPROTO(printk(" Sr ")); + setsda(adap,1); + setscl(adap,1); + udelay(adap->udelay); + + sdalo(adap); + scllo(adap); +} + + +static void i2c_stop(struct i2c_algo_bit_data *adap) +{ + DEBPROTO(printk("P\n")); + /* assert: scl is low */ + sdalo(adap); + sclhi(adap); + sdahi(adap); +} + + + +/* send a byte without start cond., look for arbitration, + check ackn. from slave */ +/* returns: + * 1 if the device acknowledged + * 0 if the device did not ack + * -ETIMEDOUT if an error occured (while raising the scl line) + */ +static int i2c_outb(struct i2c_adapter *i2c_adap, char c) +{ + int i; + int sb; + int ack; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + DEB2(printk(" i2c_outb:%2.2X\n",c&0xff)); + for ( i=7 ; i>=0 ; i-- ) { + sb = c & ( 1 << i ); + setsda(adap,sb); + udelay(adap->udelay); + DEBPROTO(printk("%d",sb!=0)); + if (sclhi(adap)<0) { /* timed out */ + sdahi(adap); /* we don't want to block the net */ + return -ETIMEDOUT; + }; + /* do arbitration here: + * if ( sb && ! getsda(adap) ) -> ouch! Get out of here. + */ + setscl(adap, 0 ); + udelay(adap->udelay); + } + sdahi(adap); + if (sclhi(adap)<0){ /* timeout */ + return -ETIMEDOUT; + }; + /* read ack: SDA should be pulled down by slave */ + ack=getsda(adap); /* ack: sda is pulled low ->success. */ + DEB2(printk(" i2c_outb: getsda() = 0x%2.2x\n", ~ack )); + + DEBPROTO( printk("[%2.2x]",c&0xff) ); + DEBPROTO(if (0==ack){ printk(" A ");} else printk(" NA ") ); + scllo(adap); + return 0==ack; /* return 1 if device acked */ + /* assert: scl is low (sda undef) */ +} + + +static int i2c_inb(struct i2c_adapter *i2c_adap) +{ + /* read byte via i2c port, without start/stop sequence */ + /* acknowledge is sent in i2c_read. */ + int i; + unsigned char indata=0; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + DEB2(printk("i2c_inb.\n")); + + sdahi(adap); + for (i=0;i<8;i++) { + if (sclhi(adap)<0) { /* timeout */ + return -ETIMEDOUT; + }; + indata *= 2; + if ( getsda(adap) ) + indata |= 0x01; + scllo(adap); + } + /* assert: scl is low */ + DEBPROTO(printk(" %2.2x", indata & 0xff)); + return (int) (indata & 0xff); +} + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct i2c_algo_bit_data *adap, char* name) { + int scl,sda; + sda=getsda(adap); + if (adap->getscl==NULL) { + printk("i2c-algo-bit.o: Warning: Adapter can't read from clock line - skipping test.\n"); + return 0; + } + scl=getscl(adap); + printk("i2c-algo-bit.o: Adapter: %s scl: %d sda: %d -- testing...\n", + name,getscl(adap),getsda(adap)); + if (!scl || !sda ) { + printk("i2c-algo-bit.o: %s seems to be busy.\n",name); + goto bailout; + } + sdalo(adap); + printk("i2c-algo-bit.o:1 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA stuck high!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL unexpected low while pulling SDA low!\n", + name); + goto bailout; + } + sdahi(adap); + printk("i2c-algo-bit.o:2 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA stuck low!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL unexpected low while SDA high!\n",name); + goto bailout; + } + scllo(adap); + printk("i2c-algo-bit.o:3 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL stuck high!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA unexpected low while pulling SCL low!\n", + name); + goto bailout; + } + sclhi(adap); + printk("i2c-algo-bit.o:4 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL stuck low!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA unexpected low while SCL high!\n", + name); + goto bailout; + } + printk("i2c-algo-bit.o: %s passed test.\n",name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + return -ENODEV; +} + +/* ----- Utility functions + */ + +/* try_address tries to contact a chip for a number of + * times before it gives up. + * return values: + * 1 chip answered + * 0 chip did not answer + * -x transmission error + */ +static inline int try_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + int i,ret = -1; + for (i=0;i<=retries;i++) { + ret = i2c_outb(i2c_adap,addr); + if (ret==1) + break; /* success! */ + i2c_stop(adap); + udelay(5/*adap->udelay*/); + if (i==retries) /* no success */ + break; + i2c_start(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk("i2c-algo-bit.o: needed %d retries for %d\n",i,addr)); + return ret; +} + +static int sendbytes(struct i2c_adapter *i2c_adap,const char *buf, int count) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + char c; + const char *temp = buf; + int retval; + int wrcount=0; + + while (count > 0) { + c = *temp; + DEB2(printk("i2c-algo-bit.o: %s i2c_write: writing %2.2X\n", + i2c_adap->name, c&0xff)); + retval = i2c_outb(i2c_adap,c); + if (retval>0) { + count--; + temp++; + wrcount++; + } else { /* arbitration or no acknowledge */ + printk("i2c-algo-bit.o: %s i2c_write: error - bailout.\n", + i2c_adap->name); + i2c_stop(adap); + return (retval<0)? retval : -EFAULT; /* got a better one ?? */ + } +#if 0 + /* from asm/delay.h */ + __delay(adap->mdelay * (loops_per_sec / 1000) ); +#endif + } + return wrcount; +} + +static inline int readbytes(struct i2c_adapter *i2c_adap,char *buf,int count) +{ + char *temp = buf; + int inval; + int rdcount=0; /* counts bytes read */ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + while (count > 0) { + inval = i2c_inb(i2c_adap); +/*printk("%#02x ",inval); if ( ! (count % 16) ) printk("\n"); */ + if (inval>=0) { + *temp = inval; + rdcount++; + } else { /* read timed out */ + printk("i2c-algo-bit.o: i2c_read: i2c_inb timed out.\n"); + break; + } + + if ( count > 1 ) { /* send ack */ + sdalo(adap); + DEBPROTO(printk(" Am ")); + } else { + sdahi(adap); /* neg. ack on last byte */ + DEBPROTO(printk(" NAm ")); + } + if (sclhi(adap)<0) { /* timeout */ + sdahi(adap); + printk("i2c-algo-bit.o: i2c_read: Timeout at ack\n"); + return -ETIMEDOUT; + }; + scllo(adap); + sdahi(adap); + temp++; + count--; + } + return rdcount; +} + +/* doAddress initiates the transfer by generating the start condition (in + * try_address) and transmits the address in the necessary format to handle + * reads, writes as well as 10bit-addresses. + * returns: + * 0 everything went okay, the chip ack'ed + * -x an error occured (like: -EREMOTEIO if the device did not answer, or + * -ETIMEDOUT, for example if the lines are stuck...) + */ +static inline int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg, + int retries) +{ + unsigned short flags = msg->flags; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + unsigned char addr; + int ret; + if ( (flags & I2C_M_TEN) ) { + /* a ten bit address */ + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk("addr0: %d\n",addr)); + /* try extended address code...*/ + ret = try_address(i2c_adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + /* the remaining 8 bit address */ + ret = i2c_outb(i2c_adap,msg->addr & 0x7f); + if (ret != 1) { + /* the chip did not ack / xmission error occured */ + printk("died at 2nd address code.\n"); + return -EREMOTEIO; + } + if ( flags & I2C_M_RD ) { + i2c_repstart(adap); + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_address(i2c_adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + } + } else { /* normal 7bit address */ + addr = ( msg->addr << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + ret = try_address(i2c_adap, addr, retries); + if (ret!=1) { + return -EREMOTEIO; + } + } + return 0; +} + +static int bit_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + int i,ret; + + i2c_start(adap); + for (i=0;iretries); + if (ret != 0) { + DEB2(printk("i2c-algo-bit.o: NAK from device adr %#2x msg #%d\n" + ,msgs[i].addr,i)); + return (ret<0) ? ret : -EREMOTEIO; + } + if (pmsg->flags & I2C_M_RD ) { + /* read bytes into buffer*/ + ret = readbytes(i2c_adap,pmsg->buf,pmsg->len); + DEB2(printk("i2c-algo-bit.o: read %d bytes.\n",ret)); + if (ret < pmsg->len ) { + return (ret<0)? ret : -EREMOTEIO; + } + } else { + /* write bytes from buffer */ + ret = sendbytes(i2c_adap,pmsg->buf,pmsg->len); + DEB2(printk("i2c-algo-bit.o: wrote %d bytes.\n",ret)); + if (ret < pmsg->len ) { + return (ret<0) ? ret : -EREMOTEIO; + } + } + if (ialgo_data; + + if (bit_test) { + int ret = test_bus(bit_adap, adap->name); + if (ret<0) + return -ENODEV; + } + + DEB2(printk("i2c-algo-bit.o: hw routines for %s registered.\n",adap->name)); + + /* register new adapter to i2c module... */ + + adap->id |= i2c_bit_algo.id; + adap->algo = &i2c_bit_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + + /* scan bus */ + if (bit_scan) { + int ack; + printk(KERN_INFO " i2c-algo-bit.o: scanning bus %s.\n", adap->name); + for (i = 0x00; i < 0xff; i+=2) { + i2c_start(bit_adap); + ack = i2c_outb(adap,i); + i2c_stop(bit_adap); + if (ack>0) { + printk("(%02x)",i>>1); + } else + printk("."); + } + printk("\n"); + } + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + i2c_add_adapter(adap); + + return 0; +} + + +int i2c_bit_del_bus(struct i2c_adapter *adap) +{ + + i2c_del_adapter(adap); + + DEB2(printk("i2c-algo-bit.o: adapter unregistered: %s\n",adap->name)); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +int __init i2c_algo_bit_init (void) +{ + printk("i2c-algo-bit.o: i2c bit algorithm module\n"); + return 0; +} + + + +EXPORT_SYMBOL(i2c_bit_add_bus); +EXPORT_SYMBOL(i2c_bit_del_bus); + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm"); + +MODULE_PARM(bit_test, "i"); +MODULE_PARM(bit_scan, "i"); +MODULE_PARM(i2c_debug,"i"); + +MODULE_PARM_DESC(bit_test, "Test the lines of the bus to see if it is stuck"); +MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus"); +MODULE_PARM_DESC(i2c_debug,"debug level - 0 off; 1 normal; 2,3 more verbose; 9 bit-protocol"); + +int init_module(void) +{ + return i2c_algo_bit_init(); +} + +void cleanup_module(void) +{ +} +#endif diff -u --recursive --new-file v2.3.33/linux/drivers/i2c/i2c-algo-pcf.c linux/drivers/i2c/i2c-algo-pcf.c --- v2.3.33/linux/drivers/i2c/i2c-algo-pcf.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/i2c/i2c-algo-pcf.c Thu Dec 16 13:59:38 1999 @@ -0,0 +1,607 @@ + +/* ------------------------------------------------------------------------- */ +/* i2c-algo-pcf.c i2c driver algorithms for PCF8584 adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020135 +#include +#else +#define __init +#endif + +#if LINUX_VERSION_CODE >= 0x020100 +# include +#else +# include +#endif + + +#include +#include +#include + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#include +#include "i2c-pcf8584.h" + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/ +#define DEBPROTO(x) if (i2c_debug>=9) x; + /* debug the protocol by showing transferred bits */ +#define DEF_TIMEOUT 16 + +/* debugging - slow down transfer to have a look at the data .. */ +/* I use this with two leds&resistors, each one connected to sda,scl */ +/* respectively. This makes sure that the algorithm works. Some chips */ +/* might not like this, as they have an internal timeout of some mils */ +/* +#if LINUX_VERSION_CODE >= 0x02016e +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#else +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#endif +*/ + + +/* ----- global variables --------------------------------------------- */ + +#ifdef SLO_IO + int jif; +#endif + +/* module parameters: + */ +static int i2c_debug=1; +static int pcf_test=0; /* see if the line-setting functions work */ +static int pcf_scan=0; /* have a look at what's hanging 'round */ + +/* --- setting states on the bus with the right timing: --------------- */ + +#define set_pcf(adap, ctl, val) adap->setpcf(adap->data, ctl, val) +#define get_pcf(adap, ctl) adap->getpcf(adap->data, ctl) +#define get_own(adap) adap->getown(adap->data) +#define get_clock(adap) adap->getclock(adap->data) +#define i2c_outb(adap, val) adap->setpcf(adap->data, 0, val) +#define i2c_inb(adap) adap->getpcf(adap->data, 0) + + +/* --- other auxiliary functions -------------------------------------- */ + +#if LINUX_VERSION_CODE < 0x02017f +static void schedule_timeout(int j) +{ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + j; + schedule(); +} +#endif + + +static void i2c_start(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk("S ")); + set_pcf(adap, 1, I2C_PCF_START); +} + +static void i2c_repstart(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk(" Sr ")); + set_pcf(adap, 1, I2C_PCF_REPSTART); +} + + +static void i2c_stop(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk("P\n")); + set_pcf(adap, 1, I2C_PCF_STOP); +} + + +static int wait_for_bb(struct i2c_algo_pcf_data *adap) { + + int timeout = DEF_TIMEOUT; + int status; + + status = get_pcf(adap, 1); + while (timeout-- && !(status & I2C_PCF_BB)) { + udelay(1000); /* How much is this? */ + status = get_pcf(adap, 1); + } + if (timeout<=0) + printk("Timeout waiting for Bus Busy\n"); + /* + set_pcf(adap, 1, I2C_PCF_STOP); + */ + return(timeout<=0); +} + + +static inline void pcf_sleep(unsigned long timeout) +{ + schedule_timeout( timeout * HZ); +} + + +static int wait_for_pin(struct i2c_algo_pcf_data *adap, int *status) { + + int timeout = DEF_TIMEOUT; + + *status = get_pcf(adap, 1); + while (timeout-- && (*status & I2C_PCF_PIN)) { + adap->waitforpin(); + *status = get_pcf(adap, 1); + } + if (timeout <= 0) + return(-1); + else + return(0); +} + + +/* + * This should perform the 'PCF8584 initialization sequence' as described + * in the Philips IC12 data book (1995, Aug 29). + * There should be a 30 clock cycle wait after reset, I assume this + * has been fulfilled. + * There should be a delay at the end equal to the longest I2C message + * to synchronize the BB-bit (in multimaster systems). How long is + * this? I assume 1 second is always long enough. + */ +static int pcf_init_8584 (struct i2c_algo_pcf_data *adap) +{ + + /* S1=0x80: S0 selected, serial interface off */ + set_pcf(adap, 1, I2C_PCF_PIN); + + /* load own address in S0, effective address is (own << 1) */ + i2c_outb(adap, get_own(adap)); + + /* S1=0xA0, next byte in S2 */ + set_pcf(adap, 1, I2C_PCF_PIN | I2C_PCF_ES1); + + /* load clock register S2 */ + i2c_outb(adap, get_clock(adap)); + + /* Enable serial interface, idle, S0 selected */ + set_pcf(adap, 1, I2C_PCF_IDLE); + + DEB2(printk("i2c-algo-pcf.o: irq: Initialized 8584.\n")); + return 0; +} + + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct i2c_algo_pcf_data *adap, char *name) { +#if 0 + int scl,sda; + sda=getsda(adap); + if (adap->getscl==NULL) { + printk("i2c-algo-pcf.o: Warning: Adapter can't read from clock line - skipping test.\n"); + return 0; + } + scl=getscl(adap); + printk("i2c-algo-pcf.o: Adapter: %s scl: %d sda: %d -- testing...\n", + name,getscl(adap),getsda(adap)); + if (!scl || !sda ) { + printk("i2c-algo-pcf.o: %s seems to be busy.\n",adap->name); + goto bailout; + } + sdalo(adap); + printk("i2c-algo-pcf.o:1 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA stuck high!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL unexpected low while pulling SDA low!\n", + name); + goto bailout; + } + sdahi(adap); + printk("i2c-algo-pcf.o:2 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA stuck low!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL unexpected low while SDA high!\n",adap->name); + goto bailout; + } + scllo(adap); + printk("i2c-algo-pcf.o:3 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL stuck high!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA unexpected low while pulling SCL low!\n", + name); + goto bailout; + } + sclhi(adap); + printk("i2c-algo-pcf.o:4 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL stuck low!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA unexpected low while SCL high!\n", + name); + goto bailout; + } + printk("i2c-algo-pcf.o: %s passed test.\n",name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + return -ENODEV; +#endif + return (0); +} + +/* ----- Utility functions + */ + +static inline int try_address(struct i2c_algo_pcf_data *adap, + unsigned char addr, int retries) +{ + int i, status, ret = -1; + for (i=0;i= 0) { + if ((status && I2C_PCF_LRB) == 0) { + i2c_stop(adap); + break; /* success! */ + } + } + i2c_stop(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk("i2c-algo-pcf.o: needed %d retries for %d\n",i,addr)); + return ret; +} + + +static int pcf_sendbytes(struct i2c_adapter *i2c_adap,const char *buf, int count) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + int wrcount, status, timeout; + + for (wrcount=0; wrcountname, buf[wrcount]&0xff)); + i2c_outb(adap, buf[wrcount]); + timeout = wait_for_pin(adap, &status); + if (timeout) { + printk("i2c-algo-pcf.o: %s i2c_write: error - timeout.\n", + i2c_adap->name); + i2c_stop(adap); + return -EREMOTEIO; /* got a better one ?? */ + } + if (status & I2C_PCF_LRB) { + printk("i2c-algo-pcf.o: %s i2c_write: error - no ack.\n", + i2c_adap->name); + i2c_stop(adap); + return -EREMOTEIO; /* got a better one ?? */ + } + } + return (wrcount); +} + + +static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count) +{ + int rdcount=0, i, status, timeout, dummy=1; + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + + for (i=0; iflags; + unsigned char addr; + int ret; + if ( (flags & I2C_M_TEN) ) { + /* a ten bit address */ + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk("addr0: %d\n",addr)); + /* try extended address code...*/ + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + /* the remaining 8 bit address */ + i2c_outb(adap,msg->addr & 0x7f); +/* Status check comes here */ + if (ret != 1) { + printk("died at 2nd address code.\n"); + return -EREMOTEIO; + } + if ( flags & I2C_M_RD ) { + i2c_repstart(adap); + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + } + } else { /* normal 7bit address */ + addr = ( msg->addr << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + i2c_outb(adap, addr); + } + return 0; +} + +static int pcf_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], + int num) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + struct i2c_msg *pmsg; + int i, ret, timeout, status; + + timeout = wait_for_bb(adap); + if (timeout) { + DEB2(printk("i2c-algo-pcf.o: Timeout waiting for BB in pcf_xfer\n");) + return -EIO; + } + pmsg = &msgs[0]; + ret = pcf_doAddress(adap, pmsg, i2c_adap->retries); + i2c_start(adap); + + for (i=0; iflags & I2C_M_RD ) { + /* read bytes into buffer*/ + ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len); + DEB2(printk("i2c-algo-pcf.o: read %d bytes.\n",ret)); + } else { + /* write bytes from buffer */ + ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len); + DEB2(printk("i2c-algo-pcf.o: wrote %d bytes.\n",ret)); + } + if (i == (num-1)) { + i2c_stop(adap); + } + else { + i2c_repstart(adap); + } + if (pmsg->flags & I2C_M_RD ) { + pmsg->buf[pmsg->len-1] = i2c_inb(adap); + } + if (i != (num-1)) { + pmsg = &msgs[0]; + ret = pcf_doAddress(adap, pmsg, i2c_adap->retries); + timeout = wait_for_pin(adap, &status); + if (timeout) { + DEB2(printk("i2c-algo-pcf.o: Timeout waiting for PIN(2) in pcf_xfer\n");) + return (-EREMOTEIO); + } + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + DEB2(printk("i2c-algo-pcf.o: No LRB(2) in pcf_xfer\n");) + return (-EREMOTEIO); + } + } + } + return (num); +} + + +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static u32 pcf_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; +} + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm pcf_algo = { + "PCF8584 algorithm", + I2C_ALGO_PCF, + pcf_xfer, + NULL, + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + pcf_func, /* functionality */ +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pcf_add_bus(struct i2c_adapter *adap) +{ + int i, status; + struct i2c_algo_pcf_data *pcf_adap = adap->algo_data; + + if (pcf_test) { + int ret = test_bus(pcf_adap, adap->name); + if (ret<0) + return -ENODEV; + } + + DEB2(printk("i2c-algo-pcf.o: hw routines for %s registered.\n",adap->name)); + + /* register new adapter to i2c module... */ + + adap->id |= pcf_algo.id; + adap->algo = &pcf_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + i2c_add_adapter(adap); + pcf_init_8584(pcf_adap); + + /* scan bus */ + if (pcf_scan) { + printk(KERN_INFO " i2c-algo-pcf.o: scanning bus %s.\n", adap->name); + for (i = 0x00; i < 0xff; i+=2) { + i2c_outb(pcf_adap, i); + i2c_start(pcf_adap); + if ((wait_for_pin(pcf_adap, &status) >= 0) && + ((status && I2C_PCF_LRB) == 0)) { + printk("(%02x)",i>>1); + } else { + printk("."); + } + i2c_stop(pcf_adap); + udelay(pcf_adap->udelay); + } + printk("\n"); + } + return 0; +} + + +int i2c_pcf_del_bus(struct i2c_adapter *adap) +{ + i2c_del_adapter(adap); + DEB2(printk("i2c-algo-pcf.o: adapter unregistered: %s\n",adap->name)); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +int __init i2c_algo_pcf_init (void) +{ + printk("i2c-algo-pcf.o: i2c pcf8584 algorithm module\n"); + return 0; +} + + +EXPORT_SYMBOL(i2c_pcf_add_bus); +EXPORT_SYMBOL(i2c_pcf_del_bus); + +#ifdef MODULE +MODULE_AUTHOR("Hans Berglund "); +MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm"); + +MODULE_PARM(pcf_test, "i"); +MODULE_PARM(pcf_scan, "i"); +MODULE_PARM(i2c_debug,"i"); + +MODULE_PARM_DESC(pcf_test, "Test if the I2C bus is available"); +MODULE_PARM_DESC(pcf_scan, "Scan for active chips on the bus"); +MODULE_PARM_DESC(i2c_debug,"debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol"); + + +int init_module(void) +{ + return i2c_algo_pcf_init(); +} + +void cleanup_module(void) +{ +} +#endif diff -u --recursive --new-file v2.3.33/linux/drivers/i2c/i2c-core.c linux/drivers/i2c/i2c-core.c --- v2.3.33/linux/drivers/i2c/i2c-core.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/i2c/i2c-core.c Thu Dec 16 13:59:38 1999 @@ -0,0 +1,1353 @@ +/* i2c-core.c - a device driver for the iic-bus interface */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ +#define RCSID "$Id: i2c-core.c,v 1.42 1999/11/30 20:06:42 frodo Exp $" +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki . + All SMBus-related things are written by Frodo Looijaard */ + +#include +#include +#include +#include +#include +#include + +#include + +/* ----- compatibility stuff ----------------------------------------------- */ + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c)) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,53) +#include +#else +#define __init +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4)) +#define copy_from_user memcpy_fromfs +#define copy_to_user memcpy_tofs +#else +#include +#endif + +/* ----- global defines ---------------------------------------------------- */ + +/* exclusive access to the bus */ +#define I2C_LOCK(adap) down(&adap->lock) +#define I2C_UNLOCK(adap) up(&adap->lock) + +#define ADAP_LOCK() down(&adap_lock) +#define ADAP_UNLOCK() up(&adap_lock) + +#define DRV_LOCK() down(&driver_lock) +#define DRV_UNLOCK() up(&driver_lock) + +#define DEB(x) if (i2c_debug>=1) x; +#define DEB2(x) if (i2c_debug>=2) x; + +/* ----- global variables -------------------------------------------------- */ + +/**** lock for writing to global variables: the adapter & driver list */ +struct semaphore adap_lock; +struct semaphore driver_lock; + +/**** adapter list */ +static struct i2c_adapter *adapters[I2C_ADAP_MAX]; +static int adap_count; + +/**** drivers list */ +static struct i2c_driver *drivers[I2C_DRIVER_MAX]; +static int driver_count; + +/**** debug level */ +static int i2c_debug=1; +static void i2c_dummy_adapter(struct i2c_adapter *adapter); +static void i2c_dummy_client(struct i2c_client *client); + +/* --------------------------------------------------- + * /proc entry declarations + *---------------------------------------------------- + */ + +/* Note that quite some things changed within the 2.1 kernel series. + Some things below are somewhat difficult to read because of this. */ + +#ifdef CONFIG_PROC_FS + +static int i2cproc_init(void); +static int i2cproc_cleanup(void); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) && \ + (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) +static void monitor_bus_i2c(struct inode *inode, int fill); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + +static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, + loff_t *ppos); +static int read_bus_i2c(char *buf, char **start, off_t offset, int len, + int *eof , void *private); + +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + +static int i2cproc_bus_read(struct inode * inode, struct file * file, + char * buf, int count); +static int read_bus_i2c(char *buf, char **start, off_t offset, int len, + int unused); + +static struct proc_dir_entry proc_bus_dir = + { + /* low_ino */ 0, /* Set by proc_register_dynamic */ + /* namelen */ 3, + /* name */ "bus", + /* mode */ S_IRUGO | S_IXUGO | S_IFDIR, + /* nlink */ 2, /* Corrected by proc_register[_dynamic] */ + /* uid */ 0, + /* gid */ 0, + /* size */ 0, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,0,36)) + /* ops */ &proc_dir_inode_operations, +#endif + }; + +static struct proc_dir_entry proc_bus_i2c_dir = + { + /* low_ino */ 0, /* Set by proc_register_dynamic */ + /* namelen */ 3, + /* name */ "i2c", + /* mode */ S_IRUGO | S_IFREG, + /* nlink */ 1, + /* uid */ 0, + /* gid */ 0, + /* size */ 0, + /* ops */ NULL, + /* get_info */ &read_bus_i2c + }; + +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + +/* To implement the dynamic /proc/bus/i2c-? files, we need our own + implementation of the read hook */ +static struct file_operations i2cproc_operations = { + NULL, + i2cproc_bus_read, +}; + +static struct inode_operations i2cproc_inode_operations = { + &i2cproc_operations +}; + +static int i2cproc_initialized = 0; + +#else /* undef CONFIG_PROC_FS */ + +#define i2cproc_init() +#define i2cproc_cleanup() + +#endif /* CONFIG_PROC_FS */ + + +/* --------------------------------------------------- + * registering functions + * --------------------------------------------------- + */ + +/* ----- + * i2c_add_adapter is called from within the algorithm layer, + * when a new hw adapter registers. A new device is register to be + * available for clients. + */ +int i2c_add_adapter(struct i2c_adapter *adap) +{ + int i,j; + + ADAP_LOCK(); + for (i = 0; i < I2C_ADAP_MAX; i++) + if (NULL == adapters[i]) + break; + if (I2C_ADAP_MAX == i) { + printk(KERN_WARNING + " i2c-core.o: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n", + adap->name); + ADAP_UNLOCK(); + return -ENOMEM; + } + + adapters[i] = adap; + adap_count++; + ADAP_UNLOCK(); + + /* init data types */ + init_MUTEX(&adap->lock); + + i2c_dummy_adapter(adap); /* actually i2c_dummy->add_adapter */ +#ifdef CONFIG_PROC_FS + + if (i2cproc_initialized) { + char name[8]; + struct proc_dir_entry *proc_entry; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) + int res; +#endif + + sprintf(name,"i2c-%d", i); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + proc_entry = create_proc_entry(name,0,proc_bus); + if (! proc_entry) { + printk("i2c-core.o: Could not create /proc/bus/%s\n", + name); + return -ENOENT; + } + proc_entry->ops = &i2cproc_inode_operations; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) + proc_entry->owner = THIS_MODULE; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) + proc_entry->fill_inode = &monitor_bus_i2c; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + adap->proc_entry = NULL; + if (!(proc_entry = kmalloc(sizeof(struct proc_dir_entry)+ + strlen(name)+1, GFP_KERNEL))) { + printk("i2c-core.o: Out of memory!\n"); + return -ENOMEM; + } + memset(proc_entry,0,sizeof(struct proc_dir_entry)); + proc_entry->namelen = strlen(name); + proc_entry->name = (char *) (proc_entry + 1); + proc_entry->mode = S_IRUGO | S_IFREG; + proc_entry->nlink = 1; + proc_entry->ops = &i2cproc_inode_operations; + + /* Nasty stuff to keep GCC satisfied */ + { + char *procname; + (const char *) procname = proc_entry->name; + strcpy (procname,name); + } + + if ((res = proc_register_dynamic(&proc_bus_dir, proc_entry))) { + printk("i2c-core.o: Could not create %s.\n",name); + kfree(proc_entry); + return res; + } + + adap->proc_entry = proc_entry; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + + adap->inode = proc_entry->low_ino; + } + +#endif /* def CONFIG_PROC_FS */ + + /* inform drivers of new adapters */ + DRV_LOCK(); + for (j=0;jflags&I2C_DF_NOTIFY) + drivers[j]->attach_adapter(adap); + DRV_UNLOCK(); + + DEB(printk("i2c-core.o: adapter %s registered as adapter %d.\n",adap->name,i)); + + return 0; +} + + +int i2c_del_adapter(struct i2c_adapter *adap) +{ + int i,j; + ADAP_LOCK(); + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adap == adapters[i]) + break; + if (I2C_ADAP_MAX == i) { + printk( " i2c-core.o: unregister_adapter adap [%s] not found.\n", + adap->name); + ADAP_UNLOCK(); + return -ENODEV; + } + + i2c_dummy_adapter(adap); /* actually i2c_dummy->del_adapter */ +#ifdef CONFIG_PROC_FS + if (i2cproc_initialized) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + char name[8]; + sprintf(name,"i2c-%d", i); + remove_proc_entry(name,proc_bus); +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + int res; + if (adapters[i]->proc_entry) { + if ((res = proc_unregister(&proc_bus_dir, + adapters[i]->proc_entry->low_ino))) { + printk("i2c-core.o: Deregistration of /proc " + "entry failed\n"); + ADAP_UNLOCK(); + return res; + } + kfree(adapters[i]->proc_entry); + } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + } +#endif /* def CONFIG_PROC_FS */ + + /* detach any active clients */ + for (j=0;jclients[j]; + if ( (client!=NULL) + /* && (client->driver->flags & I2C_DF_NOTIFY) */ ) + /* detaching devices is unconditional of the set notify + * flag, as _all_ clients that reside on the adapter + * must be deleted, as this would cause invalid states. + */ + client->driver->detach_client(client); + /* i2c_detach_client(client); --- frodo */ + } + /* all done, now unregister */ + adapters[i] = NULL; + adap_count--; + + ADAP_UNLOCK(); + DEB(printk("i2c-core.o: adapter unregistered: %s\n",adap->name)); + return 0; +} + + +/* ----- + * What follows is the "upwards" interface: commands for talking to clients, + * which implement the functions to access the physical information of the + * chips. + */ + +int i2c_add_driver(struct i2c_driver *driver) +{ + int i,j; + DRV_LOCK(); + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (NULL == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) { + printk(KERN_WARNING + " i2c-core.o: register_driver(%s) - enlarge I2C_DRIVER_MAX.\n", + driver->name); + DRV_UNLOCK(); + return -ENOMEM; + } + + drivers[i] = driver; + driver_count++; + + DRV_UNLOCK(); /* driver was successfully added */ + + DEB(printk("i2c-core.o: driver %s registered.\n",driver->name)); + + /* Notify all existing adapters and clients to dummy driver */ + ADAP_LOCK(); + if (driver->flags&I2C_DF_DUMMY) { + for (i=0; iattach_adapter(adapters[i]); + for (j=0; jclients[j]) + driver->detach_client(adapters[i]->clients[j]); + } + } + ADAP_UNLOCK(); + return 0; + } + + /* now look for instances of driver on our adapters + */ + if ( driver->flags&I2C_DF_NOTIFY ) { + for (i=0;iattach_adapter(adapters[i]); + } + ADAP_UNLOCK(); + return 0; +} + +int i2c_del_driver(struct i2c_driver *driver) +{ + int i,j,k; + + DRV_LOCK(); + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (driver == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) { + printk(KERN_WARNING " i2c-core.o: unregister_driver: [%s] not found\n", + driver->name); + DRV_UNLOCK(); + return -ENODEV; + } + /* Have a look at each adapter, if clients of this driver are still + * attached. If so, detach them to be able to kill the driver + * afterwards. + */ + DEB2(printk("i2c-core.o: unregister_driver - looking for clients.\n")); + /* removing clients does not depend on the notify flag, else + * invalid operation might (will!) result, when using stale client + * pointers. + */ + ADAP_LOCK(); /* should be moved inside the if statement... */ + if ((driver->flags&I2C_DF_DUMMY)==0) + for (k=0;kname)); + for (j=0;jclients[j]; + if (client != NULL && client->driver == driver) { + DEB2(printk("i2c-core.o: detaching client %s:\n", + client->name)); + /*i2c_detach_client(client);*/ + driver->detach_client(client); + } + } + } + ADAP_UNLOCK(); + drivers[i] = NULL; + driver_count--; + DRV_UNLOCK(); + + DEB(printk("i2c-core.o: driver unregistered: %s\n",driver->name)); + return 0; +} + + +int i2c_attach_client(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + int i; + + for (i = 0; i < I2C_CLIENT_MAX; i++) + if (NULL == adapter->clients[i]) + break; + if (I2C_CLIENT_MAX == i) { + printk(KERN_WARNING + " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", + client->name); + return -ENOMEM; + } + + adapter->clients[i] = client; + adapter->client_count++; + i2c_dummy_client(client); + + if (adapter->client_register != NULL) + adapter->client_register(client); + DEB(printk("i2c-core.o: client [%s] registered to adapter [%s](pos. %d).\n", + client->name, adapter->name,i)); + return 0; +} + + +int i2c_detach_client(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + int i; + + for (i = 0; i < I2C_CLIENT_MAX; i++) + if (client == adapter->clients[i]) + break; + if (I2C_CLIENT_MAX == i) { + printk(KERN_WARNING " i2c-core.o: unregister_client [%s] not found\n", + client->name); + return -ENODEV; + } + + if (adapter->client_unregister != NULL) + adapter->client_unregister(client); + /* client->driver->detach_client(client);*/ + + adapter->clients[i] = NULL; + adapter->client_count--; + i2c_dummy_client(client); + + DEB(printk("i2c-core.o: client [%s] unregistered.\n",client->name)); + return 0; +} + +void i2c_inc_use_client(struct i2c_client *client) +{ + + if (client->driver->inc_use != NULL) + client->driver->inc_use(client); + + if (client->adapter->inc_use != NULL) + client->adapter->inc_use(client->adapter); +} + +void i2c_dec_use_client(struct i2c_client *client) +{ + + if (client->driver->dec_use != NULL) + client->driver->dec_use(client); + + if (client->adapter->dec_use != NULL) + client->adapter->dec_use(client->adapter); +} + +/* ---------------------------------------------------- + * The /proc functions + * ---------------------------------------------------- + */ + +#ifdef CONFIG_PROC_FS + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) && \ + (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) +/* Monitor access to /proc/bus/i2c*; make unloading i2c-proc impossible + if some process still uses it or some file in it */ +void monitor_bus_i2c(struct inode *inode, int fill) +{ + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ + +/* This function generates the output for /proc/bus/i2c */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) +int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, + void *private) +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ +int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused) +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ +{ + int i; + int nr = 0; + /* Note that it is safe to write a `little' beyond len. Yes, really. */ + for (i = 0; (i < I2C_ADAP_MAX) && (nr < len); i++) + if (adapters[i]) { + nr += sprintf(buf+nr, "i2c-%d\t", i); + if (adapters[i]->algo->smbus_xfer) { + if (adapters[i]->algo->master_xfer) + nr += sprintf(buf+nr,"smbus/i2c"); + else + nr += sprintf(buf+nr,"smbus "); + } else if (adapters[i]->algo->master_xfer) + nr += sprintf(buf+nr,"i2c "); + else + nr += sprintf(buf+nr,"dummy "); + nr += sprintf(buf+nr,"\t%-32s\t%-32s\n", + adapters[i]->name, + adapters[i]->algo->name); + } + return nr; +} + +/* This function generates the output for /proc/bus/i2c-? */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) +ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, + loff_t *ppos) +{ + struct inode * inode = file->f_dentry->d_inode; +#else (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) +int i2cproc_bus_read(struct inode * inode, struct file * file,char * buf, + int count) +{ +#endif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + char *kbuf; + struct i2c_client *client; + int i,j,len=0; + + if (count < 0) + return -EINVAL; + if (count > 4000) + count = 4000; + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adapters[i]->inode == inode->i_ino) { + /* We need a bit of slack in the kernel buffer; this makes the + sprintf safe. */ + if (! (kbuf = kmalloc(count + 80,GFP_KERNEL))) + return -ENOMEM; + for (j = 0; j < I2C_CLIENT_MAX; j++) + if ((client = adapters[i]->clients[j])) + /* Filter out dummy clients */ + if (client->driver->id != I2C_DRIVERID_I2CDEV) + len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n", + client->addr, + client->name,client->driver->name); + if (file->f_pos+len > count) + len = count - file->f_pos; + len = len - file->f_pos; + if (len < 0) + len = 0; + copy_to_user (buf,kbuf+file->f_pos,len); + file->f_pos += len; + kfree(kbuf); + return len; + } + return -ENOENT; +} + +int i2cproc_init(void) +{ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + struct proc_dir_entry *proc_bus_i2c; +#else + int res; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + + i2cproc_initialized = 0; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + if (! proc_bus) { + printk("i2c-core.o: /proc/bus/ does not exist"); + i2cproc_cleanup(); + return -ENOENT; + } + proc_bus_i2c = create_proc_entry("i2c",0,proc_bus); + if (!proc_bus_i2c) { + printk("i2c-core.o: Could not create /proc/bus/i2c"); + i2cproc_cleanup(); + return -ENOENT; + } + proc_bus_i2c->read_proc = &read_bus_i2c; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) + proc_bus_i2c->owner = THIS_MODULE; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) + proc_bus_i2c->fill_inode = &monitor_bus_i2c; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + i2cproc_initialized += 2; +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + /* In Linux 2.0.x, there is no /proc/bus! But I hope no other module + introduced it, or we are fucked. And 2.0.35 and earlier does not + export proc_dir_inode_operations, so we grab it from proc_net, + which also uses it. Not nice. */ + proc_bus_dir.ops = proc_net.ops; + if ((res = proc_register_dynamic(&proc_root, &proc_bus_dir))) { + printk("i2c-core.o: Could not create /proc/bus/"); + i2cproc_cleanup(); + return res; + } + i2cproc_initialized ++; + if ((res = proc_register_dynamic(&proc_bus_dir, &proc_bus_i2c_dir))) { + printk("i2c-core.o: Could not create /proc/bus/i2c\n"); + i2cproc_cleanup(); + return res; + } + i2cproc_initialized ++; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + return 0; +} + +int i2cproc_cleanup(void) +{ + + if (i2cproc_initialized >= 1) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + remove_proc_entry("i2c",proc_bus); + i2cproc_initialized -= 2; +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + int res; + if (i2cproc_initialized >= 2) { + if ((res = proc_unregister(&proc_bus_dir, + proc_bus_i2c_dir.low_ino))) { + printk("i2c-core.o: could not delete " + "/proc/bus/i2c, module not removed."); + return res; + } + i2cproc_initialized --; + } + if ((res = proc_unregister(&proc_root,proc_bus_dir.low_ino))) { + printk("i2c-core.o: could not delete /proc/bus/, " + "module not removed."); + return res; + } + i2cproc_initialized --; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + } + return 0; +} + + +#endif /* def CONFIG_PROC_FS */ + +/* --------------------------------------------------- + * dummy driver notification + * --------------------------------------------------- + */ + +static void i2c_dummy_adapter(struct i2c_adapter *adap) +{ + int i; + for (i=0; iflags & I2C_DF_DUMMY)) + drivers[i]->attach_adapter(adap); +} + +static void i2c_dummy_client(struct i2c_client *client) +{ + int i; + for (i=0; iflags & I2C_DF_DUMMY)) + drivers[i]->detach_client(client); +} + + +/* ---------------------------------------------------- + * the functional interface to the i2c busses. + * ---------------------------------------------------- + */ + +int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num) +{ + int ret; + + if (adap->algo->master_xfer) { + DEB2(printk("i2c-core.o: master_xfer: %s with %d msgs.\n",adap->name,num)); + + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,msgs,num); + I2C_UNLOCK(adap); + + return ret; + } else { + printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", + adap->id); + return -ENOSYS; + } +} + +int i2c_master_send(struct i2c_client *client,const char *buf ,int count) +{ + int ret; + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + + if (client->adapter->algo->master_xfer) { + msg.addr = client->addr; + msg.flags = client->flags & I2C_M_TEN; + msg.len = count; + (const char *)msg.buf = buf; + + DEB2(printk("i2c-core.o: master_send: writing %d bytes on %s.\n", + count,client->adapter->name)); + + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,&msg,1); + I2C_UNLOCK(adap); + + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. + */ + return (ret == 1 )? count : ret; + } else { + printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", + client->adapter->id); + return -ENOSYS; + } +} + +int i2c_master_recv(struct i2c_client *client, char *buf ,int count) +{ + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + int ret; + if (client->adapter->algo->master_xfer) { + msg.addr = client->addr; + msg.flags = client->flags & I2C_M_TEN; + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = buf; + + DEB2(printk("i2c-core.o: master_recv: reading %d bytes on %s.\n", + count,client->adapter->name)); + + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,&msg,1); + I2C_UNLOCK(adap); + + DEB2(printk("i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n", + ret, count, client->addr)); + + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. + */ + return (ret == 1 )? count : ret; + } else { + printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", + client->adapter->id); + return -ENOSYS; + } +} + + +int i2c_control(struct i2c_client *client, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct i2c_adapter *adap = client->adapter; + + DEB2(printk("i2c-core.o: i2c ioctl, cmd: 0x%x, arg: %#lx\n", cmd, arg)); + switch ( cmd ) { + case I2C_RETRIES: + adap->retries = arg; + break; + case I2C_TIMEOUT: + adap->timeout = arg; + break; + default: + if (adap->algo->algo_control!=NULL) + ret = adap->algo->algo_control(adap,cmd,arg); + } + return ret; +} + +/* ---------------------------------------------------- + * the i2c address scanning function + * Will not work for 10-bit addresses! + * ---------------------------------------------------- + */ +int i2c_probe(struct i2c_adapter *adapter, + struct i2c_client_address_data *address_data, + i2c_client_found_addr_proc *found_proc) +{ + int addr,i,found,err; + int adap_id = i2c_adapter_id(adapter); + + /* Forget it if we can't probe using SMBUS_QUICK */ + if (! i2c_check_functionality(adapter,I2C_FUNC_SMBUS_QUICK)) + return -1; + + for (addr = 0x00; + addr <= 0x7f; + addr++) { + + /* If it is in one of the force entries, we don't do any detection + at all */ + found = 0; + + for (i = 0; + !found && (address_data->force[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->force[i]) || + (address_data->force[i] == ANY_I2C_BUS)) && + (addr == address_data->force[i+1])) { + DEB2(printk("i2c-core.o: found force parameter for adapter %d, addr %04x\n", + adap_id,addr)); + if ((err = found_proc(adapter,addr,0,0))) + return err; + found = 1; + } + } + if (found) + continue; + + /* If this address is in one of the ignores, we can forget about it + right now */ + for (i = 0; + !found && (address_data->ignore[i] != I2C_CLIENT_END); + i += 2) { + if (((adap_id == address_data->ignore[i]) || + ((address_data->ignore[i] == ANY_I2C_BUS))) && + (addr == address_data->ignore[i+1])) { + DEB2(printk("i2c-core.o: found ignore parameter for adapter %d, " + "addr %04x\n", adap_id ,addr)); + found = 1; + } + } + for (i = 0; + !found && (address_data->ignore_range[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->ignore_range[i]) || + ((address_data->ignore_range[i]==ANY_I2C_BUS))) && + (addr >= address_data->ignore_range[i+1]) && + (addr <= address_data->ignore_range[i+2])) { + DEB2(printk("i2c-core.o: found ignore_range parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + found = 1; + } + } + if (found) + continue; + + /* Now, we will do a detection, but only if it is in the normal or + probe entries */ + for (i = 0; + !found && (address_data->normal_i2c[i] != I2C_CLIENT_END); + i += 1) { + if (addr == address_data->normal_i2c[i]) { + found = 1; + DEB2(printk("i2c-core.o: found normal i2c entry for adapter %d, " + "addr %02x", adap_id,addr)); + } + } + + for (i = 0; + !found && (address_data->normal_i2c_range[i] != I2C_CLIENT_END); + i += 2) { + if ((addr >= address_data->normal_i2c_range[i]) && + (addr <= address_data->normal_i2c_range[i+1])) { + found = 1; + DEB2(printk("i2c-core.o: found normal i2c_range entry for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + + for (i = 0; + !found && (address_data->probe[i] != I2C_CLIENT_END); + i += 2) { + if (((adap_id == address_data->probe[i]) || + ((address_data->probe[i] == ANY_I2C_BUS))) && + (addr == address_data->probe[i+1])) { + found = 1; + DEB2(printk("i2c-core.o: found probe parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + for (i = 0; + !found && (address_data->probe_range[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->probe_range[i]) || + (address_data->probe_range[i] == ANY_I2C_BUS)) && + (addr >= address_data->probe_range[i+1]) && + (addr <= address_data->probe_range[i+2])) { + found = 1; + DEB2(printk("i2c-core.o: found probe_range parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + if (!found) + continue; + + /* OK, so we really should examine this address. First check + whether there is some client here at all! */ + if (i2c_smbus_xfer(adapter,addr,0,0,0,I2C_SMBUS_QUICK,NULL) >= 0) + if ((err = found_proc(adapter,addr,0,-1))) + return err; + } + return 0; +} + +/* +++ frodo + * return id number for a specific adapter + */ +int i2c_adapter_id(struct i2c_adapter *adap) +{ + int i; + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adap == adapters[i]) + return i; + return -1; +} + +/* The SMBus parts */ + +extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value) +{ + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + value,0,I2C_SMBUS_QUICK,NULL); +} + +extern s32 i2c_smbus_read_byte(struct i2c_client * client) +{ + union i2c_smbus_data data; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,0,I2C_SMBUS_BYTE, &data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value) +{ + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,NULL); +} + +extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,command, I2C_SMBUS_BYTE_DATA,&data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, + u8 command, u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_BYTE_DATA,&data); +} + +extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,command, I2C_SMBUS_WORD_DATA, &data)) + return -1; + else + return 0x0FFFF & data.word; +} + +extern s32 i2c_smbus_write_word_data(struct i2c_client * client, + u8 command, u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_WORD_DATA,&data); +} + +extern s32 i2c_smbus_process_call(struct i2c_client * client, + u8 command, u16 value) +{ + union i2c_smbus_data data; + data.word = value; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_PROC_CALL, &data)) + return -1; + else + return 0x0FFFF & data.word; +} + +/* Returns the number of read bytes */ +extern s32 i2c_smbus_read_block_data(struct i2c_client * client, + u8 command, u8 *values) +{ + union i2c_smbus_data data; + int i; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,command, + I2C_SMBUS_BLOCK_DATA,&data)) + return -1; + else { + for (i = 1; i <= data.block[0]; i++) + values[i-1] = data.block[i]; + return data.block[0]; + } +} + +extern s32 i2c_smbus_write_block_data(struct i2c_client * client, + u8 command, u8 length, u8 *values) +{ + union i2c_smbus_data data; + int i; + if (length > 32) + length = 32; + for (i = 1; i <= length; i++) + data.block[i] = values[i-1]; + data.block[0] = length; + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_BLOCK_DATA,&data); +} + +/* Simulate a SMBus command using the i2c protocol + No checking of parameters is done! */ +static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + /* So we need to generate a series of msgs. In the case of writing, we + need to use only one message; when reading, we need two. We initialize + most things with sane defaults, to keep the code below somewhat + simpler. */ + unsigned char msgbuf0[33]; + unsigned char msgbuf1[33]; + int num = read_write == I2C_SMBUS_READ?2:1; + struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, + { addr, flags | I2C_M_RD, 0, msgbuf1 } + }; + int i; + + msgbuf0[0] = command; + switch(size) { + case I2C_SMBUS_QUICK: + msg[0].len = 0; + /* Special case: The read/write field is used as data */ + msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0; + num = 1; + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + /* Special case: only a read! */ + msg[0].flags = I2C_M_RD | flags; + num = 1; + } + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 1; + else { + msg[0].len = 2; + msgbuf0[1] = data->byte; + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 2; + else { + msg[0].len=3; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = (data->word >> 8) & 0xff; + } + break; + case I2C_SMBUS_PROC_CALL: + num = 2; /* Special case */ + msg[0].len = 3; + msg[1].len = 2; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = (data->word >> 8) & 0xff; + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + printk("i2c-core.o: Block read not supported under " + "I2C emulation!\n"); + return -1; + } else { + msg[1].len = data->block[0] + 1; + if (msg[1].len > 32) { + printk("i2c-core.o: smbus_access called with " + "invalid block write size (%d)\n", + msg[1].len); + return -1; + } + for (i = 1; i <= msg[1].len; i++) + msgbuf0[i] = data->block[i]; + } + break; + default: + printk("i2c-core.o: smbus_access called with invalid size (%d)\n", + size); + return -1; + } + + if (i2c_transfer(adapter, msg, num) < 0) + return -1; + + if (read_write == I2C_SMBUS_READ) + switch(size) { + case I2C_SMBUS_BYTE: + data->byte = msgbuf0[0]; + break; + case I2C_SMBUS_BYTE_DATA: + data->byte = msgbuf1[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = msgbuf1[0] | (msgbuf1[1] << 8); + break; + } + return 0; +} + + +s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + s32 res; + flags = flags & I2C_M_TEN; + if (adapter->algo->smbus_xfer) { + I2C_LOCK(adapter); + res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write, + command,size,data); + I2C_UNLOCK(adapter); + } else + res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, + command,size,data); + return res; +} + + +/* You should always define `functionality'; the 'else' is just for + backward compatibility. */ +u32 i2c_get_functionality (struct i2c_adapter *adap) +{ + if (adap->algo->functionality) + return adap->algo->functionality(adap); + else + return 0xffffffff; +} + +int i2c_check_functionality (struct i2c_adapter *adap, u32 func) +{ + u32 adap_func = i2c_get_functionality (adap); + return (func & adap_func) == func; +} + + +static int __init i2c_init(void) +{ + printk("i2c-core.o: i2c core module\n"); + memset(adapters,0,sizeof(adapters)); + memset(drivers,0,sizeof(drivers)); + adap_count=0; + driver_count=0; + + init_MUTEX(&adap_lock); + init_MUTEX(&driver_lock); + + i2cproc_init(); + + return 0; +} + +#ifndef MODULE +#ifdef CONFIG_I2C_CHARDEV + extern int i2c_dev_init(void); +#endif +#ifdef CONFIG_I2C_ALGOBIT + extern int algo_bit_init(void); +#endif +#ifdef CONFIG_I2C_BITLP + extern int bitlp_init(void); +#endif +#ifdef CONFIG_I2C_BITELV + extern int bitelv_init(void); +#endif +#ifdef CONFIG_I2C_BITVELLE + extern int bitvelle_init(void); +#endif +#ifdef CONFIG_I2C_BITVIA + extern int bitvia_init(void); +#endif + +#ifdef CONFIG_I2C_ALGOPCF + extern int algo_pcf_init(void); +#endif +#ifdef CONFIG_I2C_PCFISA + extern int pcfisa_init(void); +#endif + +/* This is needed for automatic patch generation: sensors code starts here */ +/* This is needed for automatic patch generation: sensors code ends here */ + +int __init i2c_init_all(void) +{ + /* --------------------- global ----- */ + i2c_init(); + +#ifdef CONFIG_I2C_CHARDEV + i2c_dev_init(); +#endif + /* --------------------- bit -------- */ +#ifdef CONFIG_I2C_ALGOBIT + i2c_algo_bit_init(); +#endif +#ifdef CONFIG_I2C_PHILIPSPAR + i2c_bitlp_init(); +#endif +#ifdef CONFIG_I2C_ELV + i2c_bitelv_init(); +#endif +#ifdef CONFIG_I2C_VELLEMAN + i2c_bitvelle_init(); +#endif + + /* --------------------- pcf -------- */ +#ifdef CONFIG_I2C_ALGOPCF + i2c_algo_pcf_init(); +#endif +#ifdef CONFIG_I2C_ELEKTOR + i2c_pcfisa_init(); +#endif +/* This is needed for automatic patch generation: sensors code starts here */ +/* This is needed for automatic patch generation: sensors code ends here */ + + return 0; +} + +#endif + + + +EXPORT_SYMBOL(i2c_add_adapter); +EXPORT_SYMBOL(i2c_del_adapter); +EXPORT_SYMBOL(i2c_add_driver); +EXPORT_SYMBOL(i2c_del_driver); +EXPORT_SYMBOL(i2c_attach_client); +EXPORT_SYMBOL(i2c_detach_client); +EXPORT_SYMBOL(i2c_inc_use_client); +EXPORT_SYMBOL(i2c_dec_use_client); + + +EXPORT_SYMBOL(i2c_master_send); +EXPORT_SYMBOL(i2c_master_recv); +EXPORT_SYMBOL(i2c_control); +EXPORT_SYMBOL(i2c_transfer); +EXPORT_SYMBOL(i2c_adapter_id); +EXPORT_SYMBOL(i2c_probe); + +EXPORT_SYMBOL(i2c_smbus_xfer); +EXPORT_SYMBOL(i2c_smbus_write_quick); +EXPORT_SYMBOL(i2c_smbus_read_byte); +EXPORT_SYMBOL(i2c_smbus_write_byte); +EXPORT_SYMBOL(i2c_smbus_read_byte_data); +EXPORT_SYMBOL(i2c_smbus_write_byte_data); +EXPORT_SYMBOL(i2c_smbus_read_word_data); +EXPORT_SYMBOL(i2c_smbus_write_word_data); +EXPORT_SYMBOL(i2c_smbus_process_call); +EXPORT_SYMBOL(i2c_smbus_read_block_data); +EXPORT_SYMBOL(i2c_smbus_write_block_data); + +EXPORT_SYMBOL(i2c_get_functionality); +EXPORT_SYMBOL(i2c_check_functionality); + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus main module"); +MODULE_PARM(i2c_debug, "i"); +MODULE_PARM_DESC(i2c_debug,"debug level"); + +int init_module(void) +{ + return i2c_init(); +} + +void cleanup_module(void) +{ + i2cproc_cleanup(); +} +#endif diff -u --recursive --new-file v2.3.33/linux/drivers/i2c/i2c-dev.c linux/drivers/i2c/i2c-dev.c --- v2.3.33/linux/drivers/i2c/i2c-dev.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/i2c/i2c-dev.c Thu Dec 16 13:59:38 1999 @@ -0,0 +1,552 @@ +/* + i2c-dev.c - i2c-bus driver, char device interface + + Copyright (C) 1995-97 Simon G. Vogl + Copyright (C) 1998-99 Frodo Looijaard + + 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. +*/ + +/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module. + But I have used so much of his original code and ideas that it seems + only fair to recognize him as co-author -- Frodo */ + +#include +#include +#include +#include +#include + +/* If you want debugging uncomment: */ +/* #define DEBUG */ + +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c)) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,51) +#include +#else +#define __init +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4)) +#define copy_from_user memcpy_fromfs +#define copy_to_user memcpy_tofs +#define get_user_data(to,from) ((to) = get_user(from),0) +#else +#include +#define get_user_data(to,from) get_user(to,from) +#endif + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#include + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* def MODULE */ + +/* struct file_operations changed too often in the 2.1 series for nice code */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +static loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) +static long long i2cdev_lseek (struct file *file, long long offset, int origin); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +static long long i2cdev_llseek (struct inode *inode, struct file *file, + long long offset, int origin); +#else +static int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset, + int origin); +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, + loff_t *offset); +static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, + loff_t *offset); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +static long i2cdev_read (struct inode *inode, struct file *file, char *buf, + unsigned long count); +static long i2cdev_write (struct inode *inode, struct file *file, + const char *buf, unsigned long offset); +#else +static int i2cdev_read(struct inode *inode, struct file *file, char *buf, + int count); +static int i2cdev_write(struct inode *inode, struct file *file, + const char *buf, int count); +#endif + +static int i2cdev_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static int i2cdev_open (struct inode *inode, struct file *file); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) +static int i2cdev_release (struct inode *inode, struct file *file); +#else +static void i2cdev_release (struct inode *inode, struct file *file); +#endif + + +static int i2cdev_attach_adapter(struct i2c_adapter *adap); +static int i2cdev_detach_client(struct i2c_client *client); +static int i2cdev_command(struct i2c_client *client, unsigned int cmd, + void *arg); + +#ifdef MODULE +static +#else +extern +#endif + int __init i2c_dev_init(void); +static int i2cdev_cleanup(void); + +static struct file_operations i2cdev_fops = { + i2cdev_lseek, + i2cdev_read, + i2cdev_write, + NULL, /* i2cdev_readdir */ + NULL, /* i2cdev_select */ + i2cdev_ioctl, + NULL, /* i2cdev_mmap */ + i2cdev_open, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,118) + NULL, /* i2cdev_flush */ +#endif + i2cdev_release, +}; + +#define I2CDEV_ADAPS_MAX I2C_ADAP_MAX +static struct i2c_adapter *i2cdev_adaps[I2CDEV_ADAPS_MAX]; + +static struct i2c_driver i2cdev_driver = { + /* name */ "i2c-dev dummy driver", + /* id */ I2C_DRIVERID_I2CDEV, + /* flags */ I2C_DF_DUMMY, + /* attach_adapter */ i2cdev_attach_adapter, + /* detach_client */ i2cdev_detach_client, + /* command */ i2cdev_command, + /* inc_use */ NULL, + /* dec_use */ NULL, +}; + +static struct i2c_client i2cdev_client_template = { + /* name */ "I2C /dev entry", + /* id */ 1, + /* flags */ 0, + /* addr */ -1, + /* adapter */ NULL, + /* driver */ &i2cdev_driver, + /* data */ NULL +}; + +static int i2cdev_initialized; + +/* Note that the lseek function is called llseek in 2.1 kernels. But things + are complicated enough as is. */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) +long long i2cdev_lseek (struct file *file, long long offset, int origin) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +long long i2cdev_llseek (struct inode *inode, struct file *file, + long long offset, int origin) +#else +int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset, + int origin) +#endif +{ +#ifdef DEBUG +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) + struct inode *inode = file->f_dentry->d_inode; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ + printk("i2c-dev,o: i2c-%d lseek to %ld bytes relative to %d.\n", + MINOR(inode->i_rdev),(long) offset,origin); +#endif /* DEBUG */ + return -ESPIPE; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, + loff_t *offset) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +static long i2cdev_read (struct inode *inode, struct file *file, char *buf, + unsigned long count) +#else +static int i2cdev_read(struct inode *inode, struct file *file, char *buf, + int count) +#endif +{ + char *tmp; + int ret; + +#ifdef DEBUG +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) + struct inode *inode = file->f_dentry->d_inode; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ +#endif /* DEBUG */ + + struct i2c_client *client = (struct i2c_client *)file->private_data; + + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + +#ifdef DEBUG + printk("i2c-dev,o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev),count); +#endif + + ret = i2c_master_recv(client,tmp,count); + copy_to_user(buf,tmp,count); + kfree(tmp); + return ret; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, + loff_t *offset) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +static long i2cdev_write (struct inode *inode, struct file *file, + const char *buf, unsigned long offset) +#else +static int i2cdev_write(struct inode *inode, struct file *file, + const char *buf, int count) +#endif +{ + int ret; + char *tmp; + struct i2c_client *client = (struct i2c_client *)file->private_data; + +#ifdef DEBUG +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) + struct inode *inode = file->f_dentry->d_inode; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ +#endif /* DEBUG */ + + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + copy_from_user(tmp,buf,count); + +#ifdef DEBUG + printk("i2c-dev,o: i2c-%d writing %d bytes.\n",MINOR(inode->i_rdev),count); +#endif + ret = i2c_master_send(client,tmp,count); + kfree(tmp); + return ret; +} + +int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct i2c_client *client = (struct i2c_client *)file->private_data; + struct i2c_smbus_ioctl_data data_arg; + union i2c_smbus_data temp; + int ver,datasize,res; + unsigned long funcs; + +#ifdef DEBUG + printk("i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", + MINOR(inode->i_rdev),cmd, arg); +#endif /* DEBUG */ + + switch ( cmd ) { + case I2C_SLAVE: + if ((arg > 0x3ff) || (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) + return -EINVAL; + client->addr = arg; + return 0; + case I2C_TENBIT: + if (arg) + client->flags |= I2C_M_TEN; + else + client->flags &= ~I2C_M_TEN; + return 0; + case I2C_FUNCS: + if (! arg) { +#ifdef DEBUG + printk("i2c-dev.o: NULL argument pointer in ioctl I2C_SMBUS.\n"); +#endif + return -EINVAL; + } + if (verify_area(VERIFY_WRITE,(unsigned long *) arg, + sizeof(unsigned long))) { +#ifdef DEBUG + printk("i2c-dev.o: invalid argument pointer (%ld) " + "in IOCTL I2C_SMBUS.\n", arg); +#endif + return -EINVAL; + } + + funcs = i2c_get_functionality(client->adapter); + copy_to_user((unsigned long *)arg,&funcs,sizeof(unsigned long)); + return 0; + case I2C_SMBUS: + if (! arg) { +#ifdef DEBUG + printk("i2c-dev.o: NULL argument pointer in ioctl I2C_SMBUS.\n"); +#endif + return -EINVAL; + } + if (verify_area(VERIFY_READ,(struct i2c_smbus_ioctl_data *) arg, + sizeof(struct i2c_smbus_ioctl_data))) { +#ifdef DEBUG + printk("i2c-dev.o: invalid argument pointer (%ld) " + "in IOCTL I2C_SMBUS.\n", arg); +#endif + return -EINVAL; + } + copy_from_user(&data_arg,(struct i2c_smbus_ioctl_data *) arg, + sizeof(struct i2c_smbus_ioctl_data)); + if ((data_arg.size != I2C_SMBUS_BYTE) && + (data_arg.size != I2C_SMBUS_QUICK) && + (data_arg.size != I2C_SMBUS_BYTE_DATA) && + (data_arg.size != I2C_SMBUS_WORD_DATA) && + (data_arg.size != I2C_SMBUS_PROC_CALL) && + (data_arg.size != I2C_SMBUS_BLOCK_DATA)) { +#ifdef DEBUG + printk("i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.size); +#endif + return -EINVAL; + } + /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, + so the check is valid if size==I2C_SMBUS_QUICK too. */ + if ((data_arg.read_write != I2C_SMBUS_READ) && + (data_arg.read_write != I2C_SMBUS_WRITE)) { +#ifdef DEBUG + printk("i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.read_write); +#endif + return -EINVAL; + } + + /* Note that command values are always valid! */ + + if ((data_arg.size == I2C_SMBUS_QUICK) || + ((data_arg.size == I2C_SMBUS_BYTE) && + (data_arg.read_write == I2C_SMBUS_WRITE))) + /* These are special: we do not use data */ + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + data_arg.read_write, data_arg.command, + data_arg.size, NULL); + + if (data_arg.data == NULL) { +#ifdef DEBUG + printk("i2c-dev.o: data is NULL pointer in ioctl I2C_SMBUS.\n"); +#endif + return -EINVAL; + } + + /* This seems unlogical but it is not: if the user wants to read a + value, we must write that value to user memory! */ + ver = ((data_arg.read_write == I2C_SMBUS_WRITE) && + (data_arg.size != I2C_SMBUS_PROC_CALL))?VERIFY_READ:VERIFY_WRITE; + + if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || (data_arg.size == I2C_SMBUS_BYTE)) + datasize = sizeof(data_arg.data->byte); + else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || + (data_arg.size == I2C_SMBUS_PROC_CALL)) + datasize = sizeof(data_arg.data->word); + else /* size == I2C_SMBUS_BLOCK_DATA */ + datasize = sizeof(data_arg.data->block); + + if (verify_area(ver,data_arg.data,datasize)) { +#ifdef DEBUG + printk("i2c-dev.o: invalid pointer data (%p) in ioctl I2C_SMBUS.\n", + data_arg.data); +#endif + return -EINVAL; + } + + if ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_WRITE)) + copy_from_user(&temp,data_arg.data,datasize); + res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, + data_arg.read_write, + data_arg.command,data_arg.size,&temp); + if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_READ))) + copy_to_user(data_arg.data,&temp,datasize); + return res; + + default: + return i2c_control(client,cmd,arg); + } + return 0; +} + +int i2cdev_open (struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct i2c_client *client; + + if ((minor >= I2CDEV_ADAPS_MAX) || ! (i2cdev_adaps[minor])) { +#ifdef DEBUG + printk("i2c-dev.o: Trying to open unattached adapter i2c-%d\n",minor); +#endif + return -ENODEV; + } + + /* Note that we here allocate a client for later use, but we will *not* + register this client! Yes, this is safe. No, it is not very clean. */ + if(! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) + return -ENOMEM; + memcpy(client,&i2cdev_client_template,sizeof(struct i2c_client)); + client->adapter = i2cdev_adaps[minor]; + file->private_data = client; + + i2cdev_adaps[minor]->inc_use(i2cdev_adaps[minor]); + MOD_INC_USE_COUNT; + +#ifdef DEBUG + printk("i2c-dev.o: opened i2c-%d\n",minor); +#endif + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) +static int i2cdev_release (struct inode *inode, struct file *file) +#else +static void i2cdev_release (struct inode *inode, struct file *file) +#endif +{ + unsigned int minor = MINOR(inode->i_rdev); + kfree(file->private_data); + file->private_data=NULL; +#ifdef DEBUG + printk("i2c-dev.o: Closed: i2c-%d\n", minor); +#endif + MOD_DEC_USE_COUNT; + i2cdev_adaps[minor]->dec_use(i2cdev_adaps[minor]); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) + return 0; +#endif +} + +int i2cdev_attach_adapter(struct i2c_adapter *adap) +{ + int i; + + if ((i = i2c_adapter_id(adap)) < 0) { + printk("i2c-dev.o: Unknown adapter ?!?\n"); + return -ENODEV; + } + if (i >= I2CDEV_ADAPS_MAX) { + printk("i2c-dev.o: Adapter number too large?!? (%d)\n",i); + return -ENODEV; + } + + if (! i2cdev_adaps[i]) { + i2cdev_adaps[i] = adap; + printk("i2c-dev.o: Registered '%s' as minor %d\n",adap->name,i); + } else { + i2cdev_adaps[i] = NULL; +#ifdef DEBUG + printk("i2c-dev.o: Adapter unregistered: %s\n",adap->name); +#endif + } + + return 0; +} + +int i2cdev_detach_client(struct i2c_client *client) +{ + return 0; +} + +static int i2cdev_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + return -1; +} + +int __init i2c_dev_init(void) +{ + int res; + + printk("i2c-dev.o: i2c /dev entries driver module\n"); + + i2cdev_initialized = 0; + if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) { + printk("i2c-dev.o: unable to get major %d for i2c bus\n",I2C_MAJOR); + return -EIO; + } + i2cdev_initialized ++; + + if ((res = i2c_add_driver(&i2cdev_driver))) { + printk("i2c-dev.o: Driver registration failed, module not inserted.\n"); + i2cdev_cleanup(); + return res; + } + i2cdev_initialized ++; + return 0; +} + +int i2cdev_cleanup(void) +{ + int res; + + if (i2cdev_initialized >= 2) { + if ((res = i2c_del_driver(&i2cdev_driver))) { + printk("i2c-dev.o: Driver deregistration failed, " + "module not removed.\n"); + return res; + } + i2cdev_initialized ++; + } + + if (i2cdev_initialized >= 1) { + if ((res = unregister_chrdev(I2C_MAJOR,"i2c"))) { + printk("i2c-dev.o: unable to release major %d for i2c bus\n",I2C_MAJOR); + return res; + } + i2cdev_initialized --; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard and Simon G. Vogl "); +MODULE_DESCRIPTION("I2C /dev entries driver"); + +int init_module(void) +{ + return i2c_dev_init(); +} + +int cleanup_module(void) +{ + return i2cdev_cleanup(); +} + +#endif /* def MODULE */ + diff -u --recursive --new-file v2.3.33/linux/drivers/i2c/i2c-elektor.c linux/drivers/i2c/i2c-elektor.c --- v2.3.33/linux/drivers/i2c/i2c-elektor.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/i2c/i2c-elektor.c Thu Dec 16 13:59:38 1999 @@ -0,0 +1,325 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020135 +#include +#else +#define __init +#endif +#include +#include + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#include +#include +#include "i2c-pcf8584.h" + +#define DEFAULT_BASE 0x300 +#define DEFAULT_IRQ 0 +#define DEFAULT_CLOCK 0x1c +#define DEFAULT_OWN 0x55 + +static int base = 0; +static int irq = 0; +static int clock = 0; +static int own = 0; +static int i2c_debug=0; +static struct i2c_pcf_isa gpi; +#if (LINUX_VERSION_CODE < 0x020301) +static struct wait_queue *pcf_wait = NULL; +#else +static wait_queue_head_t pcf_wait; +#endif +static int pcf_pending; + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x +#define DEBE(x) x /* error messages */ + + +/* --- Convenience defines for the i2c port: */ +#define BASE ((struct i2c_pcf_isa *)(data))->pi_base +#define DATA BASE /* Adapter data port */ +#define CTRL (BASE+1) /* Adapter control port */ + +/* ----- local functions ---------------------------------------------- */ + +static void pcf_isa_setbyte(void *data, int ctl, int val) +{ + if (ctl) { + if (gpi.pi_irq > 0) { + DEB3(printk("i2c-elektor.o: Write control 0x%x\n", val|I2C_PCF_ENI)); + outb(val | I2C_PCF_ENI, CTRL); + } else { + DEB3(printk("i2c-elektor.o: Write control 0x%x\n", val)); + outb(val, CTRL); + } + } else { + DEB3(printk("i2c-elektor.o: Write data 0x%x\n", val)); + outb(val, DATA); + } +} + +static int pcf_isa_getbyte(void *data, int ctl) +{ + int val; + + if (ctl) { + val = inb(CTRL); + DEB3(printk("i2c-elektor.o: Read control 0x%x\n", val)); + } else { + val = inb(DATA); + DEB3(printk("i2c-elektor.o: Read data 0x%x\n", val)); + } + return (val); +} + +static int pcf_isa_getown(void *data) +{ + return (gpi.pi_own); +} + + +static int pcf_isa_getclock(void *data) +{ + return (gpi.pi_clock); +} + + + +#if LINUX_VERSION_CODE < 0x02017f +static void schedule_timeout(int j) +{ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + j; + schedule(); +} +#endif + +#if 0 +static void pcf_isa_sleep(unsigned long timeout) +{ + schedule_timeout( timeout * HZ); +} +#endif + + +static void pcf_isa_waitforpin(void) { + + int timeout = 2; + + if (gpi.pi_irq > 0) { + cli(); + if (pcf_pending == 0) { +#if LINUX_VERSION_CODE < 0x02017f + current->timeout = jiffies + timeout * HZ; + interruptible_sleep_on(&pcf_wait); +#else + interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ ); +#endif + } + else + pcf_pending = 0; + sti(); +#if LINUX_VERSION_CODE < 0x02017f + current->timeout = 0; +#endif + } + else { + udelay(100); + } +} + + +static void pcf_isa_handler(int this_irq, void *dev_id, struct pt_regs *regs) { + + pcf_pending = 1; + wake_up_interruptible(&pcf_wait); +} + + +static int pcf_isa_init(void) +{ + if (check_region(gpi.pi_base, 2) < 0 ) { + return -ENODEV; + } else { + request_region(gpi.pi_base, 2, "i2c (isa bus adapter)"); + } + if (gpi.pi_irq > 0) { + if (request_irq(gpi.pi_irq, pcf_isa_handler, 0, "PCF8584", 0) < 0) { + printk("i2c-elektor.o: Request irq%d failed\n", gpi.pi_irq); + gpi.pi_irq = 0; + } + else + enable_irq(gpi.pi_irq); + } + return 0; +} + + +static void pcf_isa_exit(void) +{ + if (gpi.pi_irq > 0) { + disable_irq(gpi.pi_irq); + free_irq(gpi.pi_irq, 0); + } + release_region(gpi.pi_base , 2); +} + + +static int pcf_isa_reg(struct i2c_client *client) +{ + return 0; +} + + +static int pcf_isa_unreg(struct i2c_client *client) +{ + return 0; +} + +static void pcf_isa_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void pcf_isa_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +static struct i2c_algo_pcf_data pcf_isa_data = { + NULL, + pcf_isa_setbyte, + pcf_isa_getbyte, + pcf_isa_getown, + pcf_isa_getclock, + pcf_isa_waitforpin, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter pcf_isa_ops = { + "PCF8584 ISA adapter", + I2C_HW_P_ELEK, + NULL, + &pcf_isa_data, + pcf_isa_inc_use, + pcf_isa_dec_use, + pcf_isa_reg, + pcf_isa_unreg, +}; + +int __init i2c_pcfisa_init(void) +{ + + struct i2c_pcf_isa *pisa = &gpi; + + printk("i2c-elektor.o: i2c pcf8584-isa adapter module\n"); + if (base == 0) + pisa->pi_base = DEFAULT_BASE; + else + pisa->pi_base = base; + + if (irq == 0) + pisa->pi_irq = DEFAULT_IRQ; + else + pisa->pi_irq = irq; + + if (clock == 0) + pisa->pi_clock = DEFAULT_CLOCK; + else + pisa->pi_clock = clock; + + if (own == 0) + pisa->pi_own = DEFAULT_OWN; + else + pisa->pi_own = own; + + pcf_isa_data.data = (void *)pisa; +#if (LINUX_VERSION_CODE >= 0x020301) + init_waitqueue_head(&pcf_wait); +#endif + if (pcf_isa_init() == 0) { + if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + printk("i2c-elektor.o: found device at %#x.\n", pisa->pi_base); + return 0; +} + + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Hans Berglund "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); + +MODULE_PARM(base, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(clock, "i"); +MODULE_PARM(own, "i"); + +int init_module(void) +{ + return i2c_pcfisa_init(); +} + +void cleanup_module(void) +{ + i2c_pcf_del_bus(&pcf_isa_ops); + pcf_isa_exit(); +} + +#endif + + diff -u --recursive --new-file v2.3.33/linux/drivers/i2c/i2c-elv.c linux/drivers/i2c/i2c-elv.c --- v2.3.33/linux/drivers/i2c/i2c-elv.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/i2c/i2c-elv.c Thu Dec 16 13:59:38 1999 @@ -0,0 +1,236 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-elv.c i2c-hw access for philips style parallel port adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- +static char rcsid[] = "$Id: i2c-elv.c,v 1.11 1999/10/08 14:25:11 frodo Exp $"; + ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020135 +#include +#else +#define __init +#endif + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#if LINUX_VERSION_CODE >= 0x020100 +# include +#else +# include +#endif + +#include +#include +#include +#include +#include + +#define DEFAULT_BASE 0x378 +static int base=0; +static unsigned char PortData = 0; + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ +#define DEBINIT(x) x /* detection status messages */ + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + + +/* ----- local functions ---------------------------------------------- */ + + +static void bit_elv_setscl(void *data, int state) +{ + if (state) { + PortData &= 0xfe; + } else { + PortData |=1; + } + outb(PortData, DATA); +} + +static void bit_elv_setsda(void *data, int state) +{ + if (state) { + PortData &=0xfd; + } else { + PortData |=2; + } + outb(PortData, DATA); +} + +static int bit_elv_getscl(void *data) +{ + return ( 0 == ( (inb_p(STAT)) & 0x08 ) ); +} + +static int bit_elv_getsda(void *data) +{ + return ( 0 == ( (inb_p(STAT)) & 0x40 ) ); +} + +static int bit_elv_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + return -ENODEV; + } else { + /* test for ELV adap. */ + if (inb(base+1) & 0x80) { /* BUSY should be high */ + DEBINIT(printk("i2c-elv.o: Busy was low.\n")); + return -ENODEV; + } else { + outb(0x0c,base+2); /* SLCT auf low */ + udelay(400); + if ( !(inb(base+1) && 0x10) ) { + outb(0x04,base+2); + DEBINIT(printk("i2c-elv.o: Select was high.\n")); + return -ENODEV; + } + } + request_region(base,(base == 0x3bc)? 3 : 8, + "i2c (ELV adapter)"); + PortData = 0; + bit_elv_setsda((void*)base,1); + bit_elv_setscl((void*)base,1); + } + return 0; +} + +static void bit_elv_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + +static int bit_elv_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_elv_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_elv_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void bit_elv_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +static struct i2c_algo_bit_data bit_elv_data = { + NULL, + bit_elv_setsda, + bit_elv_setscl, + bit_elv_getsda, + bit_elv_getscl, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter bit_elv_ops = { + "ELV Parallel port adaptor", + I2C_HW_B_ELV, + NULL, + &bit_elv_data, + bit_elv_inc_use, + bit_elv_dec_use, + bit_elv_reg, + bit_elv_unreg, +}; + +int __init i2c_bitelv_init(void) +{ + printk("i2c-elv.o: i2c ELV parallel port adapter module\n"); + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_elv_data.data=(void*)DEFAULT_BASE; + if (bit_elv_init()==0) { + if(i2c_bit_add_bus(&bit_elv_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } else { + bit_elv_ops.data=(void*)base; + if (bit_elv_init()==0) { + if(i2c_bit_add_bus(&bit_elv_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } + printk("i2c-elv.o: found device at %#x.\n",base); + return 0; +} + + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for ELV parallel port adapter") +; + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + return i2c_bitelv_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_elv_ops); + bit_elv_exit(); +} + +#endif diff -u --recursive --new-file v2.3.33/linux/drivers/i2c/i2c-pcf8584.h linux/drivers/i2c/i2c-pcf8584.h --- v2.3.33/linux/drivers/i2c/i2c-pcf8584.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/i2c/i2c-pcf8584.h Thu Dec 16 13:59:38 1999 @@ -0,0 +1,77 @@ +/* -------------------------------------------------------------------- */ +/* i2c-pcf8584.h: PCF 8584 global defines */ +/* -------------------------------------------------------------------- */ +/* Copyright (C) 1996 Simon G. Vogl + 1999 Hans Berglund + + 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: i2c-pcf8584.h,v 1.1 1999/07/18 14:01:33 frodo Exp $ */ + +/* With some changes from Frodo Looijaard */ + +#ifndef I2C_PCF8584_H +#define I2C_PCF8584_H 1 + +/* ----- Control register bits ---------------------------------------- */ +#define I2C_PCF_PIN 0x80 +#define I2C_PCF_ESO 0x40 +#define I2C_PCF_ES1 0x20 +#define I2C_PCF_ES2 0x10 +#define I2C_PCF_ENI 0x08 +#define I2C_PCF_STA 0x04 +#define I2C_PCF_STO 0x02 +#define I2C_PCF_ACK 0x01 + +#define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK) +#define I2C_PCF_REPSTART ( I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_IDLE (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ACK) + +/* ----- Status register bits ----------------------------------------- */ +/*#define I2C_PCF_PIN 0x80 as above*/ + +#define I2C_PCF_INI 0x40 /* 1 if not initialized */ +#define I2C_PCF_STS 0x20 +#define I2C_PCF_BER 0x10 +#define I2C_PCF_AD0 0x08 +#define I2C_PCF_LRB 0x08 +#define I2C_PCF_AAS 0x04 +#define I2C_PCF_LAB 0x02 +#define I2C_PCF_BB 0x01 + +/* ----- Chip clock frequencies --------------------------------------- */ +#define I2C_PCF_CLK3 0x00 +#define I2C_PCF_CLK443 0x10 +#define I2C_PCF_CLK6 0x14 +#define I2C_PCF_CLK8 0x18 +#define I2C_PCF_CLK12 0x1c + +/* ----- transmission frequencies ------------------------------------- */ +#define I2C_PCF_TRNS90 0x00 /* 90 kHz */ +#define I2C_PCF_TRNS45 0x01 /* 45 kHz */ +#define I2C_PCF_TRNS11 0x02 /* 11 kHz */ +#define I2C_PCF_TRNS15 0x03 /* 1.5 kHz */ + + +/* ----- Access to internal registers according to ES1,ES2 ------------ */ +/* they are mapped to the data port ( a0 = 0 ) */ +/* available when ESO == 0 : */ + +#define I2C_PCF_OWNADR 0 +#define I2C_PCF_INTREG I2C_PCF_ES2 +#define I2C_PCF_CLKREG I2C_PCF_ES1 + +#endif I2C_PCF8584_H diff -u --recursive --new-file v2.3.33/linux/drivers/i2c/i2c-philips-par.c linux/drivers/i2c/i2c-philips-par.c --- v2.3.33/linux/drivers/i2c/i2c-philips-par.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/i2c/i2c-philips-par.c Thu Dec 16 13:59:38 1999 @@ -0,0 +1,232 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-philips-par.c i2c-hw access for philips style parallel port adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- +static char rcsid[] = "$Id: i2c-philips-par.c,v 1.11 1999/10/08 14:25:11 frodo Exp $"; + ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020135 +#include +#else +#define __init +#endif +#include +#include + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#include + +#define DEFAULT_BASE 0x378 +static int base=0; + +/* Note: all we need to know is the base address of the parallel port, so + * instead of having a dedicated struct to store this value, we store this + * int in the pointer field (=bit_lp_ops.data) itself. + */ + +/* Note2: as the hw that implements the i2c bus on the parallel port is + * incompatible with other epp stuff etc., we access the port exclusively + * and don't cooperate with parport functions. + */ + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + +/* ----- printer port defines ------------------------------------------*/ + /* Pin Port Inverted name */ +#define I2C_ON 0x20 /* 12 status N paper */ + /* ... only for phil. not used */ +#define I2C_SDA 0x80 /* 9 data N data7 */ +#define I2C_SCL 0x08 /* 17 ctrl N dsel */ + +#define I2C_SDAIN 0x80 /* 11 stat Y busy */ +#define I2C_SCLIN 0x08 /* 15 stat Y enable */ + +#define I2C_DMASK 0x7f +#define I2C_CMASK 0xf7 + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + +/* ----- local functions ---------------------------------------------- */ + +static void bit_lp_setscl(void *data, int state) +{ + /*be cautious about state of the control register - + touch only the one bit needed*/ + if (state) { + outb(inb(CTRL)|I2C_SCL, CTRL); + } else { + outb(inb(CTRL)&I2C_CMASK, CTRL); + } +} + +static void bit_lp_setsda(void *data, int state) +{ + if (state) { + outb(I2C_DMASK , DATA); + } else { + outb(I2C_SDA , DATA); + } +} + +static int bit_lp_getscl(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SCLIN ) ); +} + +static int bit_lp_getsda(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +static int bit_lp_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + return -ENODEV; + } else { + request_region(base,(base == 0x3bc)? 3 : 8, + "i2c (parallel port adapter)"); + /* reset hardware to sane state */ + bit_lp_setsda((void*)base,1); + bit_lp_setscl((void*)base,1); + } + return 0; +} + +static void bit_lp_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + +static int bit_lp_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_lp_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_lp_inc_use(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} + +static void bit_lp_dec_use(struct i2c_adapter *adap) +{ + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + +static struct i2c_algo_bit_data bit_lp_data = { + NULL, + bit_lp_setsda, + bit_lp_setscl, + bit_lp_getsda, + bit_lp_getscl, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter bit_lp_ops = { + "Philips Parallel port adapter", + I2C_HW_B_LP, + NULL, + &bit_lp_data, + bit_lp_inc_use, + bit_lp_dec_use, + bit_lp_reg, + bit_lp_unreg, +}; + + +int __init i2c_bitlp_init(void) +{ + printk("i2c-philips-par.o: i2c Philips parallel port adapter module\n"); + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_lp_data.data=(void*)DEFAULT_BASE; + if (bit_lp_init()==0) { + if (i2c_bit_add_bus(&bit_lp_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } else { + bit_lp_data.data=(void*)base; + if (bit_lp_init()==0) { + if (i2c_bit_add_bus(&bit_lp_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } + printk("i2c-philips-par.o: found device at %#x.\n",base); + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Philips parallel port adapter"); + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + return i2c_bitlp_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_lp_ops); + bit_lp_exit(); +} + +#endif + + + diff -u --recursive --new-file v2.3.33/linux/drivers/i2c/i2c-velleman.c linux/drivers/i2c/i2c-velleman.c --- v2.3.33/linux/drivers/i2c/i2c-velleman.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/i2c/i2c-velleman.c Thu Dec 16 13:59:38 1999 @@ -0,0 +1,219 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-velleman.c i2c-hw access for Velleman K9000 adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-96 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- +static char rcsid[] = "$Id: i2c-velleman.c,v 1.13 1999/10/08 14:25:11 frodo Exp $"; + ------------------------------------------------------------------------- */ + +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020135 +#include +#else +#define __init +#endif +#include /* for 2.0 kernels to get NULL */ +#include /* for 2.0 kernels to get ENODEV */ +#include + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#include + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + + /* Pin Port Inverted name */ +#define I2C_SDA 0x02 /* ctrl bit 1 (inv) */ +#define I2C_SCL 0x08 /* ctrl bit 3 (inv) */ + +#define I2C_SDAIN 0x10 /* stat bit 4 */ +#define I2C_SCLIN 0x08 /* ctrl bit 3 (inv) (reads own output) */ + +#define I2C_DMASK 0xfd +#define I2C_CMASK 0xf7 + + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + +#define DEFAULT_BASE 0x378 +static int base=0; + +/* ----- local functions --------------------------------------------------- */ + +static void bit_velle_setscl(void *data, int state) +{ + if (state) { + outb(inb(CTRL) & I2C_CMASK, CTRL); + } else { + outb(inb(CTRL) | I2C_SCL, CTRL); + } + +} + +static void bit_velle_setsda(void *data, int state) +{ + if (state) { + outb(inb(CTRL) & I2C_DMASK , CTRL); + } else { + outb(inb(CTRL) | I2C_SDA, CTRL); + } + +} + +static int bit_velle_getscl(void *data) +{ + return ( 0 == ( (inb(CTRL)) & I2C_SCLIN ) ); +} + +static int bit_velle_getsda(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +static int bit_velle_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + DEBE(printk("i2c-velleman.o: Port %#x already in use.\n", base)); + return -ENODEV; + } else { + request_region(base, (base == 0x3bc)? 3 : 8, + "i2c (Vellemann adapter)"); + bit_velle_setsda((void*)base,1); + bit_velle_setscl((void*)base,1); + } + return 0; +} + +static void bit_velle_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + + +static int bit_velle_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_velle_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_velle_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void bit_velle_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + +static struct i2c_algo_bit_data bit_velle_data = { + NULL, + bit_velle_setsda, + bit_velle_setscl, + bit_velle_getsda, + bit_velle_getscl, + 10, 10, 100, /* waits, timeout */ +}; + +static struct i2c_adapter bit_velle_ops = { + "Velleman K8000", + I2C_HW_B_VELLE, + NULL, + &bit_velle_data, + bit_velle_inc_use, + bit_velle_dec_use, + bit_velle_reg, + bit_velle_unreg, +}; + +int __init i2c_bitvelle_init(void) +{ + printk("i2c-velleman.o: i2c Velleman K8000 adapter module\n"); + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_velle_data.data=(void*)DEFAULT_BASE; + if (bit_velle_init()==0) { + if(i2c_bit_add_bus(&bit_velle_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } else { + bit_velle_data.data=(void*)base; + if (bit_velle_init()==0) { + if(i2c_bit_add_bus(&bit_velle_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } + printk("i2c-velleman.o: found device at %#x.\n",base); + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Velleman K8000 adapter"); + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + return i2c_bitvelle_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_velle_ops); + bit_velle_exit(); +} + +#endif diff -u --recursive --new-file v2.3.33/linux/drivers/i2o/i2o_block.c linux/drivers/i2o/i2o_block.c --- v2.3.33/linux/drivers/i2o/i2o_block.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/i2o/i2o_block.c Thu Dec 16 13:57:05 1999 @@ -175,7 +175,7 @@ offset = ((u64)(req->sector+base)) << 9; __raw_writel( offset & 0xFFFFFFFF, msg+24); __raw_writel(offset>>32, msg+28); - mptr=msg+8; + mptr=msg+32; if(req->cmd == READ) { @@ -216,7 +216,7 @@ bh = bh->b_reqnext; } } - __raw_writel(I2O_MESSAGE_SIZE(mptr-msg) | SGL_OFFSET_8, msg); + __raw_writel(I2O_MESSAGE_SIZE(mptr-msg)>>2 | SGL_OFFSET_8, msg); if(req->current_nr_sectors > 8) printk("Gathered sectors %ld.\n", diff -u --recursive --new-file v2.3.33/linux/drivers/misc/acpi.c linux/drivers/misc/acpi.c --- v2.3.33/linux/drivers/misc/acpi.c Tue Dec 7 09:32:43 1999 +++ linux/drivers/misc/acpi.c Thu Dec 16 17:06:37 1999 @@ -683,9 +683,11 @@ if (pm2_cnt) { /* Disable PCI arbitration while sleeping, to avoid DMA corruption? */ + cli(); outb(inb(pm2_cnt) | ACPI_ARB_DIS, pm2_cnt); inb(acpi_p_blk + ACPI_P_LVL3); outb(inb(pm2_cnt) & ~ACPI_ARB_DIS, pm2_cnt); + sti(); } else { inb(acpi_p_blk + ACPI_P_LVL3); diff -u --recursive --new-file v2.3.33/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v2.3.33/linux/drivers/net/Makefile Wed Dec 8 14:11:26 1999 +++ linux/drivers/net/Makefile Fri Dec 17 08:18:31 1999 @@ -50,8 +50,7 @@ ifeq ($(CONFIG_SK98LIN),y) SUB_DIRS += sk98lin -MOD_IN_SUB_DIRS += sk98lin -L_OBJS += sk98lin/sk98lin.o +obj-y += sk98lin/sk98lin.o else ifeq ($(CONFIG_SK98LIN),m) MOD_IN_SUB_DIRS += sk98lin diff -u --recursive --new-file v2.3.33/linux/drivers/net/aironet4500.h linux/drivers/net/aironet4500.h --- v2.3.33/linux/drivers/net/aironet4500.h Wed Dec 8 14:11:26 1999 +++ linux/drivers/net/aironet4500.h Sat Dec 18 15:34:29 1999 @@ -2,7 +2,7 @@ * Aironet 4500 Pcmcia driver * * Elmer Joandi, Januar 1999 - * Copyright Elmer Joandi, all rights restricted + * Copyright: GPL * * * Revision 0.1 ,started 30.12.1998 @@ -15,12 +15,14 @@ #define AIRONET4500_H // redefined to avoid PCMCIA includes -#include -#include -#include - -#if (LINUX_VERSION_CODE < 0x2030e) + #include +/*#include + #include +*/ +#if LINUX_VERSION_CODE < 0x2030E #define NET_DEVICE device +#error bad kernel version code + #else #define NET_DEVICE net_device #endif @@ -28,12 +30,13 @@ #if LINUX_VERSION_CODE < 0x20300 #define init_MUTEX(a) *(a) = MUTEX; #endif - +/* #include #include #include #include #include +*/ #include //damn idiot PCMCIA stuff @@ -53,41 +56,12 @@ -#if LINUX_VERSION_CODE <= 0x20100 - -typedef struct { volatile int lock ;} my_spinlock_t; - -#define my_spin_lock_irqsave(a,b) {\ - save_flags(b);\ - cli();\ - (a)->lock++;while(0);\ - if ((a)->lock != 1 )\ - printk("awc_spinlock high at locking \n");\ -} - -#define my_spin_unlock_irqrestore(a,b) {\ - cli(); (a)->lock--;while(0);\ - if ((a)->lock != 0 )\ - printk("awc_spinlock !=0 at unlocking \n");\ - restore_flags(b);\ -} - - -#else -#if LINUX_VERSION_CODE < 0x20300 -#include -#else #include -#endif -#ifndef __SMP__ -// #warning non-SMP 2.2 kernel -#endif typedef spinlock_t my_spinlock_t ; +#define my_spin_lock_init(a) spin_lock_init(a) #define my_spin_lock_irqsave(a,b) spin_lock_irqsave(a,b) #define my_spin_unlock_irqrestore(a,b) spin_unlock_irqrestore(a,b) -#endif //kernel version - #if LINUX_VERSION_CODE <= 0x20100 #define in_interrupt() intr_count @@ -193,11 +167,17 @@ /* if (!in_interrupt())\ printk("bap lock under cli but not in int\n");\ */ + +#define AWC_LOCK_COMMAND_ISSUING(a) my_spin_lock_irqsave(&a->command_issuing_spinlock,a->command_issuing_spinlock_flags); +#define AWC_UNLOCK_COMMAND_ISSUING(a) my_spin_unlock_irqrestore(&a->command_issuing_spinlock,a->command_issuing_spinlock_flags); + #define AWC_BAP_LOCK_UNDER_CLI_REAL(cmd) \ if (!cmd.priv) {\ printk(KERN_CRIT "awc4500: no priv present in command !");\ }\ cmd.bap = &(cmd.priv->bap1);\ + if (both_bap_lock)\ + my_spin_lock_irqsave(&cmd.priv->both_bap_spinlock,cmd.priv->both_bap_spinlock_flags);\ if (cmd.bap){\ my_spin_lock_irqsave(&(cmd.bap->spinlock),cmd.bap->flags);\ cmd.bap->lock++;\ @@ -213,6 +193,8 @@ printk(KERN_CRIT "awc4500: no priv present in command,lockup follows !");\ }\ cmd.bap = &(cmd.priv->bap0);\ + if (both_bap_lock)\ + my_spin_lock_irqsave(&cmd.priv->both_bap_spinlock,cmd.priv->both_bap_spinlock_flags);\ my_spin_lock_irqsave(&(cmd.bap->spinlock),cmd.bap->flags);\ DOWN(&(cmd.priv->bap0.sem));\ cmd.bap->lock++;\ @@ -223,6 +205,8 @@ #define AWC_BAP_LOCK_NOT_CLI_CLI_REAL(cmd) {\ cmd.bap = &(cmd.priv->bap0);\ + if (both_bap_lock)\ + my_spin_lock_irqsave(&cmd.priv->both_bap_spinlock,cmd.priv->both_bap_spinlock_flags);\ my_spin_lock_irqsave(&(cmd.bap->spinlock),cmd.bap->flags);\ cmd.bap->lock++;\ if (cmd.bap->lock > 1)\ @@ -292,6 +276,8 @@ my_spin_unlock_irqrestore(&(cmd.bap->spinlock),cmd.bap->flags);\ }\ }\ + if (both_bap_lock)\ + my_spin_unlock_irqrestore(&cmd.priv->both_bap_spinlock,cmd.priv->both_bap_spinlock_flags);\ } #define AWC_RELEASE_COMMAND(com) {\ @@ -475,7 +461,7 @@ struct awc_fid * head; struct awc_fid * tail; int size; - my_spinlock_t lock; + my_spinlock_t spinlock; }; @@ -483,16 +469,13 @@ awc_fid_queue_init(struct awc_fid_queue * queue){ unsigned long flags; -#ifdef __SMP__ - queue->lock.lock = 0; -#endif memset(queue,0, sizeof(struct awc_fid_queue)); - - my_spin_lock_irqsave(&queue->lock,flags); + my_spin_lock_init(&queue->spinlock); + my_spin_lock_irqsave(&queue->spinlock,flags); queue->head = NULL; queue->tail = NULL; queue->size = 0; - my_spin_unlock_irqrestore(&queue->lock,flags); + my_spin_unlock_irqrestore(&queue->spinlock,flags); }; extern inline void @@ -501,7 +484,7 @@ unsigned long flags; - my_spin_lock_irqsave(&queue->lock,flags); + my_spin_lock_irqsave(&queue->spinlock,flags); fid->prev = queue->tail; fid->next = NULL; @@ -515,7 +498,7 @@ queue->head = fid; queue->size++; - my_spin_unlock_irqrestore(&queue->lock,flags); + my_spin_unlock_irqrestore(&queue->spinlock,flags); }; @@ -526,7 +509,7 @@ unsigned long flags; - my_spin_lock_irqsave(&queue->lock,flags); + my_spin_lock_irqsave(&queue->spinlock,flags); fid->prev = NULL; fid->next = queue->head; @@ -541,7 +524,7 @@ queue->size++; - my_spin_unlock_irqrestore(&queue->lock,flags); + my_spin_unlock_irqrestore(&queue->spinlock,flags); }; @@ -579,11 +562,11 @@ awc_fid_queue_remove( struct awc_fid_queue * queue, struct awc_fid * fid){ unsigned long flags; - my_spin_lock_irqsave(&queue->lock,flags); + my_spin_lock_irqsave(&queue->spinlock,flags); awc_fid_queue_rm(queue,fid); - my_spin_unlock_irqrestore(&queue->lock,flags); + my_spin_unlock_irqrestore(&queue->spinlock,flags); }; @@ -595,14 +578,14 @@ unsigned long flags; struct awc_fid * fid; - my_spin_lock_irqsave(&queue->lock,flags); + my_spin_lock_irqsave(&queue->spinlock,flags); fid = queue->head; if (fid) awc_fid_queue_rm(queue,fid); - my_spin_unlock_irqrestore(&queue->lock,flags); + my_spin_unlock_irqrestore(&queue->spinlock,flags); return fid; }; @@ -616,13 +599,13 @@ unsigned long flags; struct awc_fid * fid; - my_spin_lock_irqsave(&queue->lock,flags); + my_spin_lock_irqsave(&queue->spinlock,flags); fid = queue->tail; if (fid) awc_fid_queue_rm(queue,fid); - my_spin_unlock_irqrestore(&queue->lock,flags); + my_spin_unlock_irqrestore(&queue->spinlock,flags); return fid; }; @@ -630,7 +613,7 @@ #define AWC_TX_HEAD_SIZE 0x44 -#define AWC_TX_ALLOC_SMALL_SIZE 150 +#define AWC_TX_ALLOC_SMALL_SIZE 200 #define AWC_RX_BUFFS 50 @@ -1522,7 +1505,13 @@ volatile int interrupt_count; // Command serialize stuff - struct semaphore command_semaphore; +//changed to spinlock struct semaphore command_semaphore; + my_spinlock_t both_bap_spinlock; + unsigned long both_bap_spinlock_flags; + my_spinlock_t bap_setup_spinlock; + unsigned long bap_setup_spinlock_flags; + my_spinlock_t command_issuing_spinlock; + unsigned long command_issuing_spinlock_flags; volatile int unlock_command_postponed; struct awc_command cmd; long long async_command_start; @@ -1607,6 +1596,8 @@ extern int sleep_before_command ; extern int bap_sleep_before_write; extern int sleep_in_command ; +extern int both_bap_lock; +extern int bap_setup_spinlock; extern int tx_queue_len ; extern int tx_rate; extern int awc_full_stats; @@ -1614,6 +1605,7 @@ #define MAX_AWCS 4 extern struct NET_DEVICE * aironet4500_devices[MAX_AWCS]; +#define AWC_DEBUG 1 #ifdef AWC_DEBUG #define DEBUG(a,args...) if (awc_debug & a) printk( args) diff -u --recursive --new-file v2.3.33/linux/drivers/net/aironet4500_card.c linux/drivers/net/aironet4500_card.c --- v2.3.33/linux/drivers/net/aironet4500_card.c Wed Dec 8 14:11:26 1999 +++ linux/drivers/net/aironet4500_card.c Sat Dec 18 15:34:29 1999 @@ -2,7 +2,7 @@ * Aironet 4500 PCI-ISA-i365 driver * * Elmer Joandi, Januar 1999 - * Copyright Elmer Joandi, all rights restricted + * Copyright GPL * * * Revision 0.1 ,started 30.12.1998 @@ -35,7 +35,7 @@ #include #include #include - +#include #if LINUX_VERSION_CODE < 0x20100 #include #endif diff -u --recursive --new-file v2.3.33/linux/drivers/net/aironet4500_core.c linux/drivers/net/aironet4500_core.c --- v2.3.33/linux/drivers/net/aironet4500_core.c Wed Dec 8 14:11:26 1999 +++ linux/drivers/net/aironet4500_core.c Sat Dec 18 15:34:29 1999 @@ -1,14 +1,21 @@ /* - * Aironet 4500 Pcmcia driver + * Aironet 4500/4800 driver core * * Elmer Joandi, Januar 1999 - * Copyright Elmer Joandi, all rights restricted + * Copyright: GPL * * * Revision 0.1 ,started 30.12.1998 * * */ + /* CHANGELOG: + march 99, stable version 2.0 + august 99, stable version 2.2 + november 99, integration with 2.3 + 17.12.99: finally, got SMP near-correct. + timing issues remain- on SMP box its 15% slower on tcp + */ #include #include @@ -34,12 +41,16 @@ int sleep_before_command = 1; int bap_sleep_before_write= 1; int sleep_in_command = 1; +int both_bap_lock =0; /* activated at awc_init in this */ +int bap_setup_spinlock =0; /* file if numcpu >1 */ EXPORT_SYMBOL(bap_sleep); EXPORT_SYMBOL(bap_sleep_after_setup); EXPORT_SYMBOL(sleep_before_command); EXPORT_SYMBOL(bap_sleep_before_write); EXPORT_SYMBOL(sleep_in_command); +EXPORT_SYMBOL(both_bap_lock); +EXPORT_SYMBOL(bap_setup_spinlock); struct awc_strings awc_status_error_codes[]=awc_reply_error_strings; struct awc_strings awc_command_names[]=awc_command_name_strings; @@ -160,10 +171,7 @@ AWC_ENTRY_EXIT_DEBUG(" entry awc_issue_command_and_block "); - DOWN(&cmd->priv->command_semaphore); - -// save_flags(flags); -// cli(); + AWC_LOCK_COMMAND_ISSUING(cmd->priv); if (awc_command_busy_clear_wait(cmd->dev)) goto final; @@ -232,14 +240,12 @@ goto final; } - // restore_flags(flags); - UP(&cmd->priv->command_semaphore); + AWC_UNLOCK_COMMAND_ISSUING(cmd->priv); AWC_ENTRY_EXIT_DEBUG(" exit \n"); udelay(1); return 0; final: -// restore_flags(flags); - UP(&cmd->priv->command_semaphore); + AWC_UNLOCK_COMMAND_ISSUING(cmd->priv); AWC_ENTRY_EXIT_DEBUG(" BAD exit \n"); return -1; ; }; @@ -268,7 +274,7 @@ } - DOWN(&cmd->priv->command_semaphore); + AWC_LOCK_COMMAND_ISSUING(cmd->priv); if(awc_command_busy_clear_wait(cmd->dev)) goto final; @@ -304,7 +310,7 @@ AWC_ENTRY_EXIT_DEBUG(" exit \n"); return 0; final: - UP(&cmd->priv->command_semaphore); + AWC_UNLOCK_COMMAND_ISSUING(cmd->priv); AWC_ENTRY_EXIT_DEBUG(" BAD exit \n"); return -1; ; @@ -323,7 +329,7 @@ AWC_ENTRY_EXIT_DEBUG(" entry awc_issue_command_no_ack "); - DOWN(&priv->command_semaphore); + AWC_LOCK_COMMAND_ISSUING(priv); if (awc_command_busy_clear_wait(dev)) { printk("aironet4x00 no_ack command (reset) with stuck card \n"); @@ -351,11 +357,11 @@ if (awc_command_busy(dev->base_addr)) awc_event_ack_ClrStckCmdBsy(dev->base_addr); - UP(&priv->command_semaphore); + AWC_UNLOCK_COMMAND_ISSUING(priv); AWC_ENTRY_EXIT_DEBUG(" exit \n"); return 0; final: - UP(&priv->command_semaphore); + AWC_UNLOCK_COMMAND_ISSUING(priv); AWC_ENTRY_EXIT_DEBUG(" BAD exit \n"); return -1; ; }; @@ -383,7 +389,8 @@ if (!cmd->bap || !(cmd->lock_state & (AWC_BAP_SEMALOCKED |AWC_BAP_LOCKED))) DEBUG(1,"no bap or bap not locked cmd %d !!", cmd->command); - + if (bap_setup_spinlock) + my_spin_lock_irqsave(&cmd->priv->bap_setup_spinlock,cmd->priv->bap_setup_spinlock_flags); status = AWC_IN(cmd->bap->offset); if (status & ~0x2000 ){ @@ -412,14 +419,14 @@ // AWC_OUT(cmd->bap->offset, 0x800); } -// save_flags(flags); -// cli(); + save_flags(flags); + cli(); AWC_OUT(cmd->bap->select, cmd->rid); WAIT61x3; AWC_OUT(cmd->bap->offset, cmd->offset); -// restore_flags(flags); + restore_flags(flags); WAIT61x3; @@ -431,11 +438,11 @@ if ( cmd->priv->sleeping_bap) udelay(bap_sleep); if (cmd->priv->ejected) - return -1; + goto ejected_unlock; udelay(1); if (cycles > 10000) { printk(KERN_CRIT "deadlock in bap\n"); - return AWC_ERROR; + goto return_AWC_ERROR; }; status = AWC_IN(cmd->bap->offset); if (status & AWC_BAP_BUSY) { @@ -464,7 +471,7 @@ if (jiffies - jiff > 1 ) { AWC_ENTRY_EXIT_DEBUG(" BAD BUSY exit \n"); awc_dump_registers(cmd->dev); - return AWC_ERROR; + goto return_AWC_ERROR; } continue; } @@ -478,7 +485,7 @@ udelay(bap_sleep_after_setup); // success - return AWC_SUCCESS; + goto return_AWC_SUCCESS; } if (status & AWC_BAP_ERR) { @@ -486,7 +493,7 @@ // invalid rid or offset printk(KERN_ERR "bap setup error bit set for rid %x offset %x \n",cmd->rid,cmd->offset); awc_dump_registers(cmd->dev); - return AWC_ERROR; + goto return_AWC_ERROR; } if ( cmd->priv->sleeping_bap) udelay(bap_sleep); @@ -505,13 +512,31 @@ if (! (status &(AWC_BAP_ERR |AWC_BAP_DONE |AWC_BAP_BUSY))){ printk("aironet4500: bap setup lock without any status bits set"); awc_dump_registers(cmd->dev); - return AWC_ERROR; + goto return_AWC_ERROR; }; } - + AWC_ENTRY_EXIT_DEBUG(" WE MUST NOT BE HERE exit \n"); + +ejected_unlock: + if (bap_setup_spinlock) + my_spin_unlock_irqrestore(&cmd->priv->bap_setup_spinlock,cmd->priv->bap_setup_spinlock_flags); + AWC_ENTRY_EXIT_DEBUG(" ejected_unlock_exit \n"); + return -1; + +return_AWC_ERROR: + if (bap_setup_spinlock) + my_spin_unlock_irqrestore(&cmd->priv->bap_setup_spinlock,cmd->priv->bap_setup_spinlock_flags); + AWC_ENTRY_EXIT_DEBUG(" AWC_ERROR_exit \n"); + return AWC_ERROR; + +return_AWC_SUCCESS: + if (bap_setup_spinlock) + my_spin_unlock_irqrestore(&cmd->priv->bap_setup_spinlock,cmd->priv->bap_setup_spinlock_flags); + AWC_ENTRY_EXIT_DEBUG(" exit \n"); + return AWC_SUCCESS; } @@ -683,15 +708,13 @@ sleep_state = cmd.priv->sleeping_bap ; cmd.priv->sleeping_bap = 1; udelay(500); - if (awc_issue_command_and_block(&cmd)) goto final; AWC_BAP_LOCK_NOT_CLI(cmd); + if (awc_issue_command_and_block(&cmd)) goto final; udelay(1); if (awc_bap_setup(&cmd)) goto final; udelay(1); if (awc_bap_read(&cmd)) goto final; cmd.priv->sleeping_bap = sleep_state; - AWC_BAP_UNLOCK(cmd); - AWC_RELEASE_COMMAND(cmd); AWC_ENTRY_EXIT_DEBUG(" exit \n"); @@ -728,8 +751,8 @@ udelay(10); cmd.command=0x121; if (awc_issue_command_and_block(&cmd)) goto final; - AWC_BAP_UNLOCK(cmd); cmd.priv->sleeping_bap = sleep_state; + AWC_RELEASE_COMMAND(cmd); AWC_ENTRY_EXIT_DEBUG(" exit \n"); return 0; @@ -755,15 +778,13 @@ cmd.priv->sleeping_bap = 1; udelay(500); - if (awc_issue_command_and_block(&cmd)) goto final; AWC_BAP_LOCK_NOT_CLI(cmd); + if (awc_issue_command_and_block(&cmd)) goto final; if (awc_bap_setup(&cmd)) goto final; if (awc_bap_read(&cmd)) goto final; cmd.priv->sleeping_bap = sleep_state; - AWC_BAP_UNLOCK(cmd); - AWC_RELEASE_COMMAND(cmd); AWC_ENTRY_EXIT_DEBUG(" exit \n"); @@ -793,13 +814,13 @@ cmd.priv->sleeping_bap = 1; udelay(500); - if (awc_issue_command_and_block(&cmd)) goto final; AWC_BAP_LOCK_NOT_CLI(cmd); + + if (awc_issue_command_and_block(&cmd)) goto final; if (awc_bap_setup(&cmd)) goto final; if (awc_bap_write(&cmd)) goto final; cmd.priv->sleeping_bap = sleep_state; - AWC_BAP_UNLOCK(cmd); cmd.command=0x121; udelay(500); @@ -833,7 +854,9 @@ AWC_ENTRY_EXIT_DEBUG(" entry awc_issue_blocking_command "); AWC_INIT_COMMAND(AWC_NOT_CLI,cmd,dev,comm,0, 0, 0, 0 ,0 ); - + + AWC_BAP_LOCK_NOT_CLI(cmd); + if (awc_issue_command_and_block(&cmd)) goto final; @@ -1005,6 +1028,8 @@ DEBUG(32,"in %x large buffers ",cmd.priv->large_buff_mem / (dev->mtu + AWC_TX_HEAD_SIZE + 8) ); k=0;tot=0; + AWC_BAP_LOCK_NOT_CLI(cmd); + while (k < cmd.priv->large_buff_mem / (dev->mtu + AWC_TX_HEAD_SIZE + 8) ) { fid = kmalloc(sizeof(struct awc_fid),GFP_KERNEL ); @@ -1097,6 +1122,8 @@ AWC_INIT_COMMAND(AWC_NOT_CLI,cmd,dev,0x0C,0, 0,0,0,NULL); + AWC_BAP_LOCK_NOT_CLI(cmd); + if (fid->u.tx.fid){ fid_handle = cmd.par0 = fid->u.tx.fid; fid->u.tx.fid = 0; @@ -1959,7 +1986,7 @@ DEBUG(128, "rx payload read %x \n",rx_buff->u.rx.ieee_802_3.payload_length); }; - AWC_BAP_UNLOCK(cmd); + AWC_RELEASE_COMMAND(cmd); DEBUG(128,"\n payload hdr %x ",rx_buff->u.rx.ieee_802_3.status ); if (awc_debug && rx_buff->u.rx.payload) @@ -1968,8 +1995,6 @@ awc_802_11_router_rx(dev,rx_buff); - AWC_RELEASE_COMMAND(cmd); -// awc_event_ack_Rx(dev->base_addr); AWC_ENTRY_EXIT_DEBUG(" exit \n"); return 0; final: @@ -1977,7 +2002,6 @@ awc_802_11_failed_rx_copy(dev,rx_buff); // if (skb) dev_kfree_skb(skb, FREE_WRITE); AWC_RELEASE_COMMAND(cmd); -// awc_event_ack_Rx(dev->base_addr); AWC_ENTRY_EXIT_DEBUG(" BAD exit \n"); return -1; ; @@ -2089,27 +2113,31 @@ cmd.len = tx_buff->pkt_len; if (awc_bap_write(&cmd)) goto final; + AWC_RELEASE_COMMAND(cmd); +// locking probs, these two lines below and above, swithc order + if (awc_issue_command_and_block(&cmd)) goto final_unlocked; - - AWC_BAP_UNLOCK(cmd); - if (awc_issue_command_and_block(&cmd)) goto final; -// if (awc_issue_command(&cmd)) goto final; tx_buff->transmit_start_time = jiffies; awc_802_11_after_tx_packet_to_card_write(dev,tx_buff); // issue the transmit command - AWC_RELEASE_COMMAND(cmd); AWC_ENTRY_EXIT_DEBUG(" exit \n"); return 0; final: - awc_802_11_after_failed_tx_packet_to_card_write(dev,tx_buff); - + awc_802_11_after_failed_tx_packet_to_card_write(dev,tx_buff); printk(KERN_CRIT "%s awc tx command failed \n",dev->name); AWC_RELEASE_COMMAND(cmd); AWC_ENTRY_EXIT_DEBUG(" BAD exit \n"); return -1; ; + + final_unlocked: + awc_802_11_after_failed_tx_packet_to_card_write(dev,tx_buff); + printk(KERN_CRIT "%s awc tx command failed \n",dev->name); + AWC_ENTRY_EXIT_DEBUG(" BAD exit \n"); + return -1; ; + } @@ -2141,12 +2169,11 @@ AWC_BAP_LOCK_NOT_CLI(cmd); if (awc_bap_setup(&cmd)) goto final; if (awc_bap_read(&cmd)) goto final; - AWC_BAP_UNLOCK(cmd); + AWC_RELEASE_COMMAND(cmd); awc_802_11_after_tx_complete(dev,fid); - AWC_RELEASE_COMMAND(cmd); AWC_ENTRY_EXIT_DEBUG(" exit \n"); return 0; @@ -2202,7 +2229,7 @@ dev->start = 0; if (priv->command_semaphore_on){ priv->command_semaphore_on--; - UP(&priv->command_semaphore); + AWC_UNLOCK_COMMAND_ISSUING(priv); } priv->tx_chain_active =0; goto bad_end; @@ -2219,7 +2246,7 @@ if (priv->command_semaphore_on){ priv->command_semaphore_on--; - UP(&priv->command_semaphore); + AWC_UNLOCK_COMMAND_ISSUING(priv); } } }; @@ -2269,10 +2296,10 @@ // u16 ints_to_ack =0; struct awc_fid * fid = NULL; // int interrupt_reenter = 0; - unsigned long flags; +// unsigned long flags; - save_flags(flags); - cli(); +// save_flags(flags); +// cli(); // disable_irq(dev->irq); DEBUG(2," entering interrupt handler %s ",dev->name); @@ -2420,6 +2447,7 @@ printk(KERN_ERR "No tx fid when tx int active\n"); fid = awc_tx_fid_lookup_and_remove(dev, tx_fid); + if (fid) { if (priv->process_tx_results) { awc_fid_queue_push_tail(&priv->tx_post_process,fid); @@ -2491,21 +2519,21 @@ //end_here: // enable_irq(dev->irq); - restore_flags(flags); +// restore_flags(flags); return 0; reenter_end_here: AWC_ENTRY_EXIT_DEBUG(" reenter-bad end exit \n"); // enable_irq(dev->irq); - restore_flags(flags); +// restore_flags(flags); return 0; bad_end: dev->interrupt = 0; AWC_ENTRY_EXIT_DEBUG(" bad_end exit \n"); // enable_irq(dev->irq); - restore_flags(flags); +// restore_flags(flags); return -1; @@ -2516,7 +2544,7 @@ struct NET_DEVICE * aironet4500_devices[MAX_AWCS] = {NULL,NULL,NULL,NULL}; -static int awc_debug = 0; // 0xffffff; +static int awc_debug = 0; // 0xffffff; static int p802_11_send = 0; // 1 static int awc_process_tx_results = 0; @@ -2632,8 +2660,15 @@ DEBUG(2, "%s: awc_init \n", dev->name); - - + /* both_bap_lock decreases performance about 15% + * but without it card gets screwed up + */ +#ifdef CONFIG_SMP + if(smp_num_cpus > 1){ + both_bap_lock = 1; + bap_setup_spinlock = 1; + } +#endif //awc_dump_registers(dev); if (adhoc & !max_mtu) @@ -2749,7 +2784,7 @@ // here we go, bad aironet memset(&priv->SSIDs,0,sizeof(priv->SSIDs)); - memset(&priv->queues_lock,0,sizeof(priv->queues_lock)); + my_spin_lock_init(&priv->queues_lock); priv->SSIDs.ridLen =0; if (!SSID) { @@ -2807,21 +2842,29 @@ memset(priv, 0, sizeof(struct awc_private)); + my_spin_lock_init(&priv->queues_lock); + priv->bap0.select = dev->base_addr + awc_Select0_register; priv->bap0.offset = dev->base_addr + awc_Offset0_register; priv->bap0.data = dev->base_addr + awc_Data0_register; priv->bap0.lock = 0; priv->bap0.status = 0; + my_spin_lock_init(&priv->bap0.spinlock); init_MUTEX(&priv->bap0.sem); priv->bap1.select = dev->base_addr + awc_Select1_register; priv->bap1.offset = dev->base_addr + awc_Offset1_register; priv->bap1.data = dev->base_addr + awc_Data1_register; priv->bap1.lock = 0; priv->bap1.status = 0; + my_spin_lock_init(&priv->bap1.spinlock); init_MUTEX(&priv->bap1.sem); priv->sleeping_bap = 1; - init_MUTEX(&priv->command_semaphore); +//spinlock now init_MUTEX(&priv->command_semaphore); + my_spin_lock_init(&priv->command_issuing_spinlock); + my_spin_lock_init(&priv->both_bap_spinlock); + my_spin_lock_init(&priv->bap_setup_spinlock); + priv->command_semaphore_on = 0; priv->unlock_command_postponed = 0; priv->immediate_bh.next = NULL; @@ -2927,11 +2970,11 @@ udelay(10000); - DOWN(&priv->command_semaphore); + AWC_LOCK_COMMAND_ISSUING(priv); MOD_DEC_USE_COUNT; - UP(&priv->command_semaphore); + AWC_UNLOCK_COMMAND_ISSUING(priv); return 0; } @@ -2965,7 +3008,7 @@ struct awc_private *priv = (struct awc_private *)dev->priv; int retval = 0; - unsigned long flags; +// unsigned long flags; struct awc_fid * fid = NULL; int cnt=0; @@ -2980,8 +3023,8 @@ /* Transmitter timeout, serious problems. */ if (test_and_set_bit( 0, (void *) &dev->tbusy) ) { if (jiffies - dev->trans_start > 3* HZ ){ - save_flags(flags); - cli(); + // save_flags(flags); + // cli(); fid = priv->tx_in_transmit.head; cnt = 0; while (fid){ @@ -2998,12 +3041,12 @@ fid = fid->next; if (cnt++ > 200) { printk("bbb in awc_fid_queue\n"); - restore_flags(flags); + // restore_flags(flags); return -1; }; } - restore_flags(flags); + //restore_flags(flags); //debug =0x8; }; if (jiffies - dev->trans_start >= (5* HZ) ) { @@ -3092,7 +3135,7 @@ struct enet_statistics *awc_get_stats(struct NET_DEVICE *dev) { struct awc_private *priv = (struct awc_private *)dev->priv; - unsigned long flags; +// unsigned long flags; // int cnt = 0; // int unlocked_stats_in_interrupt=0; @@ -3101,11 +3144,11 @@ if (!dev->start) { return 0; } - save_flags(flags); - cli(); +// save_flags(flags); +// cli(); if (awc_full_stats) awc_readrid_dir(dev, &priv->rid_dir[9]); - restore_flags(flags); +// restore_flags(flags); // the very following is the very wrong very probably if (awc_full_stats){ @@ -3134,7 +3177,7 @@ int awc_change_mtu(struct NET_DEVICE *dev, int new_mtu){ // struct awc_private *priv = (struct awc_private *)dev->priv; - unsigned long flags; +// unsigned long flags; if ((new_mtu < 256 ) || (new_mtu > 2312) || (max_mtu && new_mtu > max_mtu) ) return -EINVAL; @@ -3144,14 +3187,14 @@ }; if (dev->mtu != new_mtu) { - save_flags(flags); - cli(); +// save_flags(flags); +// cli(); awc_disable_MAC(dev); awc_tx_dealloc(dev); dev->mtu = new_mtu; awc_tx_alloc(dev); awc_enable_MAC(dev); - restore_flags(flags); +// restore_flags(flags); printk("%s mtu has been changed to %d \n ",dev->name,dev->mtu); @@ -3208,7 +3251,7 @@ { // unsigned long flags; -// debug = awc_debug; + printk(KERN_INFO"%s", aironet4500_core_version); return 0; diff -u --recursive --new-file v2.3.33/linux/drivers/net/aironet4500_proc.c linux/drivers/net/aironet4500_proc.c --- v2.3.33/linux/drivers/net/aironet4500_proc.c Wed Dec 8 14:11:26 1999 +++ linux/drivers/net/aironet4500_proc.c Sat Dec 18 15:34:29 1999 @@ -2,7 +2,7 @@ * Aironet 4500 Pcmcia driver * * Elmer Joandi, Januar 1999 - * Copyright Elmer Joandi, all rights restricted + * Copyright GPL * * * Revision 0.1 ,started 30.12.1998 @@ -379,6 +379,8 @@ {4, "sleep_before_command" , &sleep_before_command, sizeof(sleep_before_command), 0600,NULL, proc_dointvec}, {5, "bap_sleep_before_write" , &bap_sleep_before_write, sizeof(bap_sleep_before_write), 0600,NULL, proc_dointvec}, {6, "sleep_in_command" , &sleep_in_command , sizeof(sleep_in_command), 0600,NULL, proc_dointvec}, + {7, "both_bap_lock" , &both_bap_lock , sizeof(both_bap_lock), 0600,NULL, proc_dointvec}, + {8, "bap_setup_spinlock" , &bap_setup_spinlock , sizeof(bap_setup_spinlock), 0600,NULL, proc_dointvec}, {0} }; diff -u --recursive --new-file v2.3.33/linux/drivers/net/arlan-proc.c linux/drivers/net/arlan-proc.c --- v2.3.33/linux/drivers/net/arlan-proc.c Mon Oct 4 15:49:29 1999 +++ linux/drivers/net/arlan-proc.c Sat Dec 18 15:34:29 1999 @@ -11,7 +11,6 @@ /* void enableReceive(struct net_device* dev); */ -static int arlan_command(struct net_device * dev, int command); #define ARLAN_STR_SIZE 0x2ff0 @@ -186,7 +185,7 @@ return "type A672T"; } } - +#ifdef ARLAN_DEBUGING static void arlan_print_diagnostic_info(struct net_device *dev) { int i; @@ -320,7 +319,6 @@ return 0; } - static int arlan_setup_card_by_book(struct net_device *dev) { u_char irqLevel, configuredStatusFlag; @@ -396,7 +394,7 @@ return 0; /* no errors */ } - +#endif #ifdef ARLAN_PROC_INTERFACE #ifdef ARLAN_PROC_SHM_DUMP @@ -820,7 +818,15 @@ #define CTBLN(num,card,nam) \ {num , #nam, &(arlan_conf[card].nam), \ sizeof(int), 0600, NULL, &proc_dointvec} +#ifdef ARLAN_DEBUGING +#define ARLAN_PROC_DEBUG_ENTRIES {48, "entry_exit_debug", &arlan_entry_and_exit_debug, \ + sizeof(int), 0600, NULL, &proc_dointvec},\ + {49, "debug", &arlan_debug, \ + sizeof(int), 0600, NULL, &proc_dointvec}, +#else +#define ARLAN_PROC_DEBUG_ENTRIES +#endif #define ARLAN_SYSCTL_TABLE_TOTAL(cardNo)\ CTBLN(1,cardNo,spreadingCode),\ @@ -871,10 +877,7 @@ CTBLN(45,cardNo,radioType),\ CTBLN(46,cardNo,writeEEPROM),\ CTBLN(47,cardNo,writeRadioType),\ - {48, "entry_exit_debug", &arlan_entry_and_exit_debug, \ - sizeof(int), 0600, NULL, &proc_dointvec},\ - {49, "debug", &arlan_debug, \ - sizeof(int), 0600, NULL, &proc_dointvec},\ + ARLAN_PROC_DEBUG_ENTRIES\ CTBLN(50,cardNo,in_speed),\ CTBLN(51,cardNo,out_speed),\ CTBLN(52,cardNo,in_speed10),\ @@ -1010,7 +1013,8 @@ }; #endif -static int mmtu = 1234; + +// static int mmtu = 1234; static ctl_table arlan_root_table[] = { @@ -1019,11 +1023,11 @@ }; /* Make sure that /proc/sys/dev is there */ -static ctl_table arlan_device_root_table[] = -{ - {CTL_DEV, "dev", NULL, 0, 0555, arlan_root_table}, - {0} -}; +//static ctl_table arlan_device_root_table[] = +//{ +// {CTL_DEV, "dev", NULL, 0, 0555, arlan_root_table}, +// {0} +//}; diff -u --recursive --new-file v2.3.33/linux/drivers/net/arlan.c linux/drivers/net/arlan.c --- v2.3.33/linux/drivers/net/arlan.c Mon Oct 4 15:49:29 1999 +++ linux/drivers/net/arlan.c Sat Dec 18 15:34:29 1999 @@ -17,28 +17,32 @@ static int radioNodeId = radioNodeIdUNKNOWN; static char encryptionKey[12] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}; static char *siteName = siteNameUNKNOWN; -static int irq = irqUNKNOWN; static int mem = memUNKNOWN; -static int arlan_debug = debugUNKNOWN; +int arlan_debug = debugUNKNOWN; static int probe = probeUNKNOWN; static int numDevices = numDevicesUNKNOWN; -static int testMemory = testMemoryUNKNOWN; static int spreadingCode = spreadingCodeUNKNOWN; static int channelNumber = channelNumberUNKNOWN; static int channelSet = channelSetUNKNOWN; static int systemId = systemIdUNKNOWN; static int registrationMode = registrationModeUNKNOWN; -static int txScrambled = 1; static int keyStart = 0; -static int mdebug = 0; static int tx_delay_ms = 0; static int retries = 5; static int async = 1; static int tx_queue_len = 1; +static int arlan_EEPROM_bad = 0; +int arlan_entry_and_exit_debug = 0; + +#ifdef ARLAN_DEBUGING + static int arlan_entry_debug = 0; static int arlan_exit_debug = 0; -static int arlan_entry_and_exit_debug = 0; -static int arlan_EEPROM_bad = 0; +static int testMemory = testMemoryUNKNOWN; +static int irq = irqUNKNOWN; +static int txScrambled = 1; +static int mdebug = 0; +#endif #if LINUX_VERSION_CODE > 0x20100 MODULE_PARM(irq, "i"); @@ -336,11 +340,15 @@ } else if (priv->waiting_command_mask & ARLAN_COMMAND_RESET) { + priv->under_reset=1; + dev->tbusy = 1; + arlan_drop_tx(dev); if (priv->tx_command_given || priv->rx_command_given) { printk(KERN_ERR "%s: Reset under tx or rx command \n", dev->name); }; + dev->tbusy = 1; if (arlan_debug & ARLAN_DEBUG_RESET) printk(KERN_ERR "%s: Doing chip reset\n", dev->name); priv->lastReset = jiffies; @@ -398,15 +406,10 @@ { priv->waiting_command_mask &= ~ARLAN_COMMAND_CONF_WAIT; priv->waiting_command_mask |= ARLAN_COMMAND_RX; + priv->waiting_command_mask |= ARLAN_COMMAND_TBUSY_CLEAR; priv->card_polling_interval = HZ / 10; priv->tx_command_given = 0; priv->under_config = 0; - if (dev->tbusy || !dev->start) - { - dev->tbusy = 0; - dev->start = 1; - mark_bh(NET_BH); - }; } else { @@ -424,7 +427,7 @@ WRITESHMB(arlan->commandByte, ARLAN_COM_INT | ARLAN_COM_RX_ENABLE); WRITESHMB(arlan->commandParameter[0], conf->rxParameter); arlan_interrupt_lancpu(dev); - priv->rx_command_given; + priv->rx_command_given = 0; // mnjah, bad priv->last_rx_time = arlan_time(); priv->waiting_command_mask &= ~ARLAN_COMMAND_RX; priv->card_polling_interval = 1; @@ -432,6 +435,17 @@ else priv->card_polling_interval = 2; } + else if (priv->waiting_command_mask & ARLAN_COMMAND_TBUSY_CLEAR) + { + if ( !registrationBad(dev) && (dev->tbusy || !dev->start) ) + { + priv->waiting_command_mask &= ~ARLAN_COMMAND_TBUSY_CLEAR; + + dev->tbusy = 0; + dev->start = 1; + mark_bh(NET_BH); + }; + } else if (priv->waiting_command_mask & ARLAN_COMMAND_TX) { if (!test_and_set_bit(0, (void *) &priv->tx_command_given)) @@ -692,7 +706,7 @@ } - +#ifdef ARLAN_DEBUGING static void arlan_print_registers(struct net_device *dev, int line) { @@ -718,6 +732,7 @@ ARLAN_DEBUG_EXIT("arlan_print_registers"); } +#endif static int arlan_hw_tx(struct net_device *dev, char *buf, int length) @@ -1039,12 +1054,18 @@ volatile struct arlan_shmem *arlan = (struct arlan_shmem *) memaddr; ARLAN_DEBUG_ENTRY("arlan_check_fingerprint"); + if (check_mem_region(virt_to_phys((void *)memaddr),0x2000 )){ + // printk(KERN_WARNING "arlan: memory region %lx excluded from probing \n",virt_to_phys((void*)memaddr)); + return -ENODEV; + }; memcpy_fromio(tempBuf, arlan->textRegion, 29); tempBuf[30] = 0; /* check for card at this address */ - if (0 != strncmp(tempBuf, probeText, 29)) + if (0 != strncmp(tempBuf, probeText, 29)){ +// not release_mem_region(virt_to_phys((void*)memaddr),0x2000); return -ENODEV; + } // printk(KERN_INFO "arlan found at 0x%x \n",memaddr); ARLAN_DEBUG_EXIT("arlan_check_fingerprint"); @@ -1063,17 +1084,17 @@ ARLAN_DEBUG_ENTRY("arlan_probe_everywhere"); if (mem != 0 && numDevices == 1) /* Check a single specified location. */ { - if (arlan_probe_here(dev, mem) == 0) + if (arlan_probe_here(dev, (int) phys_to_virt( mem) ) == 0) return 0; else return -ENODEV; } - for (m = lastFoundAt + 0x2000; m <= 0xDE000; m += 0x2000) + for (m = (int)phys_to_virt(lastFoundAt) + 0x2000; m <= (int)phys_to_virt(0xDE000); m += 0x2000) { if (arlan_probe_here(dev, m) == 0) { found++; - lastFoundAt = m; + lastFoundAt = (int)virt_to_phys((void*)m); break; } probed++; @@ -1100,7 +1121,7 @@ ARLAN_DEBUG_ENTRY("arlan_find_devices"); if (mem != 0 && numDevices == 1) /* Check a single specified location. */ return 1; - for (m = 0xc000; m <= 0xDE000; m += 0x2000) + for (m =(int) phys_to_virt(0xc0000); m <=(int) phys_to_virt(0xDE000); m += 0x2000) { if (arlan_check_fingerprint(m) == 0) found++; @@ -1172,6 +1193,9 @@ printk(KERN_CRIT "init_etherdev failed "); return 0; } + + memset(dev->priv,0,sizeof(struct arlan_private)); + ((struct arlan_private *) dev->priv)->conf = kmalloc(sizeof(struct arlan_shmem), GFP_KERNEL); @@ -1199,6 +1223,9 @@ dev->set_multicast_list = arlan_set_multicast; dev->change_mtu = arlan_change_mtu; dev->set_mac_address = arlan_mac_addr; + dev->tbusy = 1; + dev->start = 0; + ((struct arlan_private *) dev->priv)->irq_test_done = 0; arlan_device[num] = dev; ((struct arlan_private *) arlan_device[num]->priv)->Conf = &(arlan_conf[num]); @@ -1221,7 +1248,7 @@ if (arlan_check_fingerprint(memaddr)) return -ENODEV; - printk(KERN_NOTICE "%s: Arlan found at %#5x, \n ", dev->name, memaddr); + printk(KERN_NOTICE "%s: Arlan found at %x, \n ", dev->name, (int) virt_to_phys((void*)memaddr)); if (!arlan_allocate_device(arlans_found, dev)) return -1; @@ -1261,14 +1288,13 @@ return ret; arlan = ((struct arlan_private *) dev->priv)->card; - if (request_irq(dev->irq, &arlan_interrupt, 0, dev->name, dev)) { printk(KERN_ERR "%s: unable to get IRQ %d .\n", dev->name, dev->irq); return -EAGAIN; } - arlan_command(dev, ARLAN_COMMAND_POWERUP | ARLAN_COMMAND_LONG_WAIT_NOW); + priv->bad = 0; priv->lastReset = 0; @@ -1279,15 +1305,10 @@ dev->tbusy = 1; priv->txOffset = 0; dev->interrupt = 0; - dev->start = 1; + dev->start = 0; dev->tx_queue_len = tx_queue_len; - init_timer(&priv->timer); - priv->timer.expires = jiffies + HZ / 10; - priv->timer.data = (unsigned long) dev; - priv->timer.function = &arlan_registration_timer; /* timer handler */ priv->interrupt_processing_active = 0; priv->command_lock = 0; - add_timer(&priv->timer); init_MUTEX(&priv->card_lock); myATOMIC_INIT(priv->card_users, 1); /* damn 2.0.33 */ @@ -1295,7 +1316,8 @@ priv->registrationLastSeen = jiffies; priv->txLast = 0; priv->tx_command_given = 0; - + priv->rx_command_given = 0; + priv->reRegisterExp = 1; priv->nof_tx = 0; priv->nof_tx_ack = 0; @@ -1306,6 +1328,16 @@ priv->Conf->registrationInterrupts = 1; dev->tbusy = 0; + init_timer(&priv->timer); + priv->timer.expires = jiffies + HZ / 10; + priv->timer.data = (unsigned long) dev; + priv->timer.function = &arlan_registration_timer; /* timer handler */ + + arlan_command(dev, ARLAN_COMMAND_POWERUP | ARLAN_COMMAND_LONG_WAIT_NOW); + udelay(200000); + dev->tbusy = 0; + dev->start = 1; + add_timer(&priv->timer); MOD_INC_USE_COUNT; #ifdef CONFIG_PROC_FS @@ -1898,29 +1930,31 @@ } ARLAN_DEBUG_ENTRY("arlan_close"); + del_timer(&priv->timer); + + arlan_command(dev, ARLAN_COMMAND_POWERDOWN); + IFDEBUG(ARLAN_DEBUG_STARTUP) printk(KERN_NOTICE "%s: Closing device\n", dev->name); priv->open_time = 0; dev->tbusy = 1; dev->start = 0; - del_timer(&priv->timer); free_irq(dev->irq, dev); - MOD_DEC_USE_COUNT; ARLAN_DEBUG_EXIT("arlan_close"); return 0; } - +#ifdef ARLAN_DEBUGING static long alignLong(volatile u_char * ptr) { long ret; memcpy_fromio(&ret, (void *) ptr, 4); return ret; } - +#endif /* * Get the current statistics. @@ -2041,6 +2075,7 @@ } if (probe) arlan_probe_everywhere(arlan_device[i]); +// arlan_command(arlan_device[i], ARLAN_COMMAND_POWERDOWN ); } printk(KERN_INFO "Arlan driver %s\n", arlan_version); ARLAN_DEBUG_EXIT("init_module"); @@ -2060,6 +2095,9 @@ { if (arlan_device[i]) { + arlan_command(arlan_device[i], ARLAN_COMMAND_POWERDOWN ); + +// release_mem_region(virt_to_phys(arlan_device[i]->mem_start), 0x2000 ); unregister_netdev(arlan_device[i]); if (arlan_device[i]->priv) { diff -u --recursive --new-file v2.3.33/linux/drivers/net/arlan.h linux/drivers/net/arlan.h --- v2.3.33/linux/drivers/net/arlan.h Wed Aug 18 11:36:45 1999 +++ linux/drivers/net/arlan.h Sat Dec 18 15:34:29 1999 @@ -29,7 +29,7 @@ #include -#define DEBUG 1 +//#define ARLAN_DEBUGING 1 #define ARLAN_PROC_INTERFACE #define MAX_ARLANS 4 /* not more than 4 ! */ @@ -46,13 +46,14 @@ #endif extern struct net_device *arlan_device[MAX_ARLANS]; -static int arlan_debug; -static char * siteName; -static int arlan_entry_debug; -static int arlan_exit_debug; -static int arlan_entry_and_exit_debug; -static int testMemory; -static const char* arlan_version; +extern int arlan_debug; +extern char * siteName; +extern int arlan_entry_debug; +extern int arlan_exit_debug; +extern int arlan_entry_and_exit_debug; +extern int testMemory; +extern const char* arlan_version; +extern int arlan_command(struct net_device * dev, int command); #define SIDUNKNOWN -1 #define radioNodeIdUNKNOWN -1 @@ -75,7 +76,8 @@ #define IFDEBUG( L ) if ( (L) & arlan_debug ) #define ARLAN_FAKE_HDR_LEN 12 -#ifdef DEBUG +#ifdef ARLAN_DEBUGING + #define DEBUG 1 #define ARLAN_ENTRY_EXIT_DEBUGING 1 #define ARLAN_DEBUG(a,b) printk(KERN_DEBUG a, b) #else @@ -532,26 +534,27 @@ -#define ARLAN_COMMAND_RX 0x00001 -#define ARLAN_COMMAND_NOOP 0x00002 -#define ARLAN_COMMAND_NOOPINT 0x00004 -#define ARLAN_COMMAND_TX 0x00008 -#define ARLAN_COMMAND_CONF 0x00010 -#define ARLAN_COMMAND_RESET 0x00020 -#define ARLAN_COMMAND_TX_ABORT 0x00040 -#define ARLAN_COMMAND_RX_ABORT 0x00080 -#define ARLAN_COMMAND_POWERDOWN 0x00100 -#define ARLAN_COMMAND_POWERUP 0x00200 -#define ARLAN_COMMAND_SLOW_POLL 0x00400 -#define ARLAN_COMMAND_ACTIVATE 0x00800 -#define ARLAN_COMMAND_INT_ACK 0x01000 -#define ARLAN_COMMAND_INT_ENABLE 0x02000 -#define ARLAN_COMMAND_WAIT_NOW 0x04000 -#define ARLAN_COMMAND_LONG_WAIT_NOW 0x08000 -#define ARLAN_COMMAND_STANDBY 0x10000 -#define ARLAN_COMMAND_INT_RACK 0x20000 -#define ARLAN_COMMAND_INT_RENABLE 0x40000 -#define ARLAN_COMMAND_CONF_WAIT 0x80000 +#define ARLAN_COMMAND_RX 0x000001 +#define ARLAN_COMMAND_NOOP 0x000002 +#define ARLAN_COMMAND_NOOPINT 0x000004 +#define ARLAN_COMMAND_TX 0x000008 +#define ARLAN_COMMAND_CONF 0x000010 +#define ARLAN_COMMAND_RESET 0x000020 +#define ARLAN_COMMAND_TX_ABORT 0x000040 +#define ARLAN_COMMAND_RX_ABORT 0x000080 +#define ARLAN_COMMAND_POWERDOWN 0x000100 +#define ARLAN_COMMAND_POWERUP 0x000200 +#define ARLAN_COMMAND_SLOW_POLL 0x000400 +#define ARLAN_COMMAND_ACTIVATE 0x000800 +#define ARLAN_COMMAND_INT_ACK 0x001000 +#define ARLAN_COMMAND_INT_ENABLE 0x002000 +#define ARLAN_COMMAND_WAIT_NOW 0x004000 +#define ARLAN_COMMAND_LONG_WAIT_NOW 0x008000 +#define ARLAN_COMMAND_STANDBY 0x010000 +#define ARLAN_COMMAND_INT_RACK 0x020000 +#define ARLAN_COMMAND_INT_RENABLE 0x040000 +#define ARLAN_COMMAND_CONF_WAIT 0x080000 +#define ARLAN_COMMAND_TBUSY_CLEAR 0x100000 #define ARLAN_COMMAND_CLEAN_AND_CONF (ARLAN_COMMAND_TX_ABORT\ | ARLAN_COMMAND_RX_ABORT\ | ARLAN_COMMAND_CONF) diff -u --recursive --new-file v2.3.33/linux/drivers/net/hp100.c linux/drivers/net/hp100.c --- v2.3.33/linux/drivers/net/hp100.c Tue Aug 31 17:29:14 1999 +++ linux/drivers/net/hp100.c Thu Dec 16 13:57:05 1999 @@ -192,8 +192,8 @@ struct pci_dev *pci_dev; #endif short mem_mapped; /* memory mapped access */ - u_int *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */ - u_int *mem_ptr_phys; /* physical memory mapped area */ + u32 *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */ + unsigned long mem_ptr_phys; /* physical memory mapped area */ short lan_type; /* 10Mb/s, 100Mb/s or -1 (error) */ int hub_status; /* was login to hub successful? */ u_char mac1_mode; @@ -532,7 +532,8 @@ u_int memory_size = 0, virt_memory_size = 0; u_short local_mode, lsw; short mem_mapped; - u_int *mem_ptr_phys, *mem_ptr_virt; + unsigned long mem_ptr_phys; + u32 **mem_ptr_virt; struct hp100_private *lp; struct hp100_eisa_id *eid; @@ -722,21 +723,22 @@ /* Check for shared memory on the card, eventually remap it */ hp100_page( HW_MAP ); mem_mapped = (( hp100_inw( OPTION_LSW ) & ( HP100_MEM_EN ) ) != 0); - mem_ptr_phys = mem_ptr_virt = NULL; + mem_ptr_phys = 0UL; + mem_ptr_virt = NULL; memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07)); virt_memory_size = 0; /* For memory mapped or busmaster mode, we want the memory address */ if ( mem_mapped || (local_mode==1)) { - mem_ptr_phys = (u_int *)( hp100_inw( MEM_MAP_LSW ) | + mem_ptr_phys = ( hp100_inw( MEM_MAP_LSW ) | ( hp100_inw( MEM_MAP_MSW ) << 16 ) ); - (u_int)mem_ptr_phys &= ~0x1fff; /* 8k alignment */ + mem_ptr_phys &= ~0x1fff; /* 8k alignment */ - if ( bus == HP100_BUS_ISA && ( (u_long)mem_ptr_phys & ~0xfffff ) != 0 ) + if ( bus == HP100_BUS_ISA && (mem_ptr_phys & ~0xfffff ) != 0 ) { printk("hp100: %s: Can only use programmed i/o mode.\n", dev->name); - mem_ptr_phys = NULL; + mem_ptr_phys = 0; mem_mapped = 0; local_mode=3; /* Use programmed i/o */ } @@ -745,7 +747,7 @@ /* However in slave mode we need to remap high (>1GB) card memory */ if(local_mode!=1) /* = not busmaster */ { - if ( bus == HP100_BUS_PCI && mem_ptr_phys >= (u_int *)0x100000 ) + if ( bus == HP100_BUS_PCI && mem_ptr_phys >= 0x100000 ) { /* We try with smaller memory sizes, if ioremap fails */ for(virt_memory_size = memory_size; virt_memory_size>16383; virt_memory_size>>=1) @@ -753,13 +755,13 @@ if((mem_ptr_virt=ioremap((u_long)mem_ptr_phys,virt_memory_size))==NULL) { #ifdef HP100_DEBUG - printk( "hp100: %s: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", dev->name, virt_memory_size, (u_long)mem_ptr_phys ); + printk( "hp100: %s: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", dev->name, virt_memory_size, mem_ptr_phys ); #endif } else { #ifdef HP100_DEBUG - printk( "hp100: %s: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", dev->name, virt_memory_size, (u_long)mem_ptr_phys, (u_long)mem_ptr_virt); + printk( "hp100: %s: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", dev->name, virt_memory_size, mem_ptr_phys, (u_long)mem_ptr_virt); #endif break; } @@ -779,7 +781,8 @@ if(local_mode==3) /* io mapped forced */ { mem_mapped = 0; - mem_ptr_phys = mem_ptr_virt = NULL; + mem_ptr_phys = 0; + mem_ptr_virt = NULL; printk("hp100: %s: Using (slow) programmed i/o mode.\n", dev->name); } @@ -908,15 +911,15 @@ if ( lp->mode==2 ) /* memory mapped */ { printk( "hp100: %s: Memory area at 0x%lx-0x%lx", - dev->name,(u_long)mem_ptr_phys, - ((u_long)mem_ptr_phys+(mem_ptr_phys>(u_int *)0x100000?(u_long)lp->memory_size:16*1024))-1 ); + dev->name,mem_ptr_phys, + (mem_ptr_phys+(mem_ptr_phys>0x100000?(u_long)lp->memory_size:16*1024))-1 ); if ( mem_ptr_virt ) printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt ); printk( ".\n" ); /* Set for info when doing ifconfig */ - dev->mem_start = (u_long)mem_ptr_phys; - dev->mem_end = (u_long)mem_ptr_phys+(u_long)lp->memory_size; + dev->mem_start = mem_ptr_phys; + dev->mem_end = mem_ptr_phys+lp->memory_size; } printk( "hp100: %s: ", dev->name ); if ( lp->lan_type != HP100_LAN_ERR ) @@ -1935,9 +1938,9 @@ else { /* Note: The J2585B needs alignment to 32bits here! */ - memcpy_toio( lp->mem_ptr_phys, skb->data, (skb->len + 3) & ~3 ); + isa_memcpy_toio( lp->mem_ptr_phys, skb->data, (skb->len + 3) & ~3 ); if ( !ok_flag ) - memset_io( lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len ); + isa_memset_io( lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len ); } } else /* programmed i/o */ @@ -2019,9 +2022,9 @@ if( lp->mode==2 ) /* memory mapped mode */ { if ( lp->mem_ptr_virt ) /* if memory was remapped */ - header = *(__u32 *)lp->mem_ptr_virt; + header = readl(lp->mem_ptr_virt); else - header = readl( lp->mem_ptr_phys ); + header = isa_readl( lp->mem_ptr_phys ); } else /* programmed i/o */ header = hp100_inl( DATA32 ); @@ -2060,7 +2063,7 @@ memcpy( ptr, lp->mem_ptr_virt, pkt_len ); /* Note alignment to 32bit transfers */ else - memcpy_fromio( ptr, lp->mem_ptr_phys, pkt_len ); + isa_memcpy_fromio( ptr, lp->mem_ptr_phys, pkt_len ); } else /* io mapped */ insl( ioaddr + HP100_REG_DATA32, ptr, pkt_len >> 2 ); diff -u --recursive --new-file v2.3.33/linux/drivers/net/pcmcia/Config.in linux/drivers/net/pcmcia/Config.in --- v2.3.33/linux/drivers/net/pcmcia/Config.in Wed Dec 8 14:11:26 1999 +++ linux/drivers/net/pcmcia/Config.in Mon Dec 20 15:07:08 1999 @@ -22,9 +22,12 @@ dep_tristate ' SMC EPIC CardBus support' CONFIG_PCMCIA_EPIC100 m fi - dep_tristate ' Aviator/Raytheon 2.4MHz wireless support' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA - dep_tristate ' Xircom Netwave AirSurfer wireless support' CONFIG_PCMCIA_NETWAVE $CONFIG_PCMCIA - dep_tristate ' AT&T/Lucent Wavelan wireless support' CONFIG_PCMCIA_WAVELAN $CONFIG_PCMCIA + bool 'Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO + if [ "$CONFIG_NET_PCMCIA_RADIO" = "y" ]; then + dep_tristate ' Aviator/Raytheon 2.4MHz wireless support' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA + dep_tristate ' Xircom Netwave AirSurfer wireless support' CONFIG_PCMCIA_NETWAVE $CONFIG_PCMCIA + dep_tristate ' AT&T/Lucent Wavelan wireless support' CONFIG_PCMCIA_WAVELAN $CONFIG_PCMCIA + fi fi if [ "$CONFIG_PCMCIA_3C589" = "y" -o "$CONFIG_PCMCIA_3C574" = "y" -o \ diff -u --recursive --new-file v2.3.33/linux/drivers/net/pcmcia/netwave_cs.c linux/drivers/net/pcmcia/netwave_cs.c --- v2.3.33/linux/drivers/net/pcmcia/netwave_cs.c Thu Nov 18 20:25:37 1999 +++ linux/drivers/net/pcmcia/netwave_cs.c Mon Dec 20 15:07:08 1999 @@ -61,9 +61,9 @@ #include #include -#ifdef CONFIG_NET_RADIO +#ifdef CONFIG_NET_PCMCIA_RADIO #include -#endif +#endif /* CONFIG_NET_PCMCIA_RADIO */ #include #include diff -u --recursive --new-file v2.3.33/linux/drivers/net/pcmcia/ray_cs.c linux/drivers/net/pcmcia/ray_cs.c --- v2.3.33/linux/drivers/net/pcmcia/ray_cs.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/net/pcmcia/ray_cs.c Mon Dec 20 15:07:08 1999 @@ -51,18 +51,16 @@ #include #include -#ifdef HAS_WIRELESS_EXTENSIONS +#ifdef CONFIG_NET_PCMCIA_RADIO #include -#if WIRELESS_EXT < 8 -#warning "Wireless extension v8 or newer required" -#endif /* WIRELESS_EXT < 8 */ + /* Warning : these stuff will slow down the driver... */ #define WIRELESS_SPY /* Enable spying addresses */ /* Definitions we need for spy */ typedef struct iw_statistics iw_stats; typedef struct iw_quality iw_qual; typedef u_char mac_addr[ETH_ALEN]; /* Hardware address */ -#endif /* HAS_WIRELESS_EXTENSIONS */ +#endif /* CONFIG_NET_PCMCIA_RADIO */ #include "rayctl.h" #include "ray_cs.h" @@ -109,7 +107,7 @@ unsigned char *data); static void untranslate(ray_dev_t *local, struct sk_buff *skb, int len); #if WIRELESS_EXT > 7 /* If wireless extension exist in the kernel */ -static iw_stats * ray_get_wireless_stats(struct device * dev); +static iw_stats * ray_get_wireless_stats(struct net_device * dev); #endif /* WIRELESS_EXT > 7 */ /***** Prototypes for raylink functions **************************************/ @@ -213,7 +211,7 @@ 'priv' pointer in a dev_link_t structure can be used to point to a device-specific private data structure, like this. */ -static unsigned int ray_mem_speed = 0x2A; +static unsigned int ray_mem_speed = 500; MODULE_AUTHOR("Corey Thomas "); MODULE_DESCRIPTION("Raylink/WebGear wireless LAN driver"); @@ -543,8 +541,7 @@ req.Base = 0; req.Size = 0x8000; req.AccessSpeed = ray_mem_speed; - link->win = (window_handle_t)link->handle; - CS_CHECK(pcmcia_request_window, &link->win, &req); + CS_CHECK(pcmcia_request_window, &link->handle, &req, &link->win); mem.CardOffset = 0x0000; mem.Page = 0; CS_CHECK(pcmcia_map_mem_page, link->win, &mem); local->sram = (UCHAR *)(ioremap(req.Base,req.Size)); @@ -554,8 +551,7 @@ req.Base = 0; req.Size = 0x4000; req.AccessSpeed = ray_mem_speed; - local->rmem_handle = (window_handle_t)link->handle; - CS_CHECK(pcmcia_request_window, &local->rmem_handle, &req); + CS_CHECK(pcmcia_request_window, &link->handle, &req, &local->rmem_handle); mem.CardOffset = 0x8000; mem.Page = 0; CS_CHECK(pcmcia_map_mem_page, local->rmem_handle, &mem); local->rmem = (UCHAR *)(ioremap(req.Base,req.Size)); @@ -565,8 +561,7 @@ req.Base = 0; req.Size = 0x1000; req.AccessSpeed = ray_mem_speed; - local->amem_handle = (window_handle_t)link->handle; - CS_CHECK(pcmcia_request_window, &local->amem_handle, &req); + CS_CHECK(pcmcia_request_window, &link->handle, &req, &local->amem_handle); mem.CardOffset = 0x0000; mem.Page = 0; CS_CHECK(pcmcia_map_mem_page, local->amem_handle, &mem); local->amem = (UCHAR *)(ioremap(req.Base,req.Size)); @@ -1471,7 +1466,7 @@ } /* end ray_dev_ioctl */ /*===========================================================================*/ #if WIRELESS_EXT > 7 /* If wireless extension exist in the kernel */ -static iw_stats * ray_get_wireless_stats(struct device * dev) +static iw_stats * ray_get_wireless_stats(struct net_device * dev) { ray_dev_t * local = (ray_dev_t *) dev->priv; dev_link_t *link = local->finder; @@ -1848,7 +1843,7 @@ UCHAR cmd; UCHAR status; - if ((dev == NULL) || !dev->start) + if (dev == NULL) /* Note that we want interrupts with dev->start == 0 */ return; DEBUG(4,"ray_cs: interrupt for *dev=%p\n",dev); @@ -2094,7 +2089,7 @@ int total_len; int tmp; #ifdef WIRELESS_SPY - int siglev = prcs->var.rx_packet.rx_sig_lev; + int siglev = local->last_rsl; u_char linksrcaddr[ETH_ALEN]; /* Other end of the wireless link */ #endif diff -u --recursive --new-file v2.3.33/linux/drivers/net/pcmcia/wavelan_cs.h linux/drivers/net/pcmcia/wavelan_cs.h --- v2.3.33/linux/drivers/net/pcmcia/wavelan_cs.h Thu Nov 18 20:25:37 1999 +++ linux/drivers/net/pcmcia/wavelan_cs.h Mon Dec 20 15:07:08 1999 @@ -382,9 +382,9 @@ #include #include -#ifdef CONFIG_NET_RADIO +#ifdef CONFIG_NET_PCMCIA_RADIO #include /* Wireless extensions */ -#endif +#endif /* CONFIG_NET_PCMCIA_RADIO */ /* Pcmcia headers that we need */ #include diff -u --recursive --new-file v2.3.33/linux/drivers/net/sb1000.c linux/drivers/net/sb1000.c --- v2.3.33/linux/drivers/net/sb1000.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/net/sb1000.c Thu Dec 16 13:57:05 1999 @@ -1251,8 +1251,8 @@ void cleanup_module(void) { unregister_netdev(&dev_sb1000); - release_region(&dev_sb1000.base_addr, 16); - release_region(&dev_sb1000.rmem_end, 16); + release_region(dev_sb1000.base_addr, 16); + release_region(dev_sb1000.rmem_end, 16); kfree_s(dev_sb1000.priv, sizeof(struct sb1000_private)); dev_sb1000.priv = NULL; } diff -u --recursive --new-file v2.3.33/linux/drivers/net/tlan.c linux/drivers/net/tlan.c --- v2.3.33/linux/drivers/net/tlan.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/net/tlan.c Mon Dec 20 14:23:18 1999 @@ -34,6 +34,11 @@ * * Torben Mathiasen New Maintainer! * + * v1.1 Dec 20 -- Removed linux version checking(patch from + * Tigran Aivazian). v1.1 includes Alan's SMP + * opdates. We still have problems on SMP though, + * but I'm looking into that. + * ********************************************************************/ @@ -76,7 +81,7 @@ static u8 *TLanPadBuffer; static char TLanSignature[] = "TLAN"; static int TLanVersionMajor = 1; -static int TLanVersionMinor = 0; +static int TLanVersionMinor = 1; static TLanAdapterEntry TLanAdapterList[] = { @@ -1136,9 +1141,7 @@ printk( "TLAN: Received interrupt for uncompleted TX frame.\n" ); } -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) priv->stats.tx_bytes += head_list->frameSize; -#endif head_list->cStat = TLAN_CSTAT_UNUSED; dev->tbusy = 0; @@ -1256,9 +1259,7 @@ skb_reserve( skb, 2 ); t = (void *) skb_put( skb, head_list->frameSize ); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) priv->stats.rx_bytes += head_list->frameSize; -#endif memcpy( t, head_buffer, head_list->frameSize ); skb->protocol = eth_type_trans( skb, dev ); @@ -1280,9 +1281,8 @@ skb = (struct sk_buff *) head_list->buffer[9].address; head_list->buffer[9].address = 0; skb_trim( skb, head_list->frameSize ); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) + priv->stats.rx_bytes += head_list->frameSize; -#endif skb->protocol = eth_type_trans( skb, dev ); netif_rx( skb ); @@ -2447,7 +2447,6 @@ int err; int minten; TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - int irq; unsigned long flags; err = FALSE; diff -u --recursive --new-file v2.3.33/linux/drivers/net/tlan.h linux/drivers/net/tlan.h --- v2.3.33/linux/drivers/net/tlan.h Tue Dec 14 01:27:24 1999 +++ linux/drivers/net/tlan.h Mon Dec 20 14:23:18 1999 @@ -25,11 +25,6 @@ #include #include -#if LINUX_VERSION_CODE <= 0x20100 -#define net_device_stats enet_statistics -#endif - - /***************************************************************** diff -u --recursive --new-file v2.3.33/linux/drivers/net/wan/cosa.c linux/drivers/net/wan/cosa.c --- v2.3.33/linux/drivers/net/wan/cosa.c Fri Oct 15 15:25:13 1999 +++ linux/drivers/net/wan/cosa.c Mon Dec 20 15:07:08 1999 @@ -118,6 +118,7 @@ /* Per-channel data structure */ struct channel_data { + void *if_ptr; /* General purpose pointer (used by SPPP) */ int usage; /* Usage count; >0 for chrdev, -1 for netdev */ int num; /* Number of the channel */ struct cosa_data *cosa; /* Pointer to the per-card structure */ @@ -577,8 +578,10 @@ static void sppp_channel_init(struct channel_data *chan) { struct net_device *d; + chan->if_ptr = &chan->pppdev; + chan->pppdev.dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); sppp_attach(&chan->pppdev); - d=&chan->pppdev.dev; + d=chan->pppdev.dev; d->name = chan->name; d->base_addr = chan->cosa->datareg; d->irq = chan->cosa->irq; @@ -593,15 +596,15 @@ dev_init_buffers(d); if (register_netdev(d) == -1) { printk(KERN_WARNING "%s: register_netdev failed.\n", d->name); - sppp_detach(&chan->pppdev.dev); + sppp_detach(chan->pppdev.dev); return; } } static void sppp_channel_delete(struct channel_data *chan) { - sppp_detach(&chan->pppdev.dev); - unregister_netdev(&chan->pppdev.dev); + sppp_detach(chan->pppdev.dev); + unregister_netdev(chan->pppdev.dev); } @@ -717,7 +720,7 @@ chan->stats.rx_dropped++; return NULL; } - chan->pppdev.dev.trans_start = jiffies; + chan->pppdev.dev->trans_start = jiffies; return skb_put(chan->rx_skb, size); } @@ -731,13 +734,13 @@ return 0; } chan->rx_skb->protocol = htons(ETH_P_WAN_PPP); - chan->rx_skb->dev = &chan->pppdev.dev; + chan->rx_skb->dev = chan->pppdev.dev; chan->rx_skb->mac.raw = chan->rx_skb->data; chan->stats.rx_packets++; chan->stats.rx_bytes += chan->cosa->rxsize; netif_rx(chan->rx_skb); chan->rx_skb = 0; - chan->pppdev.dev.trans_start = jiffies; + chan->pppdev.dev->trans_start = jiffies; return 0; } @@ -755,7 +758,7 @@ chan->tx_skb = 0; chan->stats.tx_packets++; chan->stats.tx_bytes += size; - chan->pppdev.dev.tbusy = 0; + chan->pppdev.dev->tbusy = 0; mark_bh(NET_BH); return 1; } diff -u --recursive --new-file v2.3.33/linux/drivers/net/wan/hostess_sv11.c linux/drivers/net/wan/hostess_sv11.c --- v2.3.33/linux/drivers/net/wan/hostess_sv11.c Mon Oct 11 15:38:15 1999 +++ linux/drivers/net/wan/hostess_sv11.c Mon Dec 20 15:07:08 1999 @@ -41,6 +41,7 @@ struct sv11_device { + void *if_ptr; /* General purpose pointer (used by SPPP) */ struct z8530_dev sync; struct ppp_device netdev; char name[16]; @@ -240,7 +241,12 @@ goto fail3; memset(sv, 0, sizeof(*sv)); + sv->if_ptr=&sv->netdev; + sv->netdev.dev=(struct net_device *)kmalloc(sizeof(struct net_device), GFP_KERNEL); + if(!sv->netdev.dev) + goto fail2; + dev=&sv->sync; /* @@ -264,12 +270,12 @@ if(request_irq(irq, &z8530_interrupt, SA_INTERRUPT, "Hostess SV/11", dev)<0) { printk(KERN_WARNING "hostess: IRQ %d already in use.\n", irq); - goto fail2; + goto fail1; } dev->irq=irq; dev->chanA.private=sv; - dev->chanA.netdevice=&sv->netdev.dev; + dev->chanA.netdevice=sv->netdev.dev; dev->chanA.dev=dev; dev->chanB.dev=dev; dev->name=sv->name; @@ -374,6 +380,8 @@ free_dma(dev->chanA.txdma); fail: free_irq(irq, dev); +fail1: + kfree(sv->netdev.dev); fail2: kfree(sv); fail3: @@ -383,9 +391,9 @@ static void sv11_shutdown(struct sv11_device *dev) { - sppp_detach(&dev->netdev.dev); + sppp_detach(dev->netdev.dev); z8530_shutdown(&dev->sync); - unregister_netdev(&dev->netdev.dev); + unregister_netdev(dev->netdev.dev); free_irq(dev->sync.irq, dev); if(dma) { diff -u --recursive --new-file v2.3.33/linux/drivers/net/wan/sealevel.c linux/drivers/net/wan/sealevel.c --- v2.3.33/linux/drivers/net/wan/sealevel.c Mon Oct 11 15:38:15 1999 +++ linux/drivers/net/wan/sealevel.c Mon Dec 20 15:07:08 1999 @@ -32,6 +32,7 @@ struct slvl_device { + void *if_ptr; /* General purpose pointer (used by SPPP) */ struct z8530_channel *chan; struct ppp_device netdev; char name[16]; @@ -243,8 +244,19 @@ memset(b, 0, sizeof(*sv)); b->dev[0].chan = &b->board.chanA; + b->dev[0].if_ptr = &b->dev[0].netdev; + b->dev[0].netdev.dev=(struct net_device *) + kmalloc(sizeof(struct net_device), GFP_KERNEL); + if(!b->dev[0].netdev.dev) + goto fail2; + b->dev[1].chan = &b->board.chanB; - + b->dev[1].if_ptr = &b->dev[1].netdev; + b->dev[1].netdev.dev=(struct net_device *) + kmalloc(sizeof(struct net_device), GFP_KERNEL); + if(!b->dev[1].netdev.dev) + goto fail1_0; + dev=&b->board; /* @@ -283,14 +295,14 @@ if(request_irq(irq, &z8530_interrupt, SA_INTERRUPT, "SeaLevel", dev)<0) { printk(KERN_WARNING "sealevel: IRQ %d already in use.\n", irq); - goto fail2; + goto fail1_1; } dev->irq=irq; dev->chanA.private=&b->dev[0]; dev->chanB.private=&b->dev[1]; - dev->chanA.netdevice=&b->dev[0].netdev.dev; - dev->chanB.netdevice=&b->dev[1].netdev.dev; + dev->chanA.netdevice=b->dev[0].netdev.dev; + dev->chanB.netdevice=b->dev[1].netdev.dev; dev->chanA.dev=dev; dev->chanB.dev=dev; dev->name=b->dev[0].name; @@ -399,6 +411,10 @@ free_dma(dev->chanA.txdma); fail: free_irq(irq, dev); +fail1_1: + kfree(b->dev[1].netdev.dev); +fail1_0: + kfree(b->dev[0].netdev.dev); fail2: kfree(b); fail3: @@ -414,8 +430,8 @@ for(u=0; u<2; u++) { - sppp_detach(&b->dev[u].netdev.dev); - unregister_netdev(&b->dev[u].netdev.dev); + sppp_detach(b->dev[u].netdev.dev); + unregister_netdev(b->dev[u].netdev.dev); } free_irq(b->board.irq, &b->board); diff -u --recursive --new-file v2.3.33/linux/drivers/net/wan/syncppp.c linux/drivers/net/wan/syncppp.c --- v2.3.33/linux/drivers/net/wan/syncppp.c Mon Oct 11 15:38:15 1999 +++ linux/drivers/net/wan/syncppp.c Mon Dec 20 15:07:08 1999 @@ -187,7 +187,7 @@ void sppp_input (struct net_device *dev, struct sk_buff *skb) { struct ppp_header *h; - struct sppp *sp = &((struct ppp_device *)dev)->sppp; + struct sppp *sp = (struct sppp *)sppp_of(dev); skb->dev=dev; skb->mac.raw=skb->data; @@ -316,7 +316,7 @@ static int sppp_hard_header(struct sk_buff *skb, struct net_device *dev, __u16 type, void *daddr, void *saddr, unsigned int len) { - struct sppp *sp = &((struct ppp_device *)dev)->sppp; + struct sppp *sp = (struct sppp *)sppp_of(dev); struct ppp_header *h; skb_push(skb,sizeof(struct ppp_header)); h=(struct ppp_header *)skb->data; @@ -826,7 +826,7 @@ int sppp_close (struct net_device *dev) { - struct sppp *sp = &((struct ppp_device *)dev)->sppp; + struct sppp *sp = (struct sppp *)sppp_of(dev); dev->flags &= ~IFF_RUNNING; sp->lcp.state = LCP_STATE_CLOSED; sp->ipcp.state = IPCP_STATE_CLOSED; @@ -839,7 +839,7 @@ int sppp_open (struct net_device *dev) { - struct sppp *sp = &((struct ppp_device *)dev)->sppp; + struct sppp *sp = (struct sppp *)sppp_of(dev); sppp_close(dev); dev->flags |= IFF_RUNNING; if (!(sp->pp_flags & PP_CISCO)) @@ -851,7 +851,7 @@ int sppp_reopen (struct net_device *dev) { - struct sppp *sp = &((struct ppp_device *)dev)->sppp; + struct sppp *sp = (struct sppp *)sppp_of(dev); sppp_close(dev); dev->flags |= IFF_RUNNING; if (!(sp->pp_flags & PP_CISCO)) @@ -880,7 +880,7 @@ int sppp_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct sppp *sp = &((struct ppp_device *)dev)->sppp; + struct sppp *sp = (struct sppp *)sppp_of(dev); if(dev->flags&IFF_UP) return -EBUSY; @@ -913,7 +913,7 @@ void sppp_attach(struct ppp_device *pd) { - struct net_device *dev=&pd->dev; + struct net_device *dev = pd->dev; struct sppp *sp = &pd->sppp; /* Initialize keepalive handler. */ @@ -970,8 +970,7 @@ void sppp_detach (struct net_device *dev) { - struct sppp **q, *p, *sp = &((struct ppp_device *)dev)->sppp; - + struct sppp **q, *p, *sp = (struct sppp *)sppp_of(dev); /* Remove the entry from the keepalive list. */ for (q = &spppq; (p = *q); q = &p->pp_next) @@ -1296,6 +1295,8 @@ sppp_packet_type.type=htons(ETH_P_WAN_PPP); dev_add_pack(&sppp_packet_type); } + +EXPORT_SYMBOL(sync_ppp_init); #ifdef MODULE diff -u --recursive --new-file v2.3.33/linux/drivers/net/wan/syncppp.h linux/drivers/net/wan/syncppp.h --- v2.3.33/linux/drivers/net/wan/syncppp.h Mon Oct 11 15:38:15 1999 +++ linux/drivers/net/wan/syncppp.h Mon Dec 20 15:07:08 1999 @@ -51,10 +51,13 @@ struct ppp_device { - struct net_device dev; /* Network device */ + struct net_device *dev; /* Network device pointer */ struct sppp sppp; /* Synchronous PPP */ }; +#define sppp_of(dev) \ + (&((struct ppp_device *)(*(unsigned long *)((dev)->priv)))->sppp) + #define PP_KEEPALIVE 0x01 /* use keepalive protocol */ #define PP_CISCO 0x02 /* use Cisco protocol instead of PPP */ #define PP_TIMO 0x04 /* cp_timeout routine active */ @@ -82,6 +85,7 @@ int sppp_open (struct net_device *dev); int sppp_reopen (struct net_device *dev); int sppp_close (struct net_device *dev); +void sync_ppp_init (void); #endif #define SPPPIOCCISCO (SIOCDEVPRIVATE) diff -u --recursive --new-file v2.3.33/linux/drivers/pci/pci.ids linux/drivers/pci/pci.ids --- v2.3.33/linux/drivers/pci/pci.ids Wed Dec 8 14:11:26 1999 +++ linux/drivers/pci/pci.ids Tue Dec 14 23:03:50 1999 @@ -3348,7 +3348,16 @@ 2411 82801 82810 Chipset IDE 2412 82801 82810 Chipset USB 2413 82801 82810 Chipset SMBus + 2415 82801 82810 AC'97 Audio + 2416 82801 82810 AC'97 Modem 2418 82801 82810 PCI Bridge + 2420 82801AB 82810 Chipset ISA Bridge (LPC) + 2421 82801AB 82810 Chipset IDE + 2422 82801AB 82810 Chipset USB + 2423 82801AB 82810 Chipset SMBus + 2425 82801AB 82810 AC'97 Audio + 2426 82801AB 82810 AC'97 Modem + 2428 82801AB 82810 PCI Bridge 5200 EtherExpress PRO/100 5201 EtherExpress PRO/100 7000 82371SB PIIX3 ISA [Natoma/Triton II] diff -u --recursive --new-file v2.3.33/linux/drivers/pci/setup.c linux/drivers/pci/setup.c --- v2.3.33/linux/drivers/pci/setup.c Wed Dec 15 10:43:16 1999 +++ linux/drivers/pci/setup.c Wed Dec 15 10:32:47 1999 @@ -84,6 +84,7 @@ continue; min = (res->flags & IORESOURCE_IO ? min_io : min_mem); + min += root->start; size = res->end + 1; DBGC((" for root[%lx:%lx] min[%lx] size[%lx]\n", root->start, root->end, min, size)); diff -u --recursive --new-file v2.3.33/linux/drivers/pcmcia/bulkmem.c linux/drivers/pcmcia/bulkmem.c --- v2.3.33/linux/drivers/pcmcia/bulkmem.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/pcmcia/bulkmem.c Sat Dec 18 15:43:16 1999 @@ -294,7 +294,13 @@ { switch (func) { case MTDRequestWindow: - return pcmcia_request_window(a1, a2); + { + window_handle_t w; + int ret = pcmcia_request_window(a1, a2, &w); + (window_handle_t *)a1 = w; + return ret; + } + break; case MTDReleaseWindow: return pcmcia_release_window(a1); case MTDModifyWindow: @@ -470,7 +476,8 @@ ======================================================================*/ -int pcmcia_register_erase_queue(client_handle_t *handle, eraseq_hdr_t *header) +int pcmcia_register_erase_queue(client_handle_t *handle, eraseq_hdr_t *header, + eraseq_handle_t *e) { eraseq_t *queue; @@ -481,7 +488,7 @@ queue->handle = *handle; queue->count = header->QueueEntryCnt; queue->entry = header->QueueEntryArray; - *handle = (client_handle_t)queue; + *e = queue; return CS_SUCCESS; } /* register_erase_queue */ @@ -517,7 +524,7 @@ ======================================================================*/ -int pcmcia_open_memory(client_handle_t *handle, open_mem_t *open) +int pcmcia_open_memory(client_handle_t *handle, open_mem_t *open, memory_handle_t *mh) { socket_info_t *s; memory_handle_t region; @@ -534,7 +541,7 @@ region = region->info.next; } if (region && region->mtd) { - *handle = (client_handle_t)region; + *mh = region; DEBUG(1, "cs: open_memory(0x%p, 0x%x) = 0x%p\n", handle, open->Offset, region); return CS_SUCCESS; diff -u --recursive --new-file v2.3.33/linux/drivers/pcmcia/cs.c linux/drivers/pcmcia/cs.c --- v2.3.33/linux/drivers/pcmcia/cs.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/pcmcia/cs.c Mon Dec 20 17:52:46 1999 @@ -1814,7 +1814,7 @@ ======================================================================*/ -int pcmcia_request_window(client_handle_t *handle, win_req_t *req) +int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle_t *wh) { socket_info_t *s; window_t *win; @@ -1876,7 +1876,7 @@ s->state |= SOCKET_WIN_REQ(w); /* Return window handle */ - *handle = (client_handle_t)win; + *wh = win; return CS_SUCCESS; } /* request_window */ @@ -2141,7 +2141,14 @@ case ModifyWindow: return pcmcia_modify_window(a1, a2); break; case OpenMemory: - return pcmcia_open_memory(a1, a2); +/* return pcmcia_open_memory(a1, a2); */ + { + memory_handle_t m; + int ret = pcmcia_open_memory(a1, a2, &m); + (memory_handle_t *)a1 = m; + return ret; + } + break; case ParseTuple: return pcmcia_parse_tuple(a1, a2, a3); break; case ReadMemory: @@ -2149,8 +2156,15 @@ case RegisterClient: return pcmcia_register_client(a1, a2); break; case RegisterEraseQueue: - return pcmcia_register_erase_queue(a1, a2); break; - case RegisterMTD: + { + eraseq_handle_t w; + int ret = pcmcia_register_erase_queue(a1, a2, &w); + (eraseq_handle_t *)a1 = w; + return ret; + } + break; +/* return pcmcia_register_erase_queue(a1, a2); break; */ + return pcmcia_register_mtd(a1, a2); break; case ReleaseConfiguration: return pcmcia_release_configuration(a1); break; @@ -2167,7 +2181,13 @@ case RequestIRQ: return pcmcia_request_irq(a1, a2); break; case RequestWindow: - return pcmcia_request_window(a1, a2); break; + { + window_handle_t w; + int ret = pcmcia_request_window(a1, a2, &w); + (window_handle_t *)a1 = w; + return ret; + } + break; case ResetCard: return pcmcia_reset_card(a1, a2); break; case SetEventMask: @@ -2209,6 +2229,55 @@ OS-specific module glue goes here ======================================================================*/ +EXPORT_SYMBOL(pcmcia_access_configuration_register); +EXPORT_SYMBOL(pcmcia_adjust_resource_info); +EXPORT_SYMBOL(pcmcia_check_erase_queue); +EXPORT_SYMBOL(pcmcia_close_memory); +EXPORT_SYMBOL(pcmcia_copy_memory); +EXPORT_SYMBOL(pcmcia_deregister_client); +EXPORT_SYMBOL(pcmcia_deregister_erase_queue); +EXPORT_SYMBOL(pcmcia_get_first_client); +EXPORT_SYMBOL(pcmcia_get_card_services_info); +EXPORT_SYMBOL(pcmcia_get_configuration_info); +EXPORT_SYMBOL(pcmcia_get_next_client); +EXPORT_SYMBOL(pcmcia_get_first_region); +EXPORT_SYMBOL(pcmcia_get_first_tuple); +EXPORT_SYMBOL(pcmcia_get_next_region); +EXPORT_SYMBOL(pcmcia_get_next_tuple); +EXPORT_SYMBOL(pcmcia_get_status); +EXPORT_SYMBOL(pcmcia_get_tuple_data); +EXPORT_SYMBOL(pcmcia_map_mem_page); +EXPORT_SYMBOL(pcmcia_modify_configuration); +EXPORT_SYMBOL(pcmcia_modify_window); +EXPORT_SYMBOL(pcmcia_open_memory); +EXPORT_SYMBOL(pcmcia_parse_tuple); +EXPORT_SYMBOL(pcmcia_read_memory); +EXPORT_SYMBOL(pcmcia_register_client); +EXPORT_SYMBOL(pcmcia_register_erase_queue); +EXPORT_SYMBOL(pcmcia_register_mtd); +EXPORT_SYMBOL(pcmcia_release_configuration); +EXPORT_SYMBOL(pcmcia_release_io); +EXPORT_SYMBOL(pcmcia_release_irq); +EXPORT_SYMBOL(pcmcia_release_window); +EXPORT_SYMBOL(pcmcia_request_configuration); +EXPORT_SYMBOL(pcmcia_request_io); +EXPORT_SYMBOL(pcmcia_request_irq); +EXPORT_SYMBOL(pcmcia_request_window); +EXPORT_SYMBOL(pcmcia_reset_card); +EXPORT_SYMBOL(pcmcia_set_event_mask); +EXPORT_SYMBOL(pcmcia_validate_cis); +EXPORT_SYMBOL(pcmcia_write_memory); +EXPORT_SYMBOL(pcmcia_bind_device); +EXPORT_SYMBOL(pcmcia_bind_mtd); +EXPORT_SYMBOL(pcmcia_report_error); +EXPORT_SYMBOL(pcmcia_suspend_card); +EXPORT_SYMBOL(pcmcia_resume_card); +EXPORT_SYMBOL(pcmcia_eject_card); +EXPORT_SYMBOL(pcmcia_insert_card); +EXPORT_SYMBOL(pcmcia_replace_cis); +EXPORT_SYMBOL(pcmcia_get_first_window); +EXPORT_SYMBOL(pcmcia_get_next_window); +EXPORT_SYMBOL(pcmcia_get_mem_page); EXPORT_SYMBOL(register_ss_entry); EXPORT_SYMBOL(unregister_ss_entry); diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx --- v2.3.33/linux/drivers/scsi/ChangeLog.ncr53c8xx Sat Oct 9 11:47:50 1999 +++ linux/drivers/scsi/ChangeLog.ncr53c8xx Thu Dec 16 13:57:05 1999 @@ -1,3 +1,26 @@ +Mon Dec 6 22:00 1999 Gerard Roudier (groudier@club-internet.fr) + * revision 3.2d + - Change messages written by the driver at initialisation and + through the /proc FS (rather cosmetic changes that consist in + printing out the PCI bus number and device/function). + - Get rid of the old PCI bios interface, but preserve kernel 2.0 + compatibility from a simple wrapper. + - Remove the compilation condition about having to acquire the + io_request_lock since it seems to be a definite feature now.:) + - proc_dir structure no longer needed for kernel >= 2.3.27. + - Change the driver detection code by the sym53c8xx one, modulo + some minor changes. The driver can now attach any number of + controllers (>40) and does no longer hoger stack space at + initialisation. + - Definitely disable overlapped PCI arbitration for all dual + function chips, since I cannot make sure for what chip revisions + it is actually safe. + - Add support for the SYM53C1510D. + - Update the poor Tekram sync factor table. + - Remove the compilation condition about having to acquire the + io_request_lock since it seems to be a definite feature now.:) + - proc_dir structure no longer needed for kernel >= 2.3.27. + Sat Sep 11 18:00 1999 Gerard Roudier (groudier@club-internet.fr) * revision 3.2c - Handle correctly (hopefully) jiffies wrap-around. diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/ChangeLog.sym53c8xx linux/drivers/scsi/ChangeLog.sym53c8xx --- v2.3.33/linux/drivers/scsi/ChangeLog.sym53c8xx Fri Oct 15 15:25:14 1999 +++ linux/drivers/scsi/ChangeLog.sym53c8xx Thu Dec 16 13:57:05 1999 @@ -1,3 +1,33 @@ +Mon Dec 6 22:00 1999 Gerard Roudier (groudier@club-internet.fr) + * version sym53c8xx-1.5g + - Change messages written by the driver at initialisation and + through the /proc FS (rather cosmetic changes that consist in + printing out the PCI bus number and PCI device/function). + - Ensure the SCRIPTS processor is stopped while calibrating the + SCSI clock (the initialisation code has been a bit reworked). + Change moved to the FreeBSD sym_hipd driver). + - Some fixes in the MODIFY_DP/IGN_RESIDUE code and residual + calculation (moved from FreeBSD sym_hipd driver). + - Add NVRAM support for Tekram boards that use 24C16 EEPROM. + Code moved from the FreeBSD sym_hipd driver, since it has + been that one that got this feature first. + - Definitely disable overlapped PCI arbitration for all dual + function chips, since I cannot make sure for what chip revisions + it is actually safe. + - Add support for the SYM53C1510D (also for ncr53c8xx). + - Fix up properly the PCI latency timer when needed or asked for. + - Get rid of the old PCI bios interface, but preserve kernel 2.0 + compatibility from a simple wrapper. + - Update the poor Tekram sync factor table. + - Fix in a tiny 'printk' bug that may oops in case of extended + errors (unrecovered parity error, data overrun, etc ...) + (Sent by Pamela Delaney from LSILOGIC) + - Remove the compilation condition about having to acquire the + io_request_lock since it seems to be a definite feature now.:) + - Change get_pages by GetPages since Linux >= 2.3.27 now wants + get_pages to ever be used as a kernel symbol (from 2.3.27). + - proc_dir structure no longer needed for kernel >= 2.3.27. + Sun Oct 3 19:00 1999 Gerard Roudier (groudier@club-internet.fr) * version sym53c8xx-1.5f - Change the way the driver checks the PCI clock frequency, so diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v2.3.33/linux/drivers/scsi/Config.in Tue Dec 14 01:27:24 1999 +++ linux/drivers/scsi/Config.in Tue Dec 14 20:43:56 1999 @@ -11,7 +11,7 @@ comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs' #if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool 'Enable extra checks in new queueing code' CONFIG_SCSI_DEBUG_QUEUES + bool ' Enable extra checks in new queueing code' CONFIG_SCSI_DEBUG_QUEUES #fi bool ' Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN @@ -46,11 +46,11 @@ fi dep_tristate 'DTC3180/3280 SCSI support' CONFIG_SCSI_DTC3280 $CONFIG_SCSI dep_tristate 'EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support' CONFIG_SCSI_EATA $CONFIG_SCSI - if [ "$CONFIG_SCSI_EATA" != "n" ]; then - bool ' enable tagged command queueing' CONFIG_SCSI_EATA_TAGGED_QUEUE - bool ' enable elevator sorting' CONFIG_SCSI_EATA_LINKED_COMMANDS - int 'maximum number of queued commands' CONFIG_SCSI_EATA_MAX_TAGS 16 - fi +if [ "$CONFIG_SCSI_EATA" != "n" ]; then + bool ' enable tagged command queueing' CONFIG_SCSI_EATA_TAGGED_QUEUE + bool ' enable elevator sorting' CONFIG_SCSI_EATA_LINKED_COMMANDS + int 'maximum number of queued commands' CONFIG_SCSI_EATA_MAX_TAGS 16 +fi dep_tristate 'EATA-DMA [Obsolete] (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI dep_tristate 'Future Domain 16xx SCSI/AHA-2920A support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v2.3.33/linux/drivers/scsi/Makefile Tue Dec 14 01:27:24 1999 +++ linux/drivers/scsi/Makefile Mon Dec 20 14:43:40 1999 @@ -679,7 +679,7 @@ 53c8xx_d.h: 53c7,8xx.scr script_asm.pl ln -sf 53c7,8xx.scr fake8.c - $(CPP) -traditional -DCHIP=810 fake8.c | grep -v '^#' | perl script_asm.pl + $(CPP) $(CPPFLAGS) -traditional -DCHIP=810 fake8.c | grep -v '^#' | perl script_asm.pl mv script.h 53c8xx_d.h mv scriptu.h 53c8xx_u.h rm fake8.c @@ -688,7 +688,7 @@ 53c7xx_d.h: 53c7xx.scr script_asm.pl ln -sf 53c7xx.scr fake7.c - $(CPP) -traditional -DCHIP=710 fake7.c | grep -v '^#' | perl -s script_asm.pl -ncr7x0_family + $(CPP) $(CPPFLAGS) -traditional -DCHIP=710 fake7.c | grep -v '^#' | perl -s script_asm.pl -ncr7x0_family mv script.h 53c7xx_d.h mv scriptu.h 53c7xx_u.h rm fake7.c @@ -699,7 +699,7 @@ sim710_d.h: sim710.scr script_asm.pl ln -sf sim710.scr fake7.c - $(CPP) -traditional -DCHIP=710 fake7.c | grep -v '^#' | perl -s script_asm.pl -ncr7x0_family + $(CPP) $(CPPFLAGS) -traditional -DCHIP=710 fake7.c | grep -v '^#' | perl -s script_asm.pl -ncr7x0_family mv script.h sim710_d.h mv scriptu.h sim710_u.h rm fake7.c diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/ips.c linux/drivers/scsi/ips.c --- v2.3.33/linux/drivers/scsi/ips.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/scsi/ips.c Mon Dec 20 15:07:08 1999 @@ -60,6 +60,12 @@ /* 0.99.05 - Fix an oops when we get certain passthru commands */ /* 1.00.00 - Initial Public Release */ /* Functionally equivalent to 0.99.05 */ +/* 3.60.00 - Bump max commands to 128 for use with ServeRAID firmware 3.60 */ +/* - Change version to 3.60 to coincide with ServeRAID release */ +/* numbering. */ +/* 3.60.01 - Remove bogus error check in passthru routine */ +/* 3.60.02 - Make DCDB direction based on lookup table */ +/* - Only allow one DCDB command to a SCSI ID at a time */ /* */ /*****************************************************************************/ @@ -110,8 +116,8 @@ /* * DRIVER_VER */ -#define IPS_VERSION_HIGH "1.00" /* MUST be 4 chars */ -#define IPS_VERSION_LOW ".00 " /* MUST be 4 chars */ +#define IPS_VERSION_HIGH "3.60" /* MUST be 4 chars */ +#define IPS_VERSION_LOW ".02 " /* MUST be 4 chars */ #if !defined(__i386__) #error "This driver has only been tested on the x86 platform" @@ -155,6 +161,63 @@ }; /* + * Direction table + */ +static char ips_command_direction[] = { +IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, +IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, +IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, +IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_OUT, +IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT, +IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_IN, +IPS_DATA_UNK, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_UNK, +IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, +IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_NONE, +IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, +IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, +IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_NONE, +IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK, +IPS_DATA_NONE, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_NONE, +IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_OUT, +IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_NONE, +IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_OUT, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK +}; + +/* * Function prototypes */ int ips_detect(Scsi_Host_Template *); @@ -1333,7 +1396,7 @@ (scb->cmd.basic_io.op_code == DIRECT_CDB_SCATTER_GATHER)) return (0); - if (pt->CmdBSize && pt->CmdBuffer) { + if (pt->CmdBSize) { scb->data_busaddr = VIRT_TO_BUS(scb->scsi_cmd->request_buffer + sizeof(ips_passthru_t)); } else { scb->data_busaddr = 0L; @@ -1657,8 +1720,10 @@ /* set controller IDs */ ha->ha_id[0] = IPS_ADAPTER_ID; - for (i = 1; i < ha->nbus; i++) + for (i = 1; i < ha->nbus; i++) { ha->ha_id[i] = ha->conf->init_id[i-1] & 0x1f; + ha->dcdb_active[i-1] = 0; + } return (1); } @@ -1678,6 +1743,7 @@ ips_next(ips_ha_t *ha) { ips_scb_t *scb; Scsi_Cmnd *SC; + Scsi_Cmnd *p; int ret; DBG("ips_next"); @@ -1750,9 +1816,15 @@ /* * Send "Normal" I/O commands */ - while ((ha->scb_waitlist.head) && - (scb = ips_getscb(ha))) { - SC = ips_removeq_wait_head(&ha->scb_waitlist); + p = ha->scb_waitlist.head; + while ((p) && (scb = ips_getscb(ha))) { + if ((p->channel > 0) && (ha->dcdb_active[p->channel-1] & (1 << p->target))) { + ips_freescb(ha, scb); + p = (Scsi_Cmnd *) p->host_scribble; + continue; + } + + SC = ips_removeq_wait(&ha->scb_waitlist, p); SC->result = DID_OK; SC->host_scribble = NULL; @@ -1767,7 +1839,7 @@ scb->data_len = 0; scb->callback = ipsintr_done; scb->timeout = ips_cmd_timeout; - memset(&scb->cmd, 0, 4); + memset(&scb->cmd, 0, 16); /* copy in the CDB */ memcpy(scb->cdb, SC->cmnd, SC->cmd_len); @@ -1825,11 +1897,11 @@ } - if ((scb->scsi_cmd->request.cmd == READ) && (SC->request_bufflen)) - scb->dcdb.cmd_attribute |= IPS_DATA_IN; + scb->dcdb.cmd_attribute |= + ips_command_direction[scb->scsi_cmd->cmnd[0]]; - if ((scb->scsi_cmd->request.cmd == WRITE) && (SC->request_bufflen)) - scb->dcdb.cmd_attribute |= IPS_DATA_OUT; + if (!scb->dcdb.cmd_attribute & 0x3) + scb->dcdb.transfer_length = 0; if (scb->data_len >= IPS_MAX_XFER) { scb->dcdb.cmd_attribute |= TRANSFER_64K; @@ -1848,16 +1920,25 @@ scb->scsi_cmd->scsi_done(scb->scsi_cmd); } + if (scb->bus) + ha->dcdb_active[scb->bus-1] &= ~(1 << scb->target_id); + ips_freescb(ha, scb); break; case IPS_SUCCESS_IMM: if (scb->scsi_cmd) scb->scsi_cmd->scsi_done(scb->scsi_cmd); + + if (scb->bus) + ha->dcdb_active[scb->bus-1] &= ~(1 << scb->target_id); + ips_freescb(ha, scb); break; default: break; } /* end case */ + + p = (Scsi_Cmnd *) p->host_scribble; } /* end while */ } @@ -2259,12 +2340,11 @@ scb->sg_len = 0; } - if ((scb->scsi_cmd->request.cmd == READ) && (scb->data_len)) - scb->dcdb.cmd_attribute |= IPS_DATA_IN; - - if ((scb->scsi_cmd->request.cmd == WRITE) && (scb->data_len)) - scb->dcdb.cmd_attribute |= IPS_DATA_OUT; - + scb->dcdb.cmd_attribute |= + ips_command_direction[scb->scsi_cmd->cmnd[0]]; + + if (!scb->dcdb.cmd_attribute & 0x3) + scb->dcdb.transfer_length = 0; if (scb->data_len >= IPS_MAX_XFER) { scb->dcdb.cmd_attribute |= TRANSFER_64K; scb->dcdb.transfer_length = 0; @@ -2299,6 +2379,9 @@ } /* end if passthru */ #endif + if (scb->bus) + ha->dcdb_active[scb->bus-1] &= ~(1 << scb->target_id); + /* call back to SCSI layer */ scb->scsi_cmd->scsi_done(scb->scsi_cmd); ips_freescb(ha, scb); @@ -2323,6 +2406,16 @@ DBG("ips_map_status"); if (scb->bus) { +#if IPS_DEBUG >= 10 + printk(KERN_NOTICE "(%s) Physical device error: %x %x, Sense Key: %x, ASC: %x, ASCQ: %x\n", + ips_name, + scb->basic_status, + scb->extended_status, + scb->dcdb.sense_info[2] & 0xf, + scb->dcdb.sense_info[12], + scb->dcdb.sense_info[13]); +#endif + /* copy SCSI status and sense data for DCDB commands */ memcpy(scb->scsi_cmd->sense_buffer, scb->dcdb.sense_info, sizeof(scb->scsi_cmd->sense_buffer)); @@ -2669,6 +2762,7 @@ else scb->cmd.dcdb.op_code = DIRECT_CDB_SCATTER_GATHER; + ha->dcdb_active[scb->bus-1] |= (1 << scb->target_id); scb->cmd.dcdb.command_id = IPS_COMMAND_ID(ha, scb); scb->cmd.dcdb.dcdb_address = VIRT_TO_BUS(&scb->dcdb); scb->cmd.dcdb.reserved = 0; diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/ips.h linux/drivers/scsi/ips.h --- v2.3.33/linux/drivers/scsi/ips.h Thu Nov 11 20:11:47 1999 +++ linux/drivers/scsi/ips.h Mon Dec 20 16:02:52 1999 @@ -77,102 +77,102 @@ #define UDELAY udelay #define MDELAY mdelay - #define verify_area_20(t,a,sz) (0) /* success */ - #define PUT_USER put_user - #define __PUT_USER __put_user - #define PUT_USER_RET put_user_ret - #define GET_USER get_user - #define __GET_USER __get_user - #define GET_USER_RET get_user_ret + #define verify_area_20(t,a,sz) (0) /* success */ + #define PUT_USER put_user + #define __PUT_USER __put_user + #define PUT_USER_RET put_user_ret + #define GET_USER get_user + #define __GET_USER __get_user + #define GET_USER_RET get_user_ret /* * Adapter address map equates */ - #define HISR 0x08 /* Host Interrupt Status Reg */ - #define CCSAR 0x10 /* Cmd Channel System Addr Reg */ - #define CCCR 0x14 /* Cmd Channel Control Reg */ - #define SQHR 0x20 /* Status Q Head Reg */ - #define SQTR 0x24 /* Status Q Tail Reg */ - #define SQER 0x28 /* Status Q End Reg */ - #define SQSR 0x2C /* Status Q Start Reg */ - #define SCPR 0x05 /* Subsystem control port reg */ - #define ISPR 0x06 /* interrupt status port reg */ - #define CBSP 0x07 /* CBSP register */ + #define HISR 0x08 /* Host Interrupt Status Reg */ + #define CCSAR 0x10 /* Cmd Channel System Addr Reg */ + #define CCCR 0x14 /* Cmd Channel Control Reg */ + #define SQHR 0x20 /* Status Q Head Reg */ + #define SQTR 0x24 /* Status Q Tail Reg */ + #define SQER 0x28 /* Status Q End Reg */ + #define SQSR 0x2C /* Status Q Start Reg */ + #define SCPR 0x05 /* Subsystem control port reg */ + #define ISPR 0x06 /* interrupt status port reg */ + #define CBSP 0x07 /* CBSP register */ /* * Adapter register bit equates */ - #define GHI 0x04 /* HISR General Host Interrupt */ - #define SQO 0x02 /* HISR Status Q Overflow */ - #define SCE 0x01 /* HISR Status Channel Enqueue */ - #define SEMAPHORE 0x08 /* CCCR Semaphore Bit */ - #define ILE 0x10 /* CCCR ILE Bit */ - #define START_COMMAND 0x101A /* CCCR Start Command Channel */ - #define START_STOP_BIT 0x0002 /* CCCR Start/Stop Bit */ - #define RST 0x80 /* SCPR Reset Bit */ - #define EBM 0x02 /* SCPR Enable Bus Master */ - #define EI 0x80 /* HISR Enable Interrupts */ - #define OP 0x01 /* OP bit in CBSP */ + #define GHI 0x04 /* HISR General Host Interrupt */ + #define SQO 0x02 /* HISR Status Q Overflow */ + #define SCE 0x01 /* HISR Status Channel Enqueue */ + #define SEMAPHORE 0x08 /* CCCR Semaphore Bit */ + #define ILE 0x10 /* CCCR ILE Bit */ + #define START_COMMAND 0x101A /* CCCR Start Command Channel */ + #define START_STOP_BIT 0x0002 /* CCCR Start/Stop Bit */ + #define RST 0x80 /* SCPR Reset Bit */ + #define EBM 0x02 /* SCPR Enable Bus Master */ + #define EI 0x80 /* HISR Enable Interrupts */ + #define OP 0x01 /* OP bit in CBSP */ /* * Adapter Command ID Equates */ - #define GET_LOGICAL_DRIVE_INFO 0x19 - #define GET_SUBSYS_PARAM 0x40 - #define READ_NVRAM_CONFIGURATION 0x38 - #define RW_NVRAM_PAGE 0xBC - #define IPS_READ 0x02 - #define IPS_WRITE 0x03 - #define ENQUIRY 0x05 - #define FLUSH_CACHE 0x0A - #define NORM_STATE 0x00 - #define READ_SCATTER_GATHER 0x82 - #define WRITE_SCATTER_GATHER 0x83 - #define DIRECT_CDB 0x04 - #define DIRECT_CDB_SCATTER_GATHER 0x84 - #define CONFIG_SYNC 0x58 - #define POCL 0x30 - #define GET_ERASE_ERROR_TABLE 0x17 - #define RESET_CHANNEL 0x1A - #define CSL 0xFF - #define ADAPT_RESET 0xFF + #define GET_LOGICAL_DRIVE_INFO 0x19 + #define GET_SUBSYS_PARAM 0x40 + #define READ_NVRAM_CONFIGURATION 0x38 + #define RW_NVRAM_PAGE 0xBC + #define IPS_READ 0x02 + #define IPS_WRITE 0x03 + #define ENQUIRY 0x05 + #define FLUSH_CACHE 0x0A + #define NORM_STATE 0x00 + #define READ_SCATTER_GATHER 0x82 + #define WRITE_SCATTER_GATHER 0x83 + #define DIRECT_CDB 0x04 + #define DIRECT_CDB_SCATTER_GATHER 0x84 + #define CONFIG_SYNC 0x58 + #define POCL 0x30 + #define GET_ERASE_ERROR_TABLE 0x17 + #define RESET_CHANNEL 0x1A + #define CSL 0xFF + #define ADAPT_RESET 0xFF /* * Adapter Equates */ - #define IPS_MAX_ADAPTERS 16 - #define IPS_MAX_IOCTL 1 - #define IPS_MAX_IOCTL_QUEUE 8 - #define IPS_MAX_QUEUE 128 - #define IPS_BLKSIZE 512 - #define MAX_SG_ELEMENTS 17 - #define MAX_LOGICAL_DRIVES 8 - #define MAX_CHANNELS 3 - #define MAX_TARGETS 15 - #define MAX_CHUNKS 16 - #define MAX_CMDS 64 - #define IPS_MAX_XFER 0x10000 - #define COMP_MODE_HEADS 128 - #define COMP_MODE_SECTORS 32 - #define NORM_MODE_HEADS 254 - #define NORM_MODE_SECTORS 63 - #define NVRAM_PAGE5_SIGNATURE 0xFFDDBB99 - #define MAX_POST_BYTES 0x02 - #define MAX_CONFIG_BYTES 0x02 - #define GOOD_POST_BASIC_STATUS 0x80 - #define SEMAPHORE_TIMEOUT 2000 - #define IPS_INTR_OFF 0 - #define IPS_INTR_ON 1 - #define IPS_ADAPTER_ID 0xF - #define IPS_VENDORID 0x1014 - #define IPS_DEVICEID 0x002E - #define TIMEOUT_10 0x10 - #define TIMEOUT_60 0x20 - #define TIMEOUT_20M 0x30 - #define STATUS_SIZE 4 - #define STATUS_Q_SIZE (MAX_CMDS+1) * STATUS_SIZE - #define ONE_MSEC 1 - #define ONE_SEC 1000 + #define IPS_MAX_ADAPTERS 16 + #define IPS_MAX_IOCTL 1 + #define IPS_MAX_IOCTL_QUEUE 8 + #define IPS_MAX_QUEUE 128 + #define IPS_BLKSIZE 512 + #define MAX_SG_ELEMENTS 17 + #define MAX_LOGICAL_DRIVES 8 + #define MAX_CHANNELS 3 + #define MAX_TARGETS 15 + #define MAX_CHUNKS 16 + #define MAX_CMDS 128 + #define IPS_MAX_XFER 0x10000 + #define COMP_MODE_HEADS 128 + #define COMP_MODE_SECTORS 32 + #define NORM_MODE_HEADS 254 + #define NORM_MODE_SECTORS 63 + #define NVRAM_PAGE5_SIGNATURE 0xFFDDBB99 + #define MAX_POST_BYTES 0x02 + #define MAX_CONFIG_BYTES 0x02 + #define GOOD_POST_BASIC_STATUS 0x80 + #define SEMAPHORE_TIMEOUT 2000 + #define IPS_INTR_OFF 0 + #define IPS_INTR_ON 1 + #define IPS_ADAPTER_ID 0xF + #define IPS_VENDORID 0x1014 + #define IPS_DEVICEID 0x002E + #define TIMEOUT_10 0x10 + #define TIMEOUT_60 0x20 + #define TIMEOUT_20M 0x30 + #define STATUS_SIZE 4 + #define STATUS_Q_SIZE (MAX_CMDS+1) * STATUS_SIZE + #define ONE_MSEC 1 + #define ONE_SEC 1000 /* * Adapter Basic Status Codes @@ -196,24 +196,24 @@ /* * Adapter Extended Status Equates */ - #define SELECTION_TIMEOUT 0xF0 - #define DATA_OVER_UNDER_RUN 0xF2 - #define EXT_HOST_RESET 0xF7 - #define EXT_DEVICE_RESET 0xF8 - #define EXT_RECOVERY 0xFC - #define EXT_CHECK_CONDITION 0xFF + #define SELECTION_TIMEOUT 0xF0 + #define DATA_OVER_UNDER_RUN 0xF2 + #define EXT_HOST_RESET 0xF7 + #define EXT_DEVICE_RESET 0xF8 + #define EXT_RECOVERY 0xFC + #define EXT_CHECK_CONDITION 0xFF /* * Operating System Defines */ - #define OS_WINDOWS_NT 0x01 - #define OS_NETWARE 0x02 - #define OS_OPENSERVER 0x03 - #define OS_UNIXWARE 0x04 - #define OS_SOLARIS 0x05 - #define OS_OS2 0x06 - #define OS_LINUX 0x07 - #define OS_FREEBSD 0x08 + #define OS_WINDOWS_NT 0x01 + #define OS_NETWARE 0x02 + #define OS_OPENSERVER 0x03 + #define OS_UNIXWARE 0x04 + #define OS_SOLARIS 0x05 + #define OS_OS2 0x06 + #define OS_LINUX 0x07 + #define OS_FREEBSD 0x08 /* * Adapter Command/Status Packet Definitions @@ -225,11 +225,11 @@ /* * Logical Drive Equates */ - #define OFF_LINE 0x02 - #define OKAY 0x03 - #define FREE 0x00 - #define SYS 0x06 - #define CRS 0x24 + #define OFF_LINE 0x02 + #define OKAY 0x03 + #define FREE 0x00 + #define SYS 0x06 + #define CRS 0x24 /* * DCDB Table Equates @@ -237,6 +237,8 @@ #define NO_DISCONNECT 0x00 #define DISCONNECT_ALLOWED 0x80 #define NO_AUTO_REQUEST_SENSE 0x40 + #define IPS_DATA_NONE 0x00 + #define IPS_DATA_UNK 0x00 #define IPS_DATA_IN 0x01 #define IPS_DATA_OUT 0x02 #define TRANSFER_64K 0x08 @@ -734,6 +736,7 @@ typedef struct ips_ha { u8 ha_id[MAX_CHANNELS+1]; + u32 dcdb_active[MAX_CHANNELS]; u32 io_addr; /* Base I/O address */ u8 irq; /* IRQ for adapter */ u8 ntargets; /* Number of targets */ diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c --- v2.3.33/linux/drivers/scsi/ncr53c8xx.c Tue Dec 7 09:32:46 1999 +++ linux/drivers/scsi/ncr53c8xx.c Mon Dec 20 15:36:46 1999 @@ -73,7 +73,7 @@ */ /* -** Sep 10 1999, version 3.2c +** December 6 1999, version 3.2d ** ** Supported SCSI-II features: ** Synchronous negotiation @@ -93,6 +93,7 @@ ** 53C895 (Wide, Fast 40, on board rom BIOS) ** 53C895A (Wide, Fast 40, on board rom BIOS) ** 53C896 (Wide, Fast 40, on board rom BIOS) +** 53C1510D (Wide, Fast 40, on board rom BIOS) ** ** Other features: ** Memory mapped IO (linux-1.3.X and above only) @@ -103,7 +104,7 @@ /* ** Name and version of the driver */ -#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - 3.2c" +#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - version 3.2d" #define SCSI_NCR_DEBUG_FLAGS (0) @@ -511,6 +512,134 @@ #endif /* +** Simple Wrapper to kernel PCI bus interface. +** +** This wrapper allows to get rid of old kernel PCI interface +** and still allows to preserve linux-2.0 compatibilty. +** In fact, it is mostly an incomplete emulation of the new +** PCI code for pre-2.2 kernels. When kernel-2.0 support +** will be dropped, we will just have to remove most of this +** code. +*/ + +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) + +typedef struct pci_dev *pcidev_t; +#define PCIDEV_NULL (0) +#define PciBusNumber(d) (d)->bus->number +#define PciDeviceFn(d) (d)->devfn +#define PciVendorId(d) (d)->vendor +#define PciDeviceId(d) (d)->device +#define PciIrqLine(d) (d)->irq + +#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,12) + +static int __init +pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) +{ + *base = pdev->resource[index].start; + if ((pdev->resource[index].flags & 0x7) == 0x4) + ++index; + return ++index; +} +#else +static int __init +pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) +{ + *base = pdev->base_address[index++]; + if ((*base & 0x7) == 0x4) { +#if BITS_PER_LONG > 32 + *base |= (((u_long)pdev->base_address[index]) << 32); +#endif + ++index; + } + return index; +} +#endif + +#else /* Incomplete emulation of current PCI code for pre-2.2 kernels */ + +typedef unsigned int pcidev_t; +#define PCIDEV_NULL (~0u) +#define PciBusNumber(d) ((d)>>8) +#define PciDeviceFn(n) ((d)&0xff) +#define __PciDev(busn, devfn) (((busn)<<8)+(devfn)) + +#define pci_present pcibios_present + +#define pci_read_config_byte(d, w, v) \ + pcibios_read_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v) +#define pci_read_config_word(d, w, v) \ + pcibios_read_config_word(PciBusNumber(d), PciDeviceFn(d), w, v) +#define pci_read_config_dword(d, w, v) \ + pcibios_read_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v) + + +#define pci_write_config_byte(d, w, v) \ + pcibios_write_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v) +#define pci_write_config_word(d, w, v) \ + pcibios_write_config_word(PciBusNumber(d), PciDeviceFn(d), w, v) +#define pci_write_config_dword(d, w, v) \ + pcibios_write_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v) + +static pcidev_t __init +pci_find_device(unsigned int vendor, unsigned int device, pcidev_t prev) +{ + static unsigned short pci_index; + int retv; + unsigned char bus_number, device_fn; + + if (prev == PCIDEV_NULL) + pci_index = 0; + else + ++pci_index; + retv = pcibios_find_device (vendor, device, pci_index, + &bus_number, &device_fn); + return retv ? PCIDEV_NULL : __PciDev(bus_number, device_fn); +} + +static u_short __init PciVendorId(pcidev_t dev) +{ + u_short vendor_id; + pcibios_read_config_word(dev, PCI_VENDOR_ID, &vendor_id); + return vendor_id; +} + +static u_short __init PciDeviceId(pcidev_t dev) +{ + u_short device_id; + pci_read_config_word(dev, PCI_DEVICE_ID, &device_id); + return device_id; +} + +static u_int __init PciIrqLine(pcidev_t dev) +{ + u_short irq; + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); + return irq; +} + +static int __init +pci_get_base_address(pcidev_t dev, int offset, u_long *base) +{ + u_int32 tmp; + + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp); + *base = tmp; + offset += sizeof(u_int32); + if ((tmp & 0x7) == 0x4) { +#if BITS_PER_LONG > 32 + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp); + *base |= (((u_long)tmp) << 32); +#endif + offset += sizeof(u_int32); + } + return offset; +} + +#endif /* LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) */ + +/* ** SMP threading. ** ** Assuming that SMP systems are generally high end systems and may @@ -537,20 +666,11 @@ #define NCR_LOCK_NCB(np, flags) spin_lock_irqsave(&np->smp_lock, flags) #define NCR_UNLOCK_NCB(np, flags) spin_unlock_irqrestore(&np->smp_lock, flags) -# if LINUX_VERSION_CODE < LinuxVersionCode(2,3,99) - -# define NCR_LOCK_SCSI_DONE(np, flags) \ +#define NCR_LOCK_SCSI_DONE(np, flags) \ spin_lock_irqsave(&io_request_lock, flags) -# define NCR_UNLOCK_SCSI_DONE(np, flags) \ +#define NCR_UNLOCK_SCSI_DONE(np, flags) \ spin_unlock_irqrestore(&io_request_lock, flags) -# else - -# define NCR_LOCK_SCSI_DONE(np, flags) do {;} while (0) -# define NCR_UNLOCK_SCSI_DONE(np, flags) do {;} while (0) - -# endif - #else #if 0 /* not yet needed */ @@ -733,6 +853,12 @@ ** /proc directory entry and proc_info function */ +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27) +static struct proc_dir_entry proc_scsi_ncr53c8xx = { + PROC_SCSI_NCR53C8XX, 9, "ncr53c8xx", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; +#endif #ifdef SCSI_NCR_PROC_INFO_SUPPORT static int ncr53c8xx_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int func); @@ -773,7 +899,8 @@ #define bootverbose (np->verbose) #ifdef SCSI_NCR_NVRAM_SUPPORT -static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21}; +static u_char Tekram_sync[16] __initdata = + {25,31,37,43, 50,62,75,125, 12,15,18,21, 6,7,9,10}; #endif /* SCSI_NCR_NVRAM_SUPPORT */ /* @@ -1763,6 +1890,8 @@ */ u_short device_id; /* PCI device id */ u_char revision_id; /* PCI device revision id */ + u_char bus; /* PCI BUS number */ + u_char device_fn; /* PCI BUS device and function */ u_long port; /* IO space base address */ u_int irq; /* IRQ level */ u_int features; /* Chip features map */ @@ -2060,6 +2189,7 @@ #define reset_waiting_list(np) process_waiting_list((np), DID_RESET) #ifdef SCSI_NCR_NVRAM_SUPPORT +static void ncr_get_nvram (ncr_device *devp, ncr_nvram *nvp); static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram); static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram); #endif @@ -3938,7 +4068,7 @@ if (tn->flags & TEKRAM_SYNC_NEGO) { i = tn->sync_index & 0xf; - tp->usrsync = i < 12 ? Tekram_sync[i] : 255; + tp->usrsync = Tekram_sync[i]; } tp->usrwide = (tn->flags & TEKRAM_WIDE_NEGO) ? 1 : 0; @@ -4328,7 +4458,7 @@ int sync, j; struct Tekram_target *tn = &nvram->target[i]; j = tn->sync_index & 0xf; - sync = j < 12 ? Tekram_sync[j] : 255; + sync = Tekram_sync[j]; printk(KERN_DEBUG "%s-%d:%s%s%s%s%s%s PERIOD=%d\n", ncr_name(np), i, (tn->flags & TEKRAM_PARITY_CHECK) ? " PARITY" : "", @@ -4362,16 +4492,21 @@ ncr_nvram *nvram = device->nvram; int i; + printk(KERN_INFO "ncr53c%s-%d: rev 0x%x on pci bus %d device %d function %d " +#ifdef __sparc__ + "irq %s\n", +#else + "irq %d\n", +#endif + device->chip.name, unit, device->chip.revision_id, + device->slot.bus, (device->slot.device_fn & 0xf8) >> 3, + device->slot.device_fn & 7, #ifdef __sparc__ -printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%s\n", - device->chip.name, unit, device->chip.revision_id, device->slot.base, - device->slot.io_port, __irq_itoa(device->slot.irq)); -#else -printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n", - device->chip.name, unit, device->chip.revision_id, device->slot.base, - device->slot.io_port, device->slot.irq); + __irq_itoa(device->slot.irq)); +#else + device->slot.irq); #endif - + /* ** Allocate host_data structure */ @@ -4402,6 +4537,8 @@ sprintf(np->inst_name, "ncr53c%s-%d", np->chip_name, np->unit); np->device_id = device->chip.device_id; np->revision_id = device->chip.revision_id; + np->bus = device->slot.bus; + np->device_fn = device->slot.device_fn; np->features = device->chip.features; np->clock_divn = device->chip.nr_divisor; np->maxoffs = device->chip.offset_max; @@ -4498,7 +4635,11 @@ instance->max_id = np->maxwide ? 16 : 8; instance->max_lun = SCSI_NCR_MAX_LUN; #ifndef NCR_IOMAPPED - instance->base = (unsigned long)np->reg; +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,29) + instance->base = (unsigned long) np->reg; +#else + instance->base = (char *) np->reg; +#endif #endif instance->irq = device->slot.irq; instance->unique_id = device->slot.io_port; @@ -6073,13 +6214,11 @@ /* ** DEL 441 - 53C876 Rev 5 - Part Number 609-0392787/2788 - ITEM 2. ** Disable overlapped arbitration. - ** The 896 Rev 1 is also affected by this errata. + ** All 896 chips are also affected by this errata. */ - if (np->device_id == PCI_DEVICE_ID_NCR_53C875 && - np->revision_id >= 0x10 && np->revision_id <= 0x15) + if (np->device_id == PCI_DEVICE_ID_NCR_53C875) OUTB (nc_ctest0, (1<<5)); - else if (np->device_id == PCI_DEVICE_ID_NCR_53C896 && - np->revision_id <= 0x1) + else if (np->device_id == PCI_DEVICE_ID_NCR_53C896) OUTB (nc_ccntl0, DPR); /* @@ -9217,8 +9356,8 @@ #endif #endif -static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, - uchar bus, uchar device_fn, ncr_device *device); +static int +ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device); /* ** Linux entry point for NCR53C8XX devices detection routine. @@ -9273,114 +9412,43 @@ static ncr_chip ncr_chip_table[] __initdata = SCSI_NCR_CHIP_TABLE; static ushort ncr_chip_ids[] __initdata = SCSI_NCR_CHIP_IDS; -#ifdef SCSI_NCR_NVRAM_SUPPORT -static int __init -ncr_attach_using_nvram(Scsi_Host_Template *tpnt, int nvram_index, int count, ncr_device device[]) -{ - int i, j; - int attach_count = 0; - ncr_nvram *nvram; - ncr_device *devp = 0; /* to shut up gcc */ - - if (!nvram_index) - return 0; - - /* find first Symbios NVRAM if there is one as we need to check it for host boot order */ - for (i = 0, nvram_index = -1; i < count; i++) { - devp = &device[i]; - nvram = devp->nvram; - if (!nvram) - continue; - if (nvram->type == SCSI_NCR_SYMBIOS_NVRAM) { - if (nvram_index == -1) - nvram_index = i; -#ifdef SCSI_NCR_DEBUG_NVRAM - printk("ncr53c8xx: NVRAM: Symbios format Boot Block, 53c%s, PCI bus %d, device %d, function %d\n", - devp->chip.name, devp->slot.bus, - (int) (devp->slot.device_fn & 0xf8) >> 3, - (int) devp->slot.device_fn & 7); - for (j = 0 ; j < 4 ; j++) { - Symbios_host *h = &nvram->data.Symbios.host[j]; - printk("ncr53c8xx: BOOT[%d] device_id=%04x vendor_id=%04x device_fn=%02x io_port=%04x %s\n", - j, h->device_id, h->vendor_id, - h->device_fn, h->io_port, - (h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) ? "SCAN AT BOOT" : ""); - } - } - else if (nvram->type == SCSI_NCR_TEKRAM_NVRAM) { - /* display Tekram nvram data */ - printk("ncr53c8xx: NVRAM: Tekram format data, 53c%s, PCI bus %d, device %d, function %d\n", - devp->chip.name, devp->slot.bus, - (int) (devp->slot.device_fn & 0xf8) >> 3, - (int) devp->slot.device_fn & 7); -#endif - } - } - - if (nvram_index >= 0 && nvram_index < count) - nvram = device[nvram_index].nvram; - else - nvram = 0; - - if (!nvram) - goto out; - - /* - ** check devices in the boot record against devices detected. - ** attach devices if we find a match. boot table records that - ** do not match any detected devices will be ignored. - ** devices that do not match any boot table will not be attached - ** here but will attempt to be attached during the device table - ** rescan. - */ - for (i = 0; i < 4; i++) { - Symbios_host *h = &nvram->data.Symbios.host[i]; - for (j = 0 ; j < count ; j++) { - devp = &device[j]; - if (h->device_fn == devp->slot.device_fn && -#if 0 /* bus number location in nvram ? */ - h->bus == devp->slot.bus && -#endif - h->device_id == devp->chip.device_id) - break; - } - if (j < count && !devp->attach_done) { - if (!ncr_attach (tpnt, attach_count, devp)) - attach_count++; - devp->attach_done = 1; - } - } - -out: - return attach_count; -} -#endif /* SCSI_NCR_NVRAM_SUPPORT */ +/*=================================================================== +** Detect all 53c8xx hosts and then attach them. +** +** If we are using NVRAM, once all hosts are detected, we need to +** check any NVRAM for boot order in case detect and boot order +** differ and attach them using the order in the NVRAM. +** +** If no NVRAM is found or data appears invalid attach boards in +** the the order they are detected. +**=================================================================== +*/ int __init ncr53c8xx_detect(Scsi_Host_Template *tpnt) { - int i, j; - int chips; - int count = 0; - uchar bus, device_fn; - short index; + pcidev_t pcidev; + int i, j, chips, hosts, count; int attach_count = 0; - ncr_device device[8]; + ncr_device *devtbl, *devp; #ifdef SCSI_NCR_NVRAM_SUPPORT - ncr_nvram nvram[4]; - int k, nvrams; + ncr_nvram nvram0, nvram, *nvp; #endif - int hosts; -#ifdef SCSI_NCR_NVRAM_SUPPORT - int nvram_index = 0; -#endif - -#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT - ncr_debug = driver_setup.debug; -#endif + /* + ** PCI is required. + */ + if (!pci_present()) + return 0; - tpnt->proc_name = "ncr53c8xx"; + /* + ** Initialize driver general stuff. + */ #ifdef SCSI_NCR_PROC_INFO_SUPPORT +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27) + tpnt->proc_dir = &proc_scsi_ncr53c8xx; +#else + tpnt->proc_name = "ncr53c8xx"; +#endif tpnt->proc_info = ncr53c8xx_proc_info; #endif @@ -9388,145 +9456,194 @@ if (ncr53c8xx) ncr53c8xx_setup(ncr53c8xx); #endif +#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT + ncr_debug = driver_setup.debug; +#endif if (initverbose >= 2) ncr_print_driver_setup(); - /* - ** Detect all 53c8xx hosts and then attach them. - ** - ** If we are using NVRAM, once all hosts are detected, we need to check - ** any NVRAM for boot order in case detect and boot order differ and - ** attach them using the order in the NVRAM. - ** - ** If no NVRAM is found or data appears invalid attach boards in the - ** the order they are detected. + /* + ** Allocate the device table since we donnot want to + ** overflow the kernel stack. + ** 1 x 4K PAGE is enough for more than 40 devices for i386. */ - -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,92) - if (!pci_present()) -#else - if (!pcibios_present()) -#endif + devtbl = kmalloc(4000, GFP_ATOMIC); + if (!devtbl) return 0; + /* + ** Detect all 53c8xx hosts. + ** Save the first Symbios NVRAM content if any + ** for the boot order. + */ chips = sizeof(ncr_chip_ids) / sizeof(ncr_chip_ids[0]); - hosts = sizeof(device) / sizeof(device[0]); + hosts = 4000 / sizeof(*devtbl); #ifdef SCSI_NCR_NVRAM_SUPPORT - k = 0; - if (driver_setup.use_nvram & 0x1) - nvrams = sizeof(nvram) / sizeof(nvram[0]); - else - nvrams = 0; + nvp = (driver_setup.use_nvram & 0x1) ? &nvram0 : 0; #endif - - for (j = 0; j < chips ; ++j) { - i = driver_setup.reverse_probe ? chips-1 - j : j; - for (index = 0; ; index++) { - char *msg = ""; - if ((pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i], - index, &bus, &device_fn)) || - (count == hosts)) + j = 0; + count = 0; + pcidev = PCIDEV_NULL; + while (1) { + char *msg = ""; + if (count >= hosts) + break; + if (j >= chips) + break; + i = driver_setup.reverse_probe ? chips - 1 - j : j; + pcidev = pci_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i], + pcidev); + if (pcidev == PCIDEV_NULL) { + ++j; + continue; + } + /* Some HW as the HP LH4 may report twice PCI devices */ + for (i = 0; i < count ; i++) { + if (devtbl[i].slot.bus == PciBusNumber(pcidev) && + devtbl[i].slot.device_fn == PciDeviceFn(pcidev)) break; + } + if (i != count) /* Ignore this device if we already have it */ + continue; + devp = &devtbl[count]; + devp->host_id = driver_setup.host_id; + devp->attach_done = 0; + if (ncr53c8xx_pci_init(tpnt, pcidev, devp)) { + continue; + } + ++count; #ifdef SCSI_NCR_NVRAM_SUPPORT - device[count].nvram = k < nvrams ? &nvram[k] : 0; -#else - device[count].nvram = 0; -#endif - device[count].host_id = driver_setup.host_id; - if (ncr53c8xx_pci_init(tpnt, bus, device_fn, &device[count])) { - device[count].nvram = 0; - continue; - } -#ifdef SCSI_NCR_NVRAM_SUPPORT - if (device[count].nvram) { - ++k; - nvram_index |= device[count].nvram->type; - switch (device[count].nvram->type) { - case SCSI_NCR_TEKRAM_NVRAM: - msg = "with Tekram NVRAM"; - break; - case SCSI_NCR_SYMBIOS_NVRAM: - msg = "with Symbios NVRAM"; - break; - default: - msg = ""; - device[count].nvram = 0; - --k; - } + if (nvp) { + ncr_get_nvram(devp, nvp); + switch(nvp->type) { + case SCSI_NCR_SYMBIOS_NVRAM: + /* + * Switch to the other nvram buffer, so that + * nvram0 will contain the first Symbios + * format NVRAM content with boot order. + */ + nvp = &nvram; + msg = "with Symbios NVRAM"; + break; + case SCSI_NCR_TEKRAM_NVRAM: + msg = "with Tekram NVRAM"; + break; } -#endif - printk(KERN_INFO "ncr53c8xx: 53c%s detected %s\n", - device[count].chip.name, msg); - ++count; } +#endif + printk(KERN_INFO "ncr53c8xx: 53c%s detected %s\n", + devp->chip.name, msg); } + + /* + ** If we have found a SYMBIOS NVRAM, use first the NVRAM boot + ** sequence as device boot order. + ** check devices in the boot record against devices detected. + ** attach devices if we find a match. boot table records that + ** do not match any detected devices will be ignored. + ** devices that do not match any boot table will not be attached + ** here but will attempt to be attached during the device table + ** rescan. + */ #ifdef SCSI_NCR_NVRAM_SUPPORT - attach_count = ncr_attach_using_nvram(tpnt, nvram_index, count, device); + if (!nvp || nvram0.type != SCSI_NCR_SYMBIOS_NVRAM) + goto next; + for (i = 0; i < 4; i++) { + Symbios_host *h = &nvram0.data.Symbios.host[i]; + for (j = 0 ; j < count ; j++) { + devp = &devtbl[j]; + if (h->device_fn != devp->slot.device_fn || + h->bus_nr != devp->slot.bus || + h->device_id != devp->chip.device_id) + continue; + if (devp->attach_done) + continue; + ncr_get_nvram(devp, nvp); + if (!ncr_attach (tpnt, attach_count, devp)) + attach_count++; + devp->attach_done = 1; + break; + } + } +next: #endif + /* - ** rescan device list to make sure all boards attached. - ** devices without boot records will not be attached yet - ** so try to attach them here. + ** Rescan device list to make sure all boards attached. + ** Devices without boot records will not be attached yet + ** so try to attach them here. */ for (i= 0; i < count; i++) { - if (!device[i].attach_done && - !ncr_attach (tpnt, attach_count, &device[i])) { - attach_count++; + devp = &devtbl[i]; + if (!devp->attach_done) { +#ifdef SCSI_NCR_NVRAM_SUPPORT + ncr_get_nvram(devp, nvp); +#endif + if (!ncr_attach (tpnt, attach_count, devp)) + attach_count++; } } + kfree(devtbl); + return attach_count; } -/* -** Generically read a base address from the PCI configuration space. -** Return the offset immediately after the base address that has -** been read. Btw, we blindly assume that the high 32 bits of 64 bit -** base addresses are set to zero on 32 bit architectures. +/*=================================================================== +** Detect and try to read SYMBIOS and TEKRAM NVRAM. +** +** Data can be used to order booting of boards. ** +** Data is saved in ncr_device structure if NVRAM found. This +** is then used to find drive boot order for ncr_attach(). +** +** NVRAM data is passed to Scsi_Host_Template later during +** ncr_attach() for any device set up. +*=================================================================== */ -#if LINUX_VERSION_CODE <= LinuxVersionCode(2,1,92) -static int __init -pci_read_base_address(u_char bus, u_char device_fn, int offset, u_long *base) +#ifdef SCSI_NCR_NVRAM_SUPPORT +static void __init ncr_get_nvram(ncr_device *devp, ncr_nvram *nvp) { - u_int32 tmp; - - pcibios_read_config_dword(bus, device_fn, offset, &tmp); - *base = tmp; - offset += sizeof(u_int32); - if ((tmp & 0x7) == 0x4) { -#if BITS_PER_LONG > 32 - pcibios_read_config_dword(bus, device_fn, offset, &tmp); - *base |= (((u_long)tmp) << 32); + devp->nvram = nvp; + if (!nvp) + return; + /* + ** Get access to chip IO registers + */ +#ifdef NCR_IOMAPPED + request_region(devp->slot.io_port, 128, "ncr53c8xx"); + devp->slot.port = devp->slot.io_port; +#else + devp->slot.reg = (struct ncr_reg *) remap_pci_mem(devp->slot.base, 128); + if (!devp->slot.reg) + return; #endif - offset += sizeof(u_int32); + + /* + ** Try to read SYMBIOS nvram. + ** Try to read TEKRAM nvram if Symbios nvram not found. + */ + if (!ncr_get_Symbios_nvram(&devp->slot, &nvp->data.Symbios)) + nvp->type = SCSI_NCR_SYMBIOS_NVRAM; + else if (!ncr_get_Tekram_nvram(&devp->slot, &nvp->data.Tekram)) + nvp->type = SCSI_NCR_TEKRAM_NVRAM; + else { + nvp->type = 0; + devp->nvram = 0; } - return offset; -} -#elif LINUX_VERSION_CODE <= LinuxVersionCode(2,3,12) -static int __init -pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) -{ - *base = pdev->base_address[index++]; - if ((*base & 0x7) == 0x4) { -#if BITS_PER_LONG > 32 - *base |= (((u_long)pdev->base_address[index]) << 32); + + /* + ** Release access to chip IO registers + */ +#ifdef NCR_IOMAPPED + release_region(devp->slot.port, 128); +#else + unmap_pci_mem((u_long) devp->slot.reg, 128ul); #endif - ++index; - } - return index; -} -#else /* LINUX_VERSION_CODE > LinuxVersionCode(2,3,12) */ -static int __init -pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) -{ - *base = pdev->resource[index].start; - if ((pdev->resource[index].flags & 0x7) == 0x4) - ++index; - return ++index; + } -#endif +#endif /* SCSI_NCR_NVRAM_SUPPORT */ /* ** Read and check the PCI configuration for any detected NCR @@ -9534,18 +9651,13 @@ ** been detected. */ -static int __init ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, - uchar bus, uchar device_fn, ncr_device *device) +static int __init +ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device) { ushort vendor_id, device_id, command; uchar cache_line_size, latency_timer; uchar revision; -#if LINUX_VERSION_CODE > LinuxVersionCode(2,1,92) - struct pci_dev *pdev; uint irq; -#else - uchar irq; -#endif ulong base, base_2, io_port; int i; #ifdef SCSI_NCR_NVRAM_SUPPORT @@ -9555,33 +9667,20 @@ /* ** Read info from the PCI config space. - ** pcibios_read_config_xxx() functions are assumed to be used for + ** pci_read_config_xxx() functions are assumed to be used for ** successfully detected PCI devices. */ -#if LINUX_VERSION_CODE > LinuxVersionCode(2,1,92) - pdev = pci_find_slot(bus, device_fn); - vendor_id = pdev->vendor; - device_id = pdev->device; - irq = pdev->irq; + vendor_id = PciVendorId(pdev); + device_id = PciDeviceId(pdev); + irq = PciIrqLine(pdev); i = 0; i = pci_get_base_address(pdev, i, &io_port); i = pci_get_base_address(pdev, i, &base); (void) pci_get_base_address(pdev, i, &base_2); -#else - pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id); - pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id); - pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq); - i = PCI_BASE_ADDRESS_0; - i = pci_read_base_address(bus, device_fn, i, &io_port); - i = pci_read_base_address(bus, device_fn, i, &base); - (void) pci_read_base_address(bus, device_fn, i, &base_2); -#endif - pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command); - pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION, &revision); - pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, - &cache_line_size); - pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER, - &latency_timer); + pci_read_config_word(pdev, PCI_COMMAND, &command); + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size); + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer); /* ** If user excludes this chip, donnot initialize it. @@ -9631,7 +9730,9 @@ #endif printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n", - bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7); + PciBusNumber(pdev), + (int) (PciDeviceFn(pdev) & 0xf8) >> 3, + (int) (PciDeviceFn(pdev) & 0x7)); if (!chip) { printk("ncr53c8xx: not initializing, device not supported\n"); @@ -9646,8 +9747,8 @@ if (!(command & PCI_COMMAND_MASTER)) { printk("ncr53c8xx: attempting to force PCI_COMMAND_MASTER..."); command |= PCI_COMMAND_MASTER; - pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command); - pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command); + pci_write_config_word(pdev, PCI_COMMAND, command); + pci_read_config_word(pdev, PCI_COMMAND, &command); if (!(command & PCI_COMMAND_MASTER)) { printk("failed!\n"); } else { @@ -9658,8 +9759,8 @@ if (!(command & PCI_COMMAND_IO)) { printk("ncr53c8xx: attempting to force PCI_COMMAND_IO..."); command |= PCI_COMMAND_IO; - pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command); - pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command); + pci_write_config_word(pdev, PCI_COMMAND, command); + pci_read_config_word(pdev, PCI_COMMAND, &command); if (!(command & PCI_COMMAND_IO)) { printk("failed!\n"); } else { @@ -9670,8 +9771,8 @@ if (!(command & PCI_COMMAND_MEMORY)) { printk("ncr53c8xx: attempting to force PCI_COMMAND_MEMORY..."); command |= PCI_COMMAND_MEMORY; - pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command); - pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command); + pci_write_config_word(pdev, PCI_COMMAND, command); + pci_read_config_word(pdev, PCI_COMMAND, &command); if (!(command & PCI_COMMAND_MEMORY)) { printk("failed!\n"); } else { @@ -9685,17 +9786,17 @@ if (io_port >= 0x10000000) { printk("ncr53c8xx: reallocating io_port (Wacky IBM)"); io_port = (io_port & 0x00FFFFFF) | 0x01000000; - pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, io_port); + pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, io_port); } if (base >= 0x10000000) { printk("ncr53c8xx: reallocating base (Wacky IBM)"); base = (base & 0x00FFFFFF) | 0x01000000; - pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, base); + pci_write_config_dword(pdev, PCI_BASE_ADDRESS_1, base); } if (base_2 >= 0x10000000) { printk("ncr53c8xx: reallocating base2 (Wacky IBM)"); base_2 = (base_2 & 0x00FFFFFF) | 0x01000000; - pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_2, base_2); + pci_write_config_dword(pdev, PCI_BASE_ADDRESS_2, base_2); } } #endif @@ -9716,16 +9817,16 @@ if (initverbose >= 2) printk("ncr53c8xx: setting PCI_COMMAND_MASTER bit (fixup)\n"); command |= PCI_COMMAND_MASTER; - pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command); - pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command); + pci_write_config_word(pdev, PCI_COMMAND, command); + pci_read_config_word(pdev, PCI_COMMAND, &command); } if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) { if (initverbose >= 2) printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fixup)\n"); command |= PCI_COMMAND_INVALIDATE; - pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command); - pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command); + pci_write_config_word(pdev, PCI_COMMAND, command); + pci_read_config_word(pdev, PCI_COMMAND, &command); } if ((chip->features & FE_CLSE) && !cache_line_size) { @@ -9733,20 +9834,16 @@ cache_line_size = 64 / sizeof(u_int32); if (initverbose >= 2) printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fixup)\n", cache_line_size); - pcibios_write_config_byte(bus, device_fn, - PCI_CACHE_LINE_SIZE, cache_line_size); - pcibios_read_config_byte(bus, device_fn, - PCI_CACHE_LINE_SIZE, &cache_line_size); + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, cache_line_size); + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size); } if (!latency_timer) { latency_timer = 128; if (initverbose >= 2) printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fixup)\n", latency_timer); - pcibios_write_config_byte(bus, device_fn, - PCI_LATENCY_TIMER, latency_timer); - pcibios_read_config_byte(bus, device_fn, - PCI_LATENCY_TIMER, &latency_timer); + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, latency_timer); + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer); } #endif /* __sparc__ */ @@ -9824,8 +9921,7 @@ case 5: cache_line_size = 8; break; } if (cache_line_size) - (void) pcibios_write_config_byte(bus, device_fn, - PCI_CACHE_LINE_SIZE, cache_line_size); + (void) pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, cache_line_size); if (initverbose) printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size); } @@ -9833,8 +9929,7 @@ if ((driver_setup.pci_fix_up & 2) && cache_line_size && (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) { command |= PCI_COMMAND_INVALIDATE; - (void) pcibios_write_config_word(bus, device_fn, - PCI_COMMAND, command); + (void) pci_write_config_word(pdev, PCI_COMMAND, command); if (initverbose) printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n"); } @@ -9876,8 +9971,7 @@ latency_timer = lt; if (initverbose) printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer); - (void) pcibios_write_config_byte(bus, device_fn, - PCI_LATENCY_TIMER, latency_timer); + (void) pci_write_config_byte(pdev, PCI_LATENCY_TIMER, latency_timer); } } @@ -9900,8 +9994,8 @@ #endif /* SCSI_NCR_PCI_FIX_UP_SUPPORT */ /* initialise ncr_device structure with items required by ncr_attach */ - device->slot.bus = bus; - device->slot.device_fn = device_fn; + device->slot.bus = PciBusNumber(pdev); + device->slot.device_fn = PciDeviceFn(pdev); device->slot.base = base; device->slot.base_2 = base_2; device->slot.io_port = io_port; @@ -10638,29 +10732,28 @@ info.offset = offset; info.pos = 0; - copy_info(&info, "General information:\n"); - copy_info(&info, " Chip NCR53C%s, ", np->chip_name); - copy_info(&info, "device id 0x%x, ", np->device_id); - copy_info(&info, "revision id 0x%x\n", np->revision_id); - - copy_info(&info, " IO port address 0x%lx, ", (u_long) np->port); + copy_info(&info, " Chip NCR53C%s, device id 0x%x, " + "revision id 0x%x\n", + np->chip_name, np->device_id, np->revision_id); + copy_info(&info, " On PCI bus %d, device %d, function %d, " #ifdef __sparc__ - copy_info(&info, "IRQ number %s\n", __irq_itoa(np->irq)); + "IRQ %s\n", #else - copy_info(&info, "IRQ number %d\n", (int) np->irq); + "IRQ %d\n", #endif - -#ifndef NCR_IOMAPPED - if (np->reg) - copy_info(&info, " Using memory mapped IO at virtual address 0x%lx\n", - (u_long) np->reg); + np->bus, (np->device_fn & 0xf8) >> 3, np->device_fn & 7, +#ifdef __sparc__ + __irq_itoa(np->irq)); +#else + (int) np->irq); #endif - copy_info(&info, " Synchronous period factor %d, ", (int) np->minsync); - copy_info(&info, "max commands per lun %d\n", MAX_TAGS); + copy_info(&info, " Synchronous period factor %d, " + "max commands per lun %d\n", + (int) np->minsync, MAX_TAGS); if (driver_setup.debug || driver_setup.verbose > 1) { - copy_info(&info, " Debug flags 0x%x, ", driver_setup.debug); - copy_info(&info, "verbosity level %d\n", driver_setup.verbose); + copy_info(&info, " Debug flags 0x%x, verbosity level %d\n", + driver_setup.debug, driver_setup.verbose); } #ifdef SCSI_NCR_PROFILE_SUPPORT diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- v2.3.33/linux/drivers/scsi/scsi.c Wed Dec 15 10:43:16 1999 +++ linux/drivers/scsi/scsi.c Sat Dec 18 15:36:40 1999 @@ -470,7 +470,10 @@ initialize_merge_fn(SDpnt); - init_waitqueue_head(&SDpnt->device_wait); + /* + * Initialize the object that we will use to wait for command blocks. + */ + init_waitqueue_head(&SDpnt->scpnt_wait); /* * Next, hook the device to the host in question. @@ -901,7 +904,10 @@ SDpnt->device_queue = SCpnt; SDpnt->online = TRUE; - init_waitqueue_head(&SDpnt->device_wait); + /* + * Initialize the object that we will use to wait for command blocks. + */ + init_waitqueue_head(&SDpnt->scpnt_wait); /* * Since we just found one device, there had damn well better be one in the list @@ -984,24 +990,6 @@ #define IN_RESET3 8 -/* This function takes a quick look at a request, and decides if it - * can be queued now, or if there would be a stall while waiting for - * something else to finish. This routine assumes that interrupts are - * turned off when entering the routine. It is the responsibility - * of the calling code to ensure that this is the case. - */ - - -/* This function returns a structure pointer that will be valid for - * the device. The wait parameter tells us whether we should wait for - * the unit to become free or not. We are also able to tell this routine - * not to return a descriptor if the host is unable to accept any more - * commands for the time being. We need to keep in mind that there is no - * guarantee that the host remain not busy. Keep in mind the - * scsi_request_queueable function also knows the internal allocation scheme - * of the packets for each device - */ - /* * This lock protects the freelist for all devices on the system. * We could make this finer grained by having a single lock per @@ -1029,15 +1017,24 @@ * Arguments: device - device for which we want a command descriptor * wait - 1 if we should wait in the event that none * are available. + * interruptible - 1 if we should unblock and return NULL + * in the event that we must wait, and a signal + * arrives. * * Lock status: No locks assumed to be held. This function is SMP-safe. * * Returns: Pointer to command descriptor. * * Notes: Prior to the new queue code, this function was not SMP-safe. + * + * If the wait flag is true, and we are waiting for a free + * command block, this function will interrupt and return + * NULL in the event that a signal arrives that needs to + * be handled. */ -Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait) +Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait, + int interruptable) { struct Scsi_Host *host; Scsi_Cmnd *SCpnt = NULL; @@ -1121,15 +1118,53 @@ * If we have been asked to wait for a free block, then * wait here. */ - spin_unlock_irqrestore(&device_request_lock, flags); if (wait) { + DECLARE_WAITQUEUE(wait, current); + + /* + * We need to wait for a free commandblock. We need to + * insert ourselves into the list before we release the + * lock. This way if a block were released the same + * microsecond that we released the lock, the call + * to schedule() wouldn't block (well, it might switch, + * but the current task will still be schedulable. + */ + add_wait_queue(&device->scpnt_wait, &wait); + if( interruptable ) { + set_current_state(TASK_INTERRUPTIBLE); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + } + + spin_unlock_irqrestore(&device_request_lock, flags); + /* * This should block until a device command block * becomes available. */ - sleep_on(&device->device_wait); + schedule(); + spin_lock_irqsave(&device_request_lock, flags); + + remove_wait_queue(&device->scpnt_wait, &wait); + /* + * FIXME - Isn't this redundant?? Someone + * else will have forced the state back to running. + */ + set_current_state(TASK_RUNNING); + /* + * In the event that a signal has arrived that we need + * to consider, then simply return NULL. Everyone + * that calls us should be prepared for this + * possibility, and pass the appropriate code back + * to the user. + */ + if( interruptable ) { + if (signal_pending(current)) + return NULL; + } } else { + spin_unlock_irqrestore(&device_request_lock, flags); return NULL; } } @@ -1172,11 +1207,22 @@ * * Notes: The command block can no longer be used by the caller once * this funciton is called. This is in effect the inverse - * of scsi_allocate_device/scsi_request_queueable. + * of scsi_allocate_device. Note that we also must perform + * a couple of additional tasks. We must first wake up any + * processes that might have blocked waiting for a command + * block, and secondly we must hit the queue handler function + * to make sure that the device is busy. + * + * The idea is that a lot of the mid-level internals gunk + * gets hidden in this function. Upper level drivers don't + * have any chickens to wave in the air to get things to + * work reliably. */ void scsi_release_command(Scsi_Cmnd * SCpnt) { unsigned long flags; + Scsi_Device * SDpnt; + spin_lock_irqsave(&device_request_lock, flags); SCpnt->request.rq_status = RQ_INACTIVE; @@ -1204,13 +1250,41 @@ atomic_read(&SCpnt->host->eh_wait->count))); up(SCpnt->host->eh_wait); } + + SDpnt = SCpnt->device; + spin_unlock_irqrestore(&device_request_lock, flags); + + /* + * Wake up anyone waiting for this device. Do this after we + * have released the lock, as they will need it as soon as + * they wake up. + */ + wake_up(&SDpnt->scpnt_wait); + + /* + * Finally, hit the queue request function to make sure that + * the device is actually busy if there are requests present. + * This won't block - if the device cannot take any more, life + * will go on. + */ + { + request_queue_t *q; + + q = &SDpnt->request_queue; + scsi_queue_next_request(q, NULL); + } } /* - * This is inline because we have stack problemes if we recurse to deeply. + * Function: scsi_dispatch_command + * + * Purpose: Dispatch a command to the low-level driver. + * + * Arguments: SCpnt - command block we are dispatching. + * + * Notes: */ - int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt) { #ifdef DEBUG_DELAY @@ -1921,8 +1995,6 @@ SCpnt->lun = SDpnt->lun; SCpnt->channel = SDpnt->channel; SCpnt->request.rq_status = RQ_INACTIVE; - SCpnt->host_wait = FALSE; - SCpnt->device_wait = FALSE; SCpnt->use_sg = 0; SCpnt->old_use_sg = 0; SCpnt->old_cmd_len = 0; @@ -2885,7 +2957,7 @@ DECLARE_MUTEX_LOCKED(sem); shpnt->eh_notify = &sem; - send_sig(SIGKILL, shpnt->ehandler, 1); + send_sig(SIGHUP, shpnt->ehandler, 1); down(&sem); shpnt->eh_notify = NULL; } diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h --- v2.3.33/linux/drivers/scsi/scsi.h Wed Dec 15 10:43:16 1999 +++ linux/drivers/scsi/scsi.h Mon Dec 20 16:03:01 1999 @@ -407,6 +407,7 @@ */ extern void initialize_merge_fn(Scsi_Device * SDpnt); extern void scsi_request_fn(request_queue_t * q); +extern void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt); extern int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int); extern int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt); @@ -427,7 +428,7 @@ void (*done) (struct scsi_cmnd *), int timeout, int retries); -extern Scsi_Cmnd *scsi_allocate_device(Scsi_Device *, int); +extern Scsi_Cmnd *scsi_allocate_device(Scsi_Device *, int, int); extern Scsi_Cmnd *scsi_request_queueable(struct request *, Scsi_Device *); @@ -456,7 +457,7 @@ */ struct scsi_device *next; /* Used for linked list */ struct scsi_device *prev; /* Used for linked list */ - wait_queue_head_t device_wait; /* Used to wait if + wait_queue_head_t scpnt_wait; /* Used to wait if device is busy */ struct Scsi_Host *host; request_queue_t request_queue; @@ -636,16 +637,6 @@ * timeout handler is already running. */ unsigned done_late:1; - - /* - * These two flags are used to track commands that are in the - * mid-level queue. The idea is that a command can be there for - * one of two reasons - either the host is busy or the device is - * busy. Thus when a command on the host finishes, we only try - * and requeue commands that we might expect to be queueable. - */ - unsigned host_wait:1; - unsigned device_wait:1; /* Low-level done function - can be used by low-level driver to point * to completion function. Not used by mid/upper level code. */ diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/scsi_error.c linux/drivers/scsi/scsi_error.c --- v2.3.33/linux/drivers/scsi/scsi_error.c Wed Dec 15 10:43:16 1999 +++ linux/drivers/scsi/scsi_error.c Sat Dec 18 15:36:40 1999 @@ -40,8 +40,14 @@ * the host drivers that we are using may be loaded as modules, and * when we unload these, we need to ensure that the error handler thread * can be shut down. + * + * Note - when we unload a module, we send a SIGHUP. We mustn't + * enable SIGTERM, as this is how the init shuts things down when you + * go to single-user mode. For that matter, init also sends SIGKILL, + * so we mustn't enable that one either. We use SIGHUP instead. Other + * options would be SIGPWR, I suppose. */ -#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)) +#define SHUTDOWN_SIGS (sigmask(SIGHUP)) #ifdef DEBUG #define SENSE_TIMEOUT SCSI_TIMEOUT diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c --- v2.3.33/linux/drivers/scsi/scsi_ioctl.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/scsi/scsi_ioctl.c Sat Dec 18 15:36:40 1999 @@ -111,7 +111,12 @@ SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", cmd[0])); - SCpnt = scsi_allocate_device(dev, 1); + SCpnt = scsi_allocate_device(dev, TRUE, TRUE); + if( SCpnt == NULL ) + { + return -EINTR; + } + { DECLARE_MUTEX_LOCKED(sem); SCpnt->request.sem = &sem; @@ -163,8 +168,6 @@ scsi_release_command(SCpnt); SCpnt = NULL; - - wake_up(&SDpnt->device_wait); return result; } @@ -297,7 +300,11 @@ #ifndef DEBUG_NO_CMD - SCpnt = scsi_allocate_device(dev, 1); + SCpnt = scsi_allocate_device(dev, TRUE, TRUE); + if( SCpnt == NULL ) + { + return -EINTR; + } { DECLARE_MUTEX_LOCKED(sem); @@ -328,7 +335,6 @@ result = SCpnt->result; - wake_up(&SCpnt->device->device_wait); SDpnt = SCpnt->device; scsi_release_command(SCpnt); SCpnt = NULL; diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/scsi_lib.c linux/drivers/scsi/scsi_lib.c --- v2.3.33/linux/drivers/scsi/scsi_lib.c Wed Dec 15 10:43:16 1999 +++ linux/drivers/scsi/scsi_lib.c Sat Dec 18 15:36:40 1999 @@ -87,8 +87,10 @@ SCpnt->request.special = (void *) SCpnt; /* - * For the moment, we insert at the head of the queue. This may turn - * out to be a bad idea, but we will see about that when we get there. + * We have the option of inserting the head or the tail of the queue. + * Typically we use the tail for new ioctls and so forth. We use the + * head of the queue for things like a QUEUE_FULL message from a + * device, or a host that is unable to accept a particular command. */ spin_lock_irqsave(&io_request_lock, flags); @@ -97,8 +99,12 @@ q->current_request = &SCpnt->request; } else { /* - * FIXME(eric) - we always insert at the tail of the list. Otherwise - * ioctl commands would always take precedence over normal I/O. + * FIXME(eric) - we always insert at the tail of the + * list. Otherwise ioctl commands would always take + * precedence over normal I/O. An ioctl on a busy + * disk might be delayed indefinitely because the + * request might not float high enough in the queue + * to be scheduled. */ SCpnt->request.next = NULL; if (q->current_request == NULL) { @@ -116,9 +122,9 @@ } /* - * Now hit the requeue function for the queue. If the host is already - * busy, so be it - we have nothing special to do. If the host can queue - * it, then send it off. + * Now hit the requeue function for the queue. If the host is + * already busy, so be it - we have nothing special to do. If + * the host can queue it, then send it off. */ q->request_fn(q); spin_unlock_irqrestore(&io_request_lock, flags); @@ -219,13 +225,41 @@ q->current_request = &SCpnt->request; SCpnt->request.special = (void *) SCpnt; } + /* * Just hit the requeue function for the queue. - * FIXME - if this queue is empty, check to see if we might need to - * start requests for other devices attached to the same host. */ q->request_fn(q); + SDpnt = (Scsi_Device *) q->queuedata; + SHpnt = SDpnt->host; + + /* + * If this is a single-lun device, and we are currently finished + * with this device, then see if we need to get another device + * started. FIXME(eric) - if this function gets too cluttered + * with special case code, then spin off separate versions and + * use function pointers to pick the right one. + */ + if (SDpnt->single_lun + && q->current_request == NULL + && SDpnt->device_busy == 0) { + request_queue_t *q; + + for (SDpnt = SHpnt->host_queue; + SDpnt; + SDpnt = SDpnt->next) { + if (((SHpnt->can_queue > 0) + && (SHpnt->host_busy >= SHpnt->can_queue)) + || (SHpnt->host_blocked) + || (SDpnt->device_blocked)) { + break; + } + q = &SDpnt->request_queue; + q->request_fn(q); + } + } + /* * Now see whether there are other devices on the bus which * might be starved. If so, hit the request function. If we @@ -234,8 +268,6 @@ * flag as the queue function releases the lock and thus some * other device might have become starved along the way. */ - SDpnt = (Scsi_Device *) q->queuedata; - SHpnt = SDpnt->host; all_clear = 1; if (SHpnt->some_device_starved) { for (SDpnt = SHpnt->host_queue; SDpnt; SDpnt = SDpnt->next) { @@ -274,6 +306,9 @@ * * Notes: This is called for block device requests in order to * mark some number of sectors as complete. + * + * We are guaranteeing that the request queue will be goosed + * at some point during this call. */ Scsi_Cmnd *scsi_end_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors) { @@ -311,7 +346,16 @@ * to queue the remainder of them. */ if (req->bh) { + request_queue_t *q; + + q = &SCpnt->device->request_queue; + req->buffer = bh->b_data; + /* + * Bleah. Leftovers again. Stick the leftovers in + * the front of the queue, and goose the queue again. + */ + scsi_queue_next_request(q, SCpnt); return SCpnt; } /* @@ -323,6 +367,11 @@ up(req->sem); } add_blkdev_randomness(MAJOR(req->rq_dev)); + + /* + * This will goose the queue request function at the end, so we don't + * need to worry about launching another command. + */ scsi_release_command(SCpnt); return NULL; } @@ -351,6 +400,19 @@ int this_count = SCpnt->bufflen >> 9; request_queue_t *q = &SCpnt->device->request_queue; + /* + * We must do one of several things here: + * + * Call scsi_end_request. This will finish off the specified + * number of sectors. If we are done, the command block will + * be released, and the queue function will be goosed. If we + * are not done, then scsi_end_request will directly goose + * the the queue. + * + * We can just use scsi_queue_next_request() here. This + * would be used if we just wanted to retry, for example. + * + */ ASSERT_LOCK(&io_request_lock, 0); /* @@ -417,7 +479,6 @@ * rest of the command, or start a new one. */ if (result == 0) { - scsi_queue_next_request(q, SCpnt); return; } } @@ -446,13 +507,13 @@ */ SCpnt->device->changed = 1; SCpnt = scsi_end_request(SCpnt, 0, this_count); - scsi_queue_next_request(q, SCpnt); return; } else { /* - * Must have been a power glitch, or a bus reset. - * Could not have been a media change, so we just retry - * the request and see what happens. + * Must have been a power glitch, or a + * bus reset. Could not have been a + * media change, so we just retry the + * request and see what happens. */ scsi_queue_next_request(q, SCpnt); return; @@ -469,11 +530,14 @@ case ILLEGAL_REQUEST: if (SCpnt->device->ten) { SCpnt->device->ten = 0; + /* + * This will cause a retry with a 6-byte + * command. + */ scsi_queue_next_request(q, SCpnt); result = 0; } else { SCpnt = scsi_end_request(SCpnt, 0, this_count); - scsi_queue_next_request(q, SCpnt); return; } break; @@ -481,7 +545,6 @@ printk(KERN_INFO "Device %x not ready.\n", SCpnt->request.rq_dev); SCpnt = scsi_end_request(SCpnt, 0, this_count); - scsi_queue_next_request(q, SCpnt); return; break; case MEDIUM_ERROR: @@ -492,7 +555,6 @@ print_command(SCpnt->cmnd); print_sense("sd", SCpnt); SCpnt = scsi_end_request(SCpnt, 0, block_sectors); - scsi_queue_next_request(q, SCpnt); return; default: break; @@ -508,7 +570,6 @@ if (driver_byte(result) & DRIVER_SENSE) print_sense("sd", SCpnt); SCpnt = scsi_end_request(SCpnt, 0, SCpnt->request.current_nr_sectors); - scsi_queue_next_request(q, SCpnt); return; } } @@ -603,7 +664,7 @@ */ while (1 == 1) { /* - * If the host cannot accept another request, then quit. + * If the device cannot accept another request, then quit. */ if (SDpnt->device_blocked) { break; @@ -671,7 +732,7 @@ */ recount_segments(SCpnt); } else { - SCpnt = scsi_allocate_device(SDpnt, FALSE); + SCpnt = scsi_allocate_device(SDpnt, FALSE, FALSE); } /* * If so, we are ready to do something. Bump the count @@ -785,29 +846,5 @@ * the request queue and try to find another command. */ spin_lock_irq(&io_request_lock); - } - - /* - * If this is a single-lun device, and we are currently finished - * with this device, then see if we need to get another device - * started. - */ - if (SDpnt->single_lun - && q->current_request == NULL - && SDpnt->device_busy == 0) { - request_queue_t *q; - - for (SDpnt = SHpnt->host_queue; - SDpnt; - SDpnt = SDpnt->next) { - if (((SHpnt->can_queue > 0) - && (SHpnt->host_busy >= SHpnt->can_queue)) - || (SHpnt->host_blocked) - || (SDpnt->device_blocked)) { - break; - } - q = &SDpnt->request_queue; - q->request_fn(q); - } } } diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/scsi_merge.c linux/drivers/scsi/scsi_merge.c --- v2.3.33/linux/drivers/scsi/scsi_merge.c Wed Dec 15 10:43:16 1999 +++ linux/drivers/scsi/scsi_merge.c Sat Dec 18 15:36:40 1999 @@ -66,7 +66,6 @@ * Enable a bunch of additional consistency checking. Turn this off * if you are benchmarking. */ - static int dump_stats(struct request *req, int use_clustering, int dma_host, @@ -109,6 +108,40 @@ #define SANITY_CHECK(req, _CLUSTER, _DMA) #endif +static void dma_exhausted(Scsi_Cmnd * SCpnt, int i) +{ + int jj; + struct scatterlist *sgpnt; + int consumed = 0; + + sgpnt = (struct scatterlist *) SCpnt->request_buffer; + + /* + * Now print out a bunch of stats. First, start with the request + * size. + */ + printk("dma_free_sectors:%d\n", scsi_dma_free_sectors); + printk("use_sg:%d\ti:%d\n", SCpnt->use_sg, i); + printk("request_bufflen:%d\n", SCpnt->request_bufflen); + /* + * Now dump the scatter-gather table, up to the point of failure. + */ + for(jj=0; jj < SCpnt->use_sg; jj++) + { + printk("[%d]\tlen:%d\taddr:%p\talt:%p\n", + jj, + sgpnt[jj].length, + sgpnt[jj].address, + sgpnt[jj].alt_address); + if( sgpnt[jj].alt_address != NULL ) + { + consumed = (sgpnt[jj].length >> 9); + } + } + printk("Total %d sectors consumed\n", consumed); + panic("DMA pool exhausted"); +} + /* * FIXME(eric) - the original disk code disabled clustering for MOD * devices. I have no idea why we thought this was a good idea - my @@ -516,14 +549,15 @@ int use_clustering, int dma_host) { - struct buffer_head *bh; - struct buffer_head *bhprev; - char *buff; - int count; - int i; - struct request *req; - struct scatterlist *sgpnt; - int this_count; + struct buffer_head * bh; + struct buffer_head * bhprev; + char * buff; + int count; + int i; + struct request * req; + int sectors; + struct scatterlist * sgpnt; + int this_count; /* * FIXME(eric) - don't inline this - it doesn't depend on the @@ -647,21 +681,23 @@ */ SCpnt->request_bufflen = 0; for (i = 0; i < count; i++) { + sectors = (sgpnt[i].length >> 9); SCpnt->request_bufflen += sgpnt[i].length; if (virt_to_phys(sgpnt[i].address) + sgpnt[i].length - 1 > ISA_DMA_THRESHOLD) { - if( scsi_dma_free_sectors <= 10 ) { + if( scsi_dma_free_sectors - sectors <= 10 ) { /* - * If the DMA pool is nearly empty, then - * let's stop here. Don't make this request - * any larger. This is kind of a safety valve - * that we use - we could get screwed later on - * if we run out completely. + * If this would nearly drain the DMA + * pool, mpty, then let's stop here. + * Don't make this request any larger. + * This is kind of a safety valve that + * we use - we could get screwed later + * on if we run out completely. */ SCpnt->request_bufflen -= sgpnt[i].length; SCpnt->use_sg = i; if (i == 0) { - panic("DMA pool exhausted"); + goto big_trouble; } break; } @@ -678,7 +714,7 @@ SCpnt->request_bufflen -= sgpnt[i].length; SCpnt->use_sg = i; if (i == 0) { - panic("DMA pool exhausted"); + goto big_trouble; } break; } @@ -690,6 +726,63 @@ } return 1; + big_trouble: + /* + * We come here in the event that we get one humongous + * request, where we need a bounce buffer, and the buffer is + * more than we can allocate in a single call to + * scsi_malloc(). In addition, we only come here when it is + * the 0th element of the scatter-gather table that gets us + * into this trouble. As a fallback, we fall back to + * non-scatter-gather, and ask for a single segment. We make + * a half-hearted attempt to pick a reasonably large request + * size mainly so that we don't thrash the thing with + * iddy-biddy requests. + */ + + /* + * The original number of sectors in the 0th element of the + * scatter-gather table. + */ + sectors = sgpnt[0].length >> 9; + + /* + * Free up the original scatter-gather table. Note that since + * it was the 0th element that got us here, we don't have to + * go in and free up memory from the other slots. + */ + SCpnt->request_bufflen = 0; + SCpnt->use_sg = 0; + scsi_free(SCpnt->request_buffer, SCpnt->sglist_len); + + /* + * Make an attempt to pick up as much as we reasonably can. + * Just keep adding sectors until the pool starts running kind of + * low. The limit of 30 is somewhat arbitrary - the point is that + * it would kind of suck if we dropped down and limited ourselves to + * single-block requests if we had hundreds of free sectors. + */ + if( scsi_dma_free_sectors > 30 ) { + for (this_count = 0, bh = SCpnt->request.bh; + bh; bh = bh->b_reqnext) { + if( scsi_dma_free_sectors < 30 || this_count == sectors ) + { + break; + } + this_count += bh->b_size >> 9; + } + + } else { + /* + * Yow! Take the absolute minimum here. + */ + this_count = SCpnt->request.current_nr_sectors; + } + + /* + * Now drop through into the single-segment case. + */ + single_segment: /* * Come here if for any reason we choose to do this as a single @@ -713,7 +806,7 @@ this_count = SCpnt->request.current_nr_sectors; buff = (char *) scsi_malloc(this_count << 9); if (!buff) { - panic("Unable to allocate DMA buffer\n"); + dma_exhausted(SCpnt, 0); } } if (SCpnt->request.cmd == WRITE) diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/scsi_queue.c linux/drivers/scsi/scsi_queue.c --- v2.3.33/linux/drivers/scsi/scsi_queue.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/scsi/scsi_queue.c Sat Dec 18 15:36:40 1999 @@ -108,7 +108,6 @@ } } host->host_blocked = TRUE; - cmd->host_wait = TRUE; } else { /* * Protect against race conditions. If the device isn't busy, @@ -124,7 +123,6 @@ } } cmd->device->device_blocked = TRUE; - cmd->device_wait = TRUE; } /* diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c --- v2.3.33/linux/drivers/scsi/sd.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/scsi/sd.c Sat Dec 18 15:36:40 1999 @@ -705,7 +705,7 @@ * just after a scsi bus reset. */ - SCpnt = scsi_allocate_device(rscsi_disks[i].device, 1); + SCpnt = scsi_allocate_device(rscsi_disks[i].device, 1, FALSE); buffer = (unsigned char *) scsi_malloc(512); @@ -950,7 +950,6 @@ SCpnt->device->remap = 1; SCpnt->device->sector_size = sector_size; /* Wake up a process waiting for device */ - wake_up(&SCpnt->device->device_wait); scsi_release_command(SCpnt); SCpnt = NULL; diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c --- v2.3.33/linux/drivers/scsi/sg.c Wed Dec 15 10:43:16 1999 +++ linux/drivers/scsi/sg.c Sat Dec 18 15:36:40 1999 @@ -65,6 +65,7 @@ #include #include +static spinlock_t sg_request_lock = SPIN_LOCK_UNLOCKED; int sg_big_buff = SG_DEF_RESERVED_SIZE; /* sg_big_buff is ro through sysctl */ /* N.B. This global is here to keep existing software happy. It now holds @@ -432,9 +433,13 @@ } /* SCSI_LOG_TIMEOUT(7, printk("sg_write: allocating device\n")); */ if (! (SCpnt = scsi_allocate_device(sdp->device, - !(filp->f_flags & O_NONBLOCK)))) - { + !(filp->f_flags & O_NONBLOCK), + TRUE))) { sg_finish_rem_req(srp, NULL, 0); + if( signal_pending(current) ) + { + return -EINTR; + } return -EAGAIN; /* No available command blocks at the moment */ } /* SCSI_LOG_TIMEOUT(7, printk("sg_write: device allocated\n")); */ @@ -1043,7 +1048,7 @@ continue; /* dirty but lowers nesting */ if (sdp->headfp) { /* Need to stop sg_command_done() playing with this list during this loop */ - spin_lock_irqsave(&io_request_lock, flags); + spin_lock_irqsave(&sg_request_lock, flags); sfp = sdp->headfp; while (sfp) { srp = sfp->headrp; @@ -1054,7 +1059,7 @@ } sfp = sfp->nextfp; } - spin_unlock_irqrestore(&io_request_lock, flags); + spin_unlock_irqrestore(&sg_request_lock, flags); SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d, dirty, sleep(3)\n", k)); scsi_sleep(3); /* sleep 3 jiffies, hoping for timeout to go off */ } @@ -1114,9 +1119,9 @@ scsi_add_timer(scpnt, scpnt->timeout_per_command, scsi_old_times_out); #else - spin_unlock_irq(&io_request_lock); + spin_unlock_irq(&sg_request_lock); scsi_sleep(HZ); /* just sleep 1 second and hope ... */ - spin_lock_irq(&io_request_lock); + spin_lock_irq(&sg_request_lock); #endif } diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c --- v2.3.33/linux/drivers/scsi/sr.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/scsi/sr.c Sat Dec 18 15:36:40 1999 @@ -486,7 +486,7 @@ buffer = (unsigned char *) scsi_malloc(512); - SCpnt = scsi_allocate_device(scsi_CDs[i].device, 1); + SCpnt = scsi_allocate_device(scsi_CDs[i].device, 1, FALSE); retries = 3; do { @@ -509,7 +509,6 @@ } while (the_result && retries); - wake_up(&SCpnt->device->device_wait); scsi_release_command(SCpnt); SCpnt = NULL; @@ -659,7 +658,7 @@ int buflen; /* get the device */ - SCpnt = scsi_allocate_device(device, 1); + SCpnt = scsi_allocate_device(device, 1, FALSE); if (SCpnt == NULL) return -ENODEV; /* this just doesn't seem right /axboe */ @@ -864,7 +863,7 @@ sr_hardsizes = NULL; } blksize_size[MAJOR_NR] = NULL; - hardsect_size[MAJOR_NR] = sr_hardsizes; + hardsect_size[MAJOR_NR] = NULL; blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); blk_size[MAJOR_NR] = NULL; read_ahead[MAJOR_NR] = 0; diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c --- v2.3.33/linux/drivers/scsi/sr_ioctl.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/scsi/sr_ioctl.c Sat Dec 18 15:36:40 1999 @@ -59,7 +59,7 @@ char *bounce_buffer; SDev = scsi_CDs[target].device; - SCpnt = scsi_allocate_device(scsi_CDs[target].device, 1); + SCpnt = scsi_allocate_device(scsi_CDs[target].device, 1, FALSE); /* use ISA DMA buffer if necessary */ SCpnt->request.buffer = buffer; @@ -142,7 +142,6 @@ } result = SCpnt->result; /* Wake up a process waiting for device */ - wake_up(&SCpnt->device->device_wait); scsi_release_command(SCpnt); SCpnt = NULL; return err; diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/st.c linux/drivers/scsi/st.c --- v2.3.33/linux/drivers/scsi/st.c Tue Dec 14 01:27:24 1999 +++ linux/drivers/scsi/st.c Sat Dec 18 15:36:40 1999 @@ -289,7 +289,7 @@ unsigned char *bp; if (SCpnt == NULL) - SCpnt = scsi_allocate_device(STp->device, 1); + SCpnt = scsi_allocate_device(STp->device, 1, FALSE); if (SCpnt == NULL) { printk(KERN_ERR "st%d: Can't get SCSI request.\n", TAPE_NR(STp->devt)); diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/sym53c8xx.c linux/drivers/scsi/sym53c8xx.c --- v2.3.33/linux/drivers/scsi/sym53c8xx.c Tue Dec 7 09:32:46 1999 +++ linux/drivers/scsi/sym53c8xx.c Mon Dec 20 15:36:46 1999 @@ -55,7 +55,7 @@ */ /* -** October 3 1999, sym53c8xx 1.5f +** December 6 1999, sym53c8xx 1.5g ** ** Supported SCSI features: ** Synchronous data transfers @@ -73,6 +73,7 @@ ** 53C895 (Wide, Fast 40, on-board rom BIOS) ** 53C895A (Wide, Fast 40, on-board rom BIOS) ** 53C896 (Wide, Fast 40 Dual, on-board rom BIOS) +** 53C1510D (Wide, Fast 40 Dual, on-board rom BIOS) ** ** Other features: ** Memory mapped IO @@ -83,7 +84,7 @@ /* ** Name and version of the driver */ -#define SCSI_NCR_DRIVER_NAME "sym53c8xx - version 1.5f" +#define SCSI_NCR_DRIVER_NAME "sym53c8xx - version 1.5g" /* #define DEBUG_896R1 */ #define SCSI_NCR_OPTIMIZE_896 @@ -449,6 +450,133 @@ #define offsetof(t, m) ((size_t) (&((t *)0)->m)) #endif +/* +** Simple Wrapper to kernel PCI bus interface. +** +** This wrapper allows to get rid of old kernel PCI interface +** and still allows to preserve linux-2.0 compatibilty. +** In fact, it is mostly an incomplete emulation of the new +** PCI code for pre-2.2 kernels. When kernel-2.0 support +** will be dropped, we will just have to remove most of this +** code. +*/ + +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) + +typedef struct pci_dev *pcidev_t; +#define PCIDEV_NULL (0) +#define PciBusNumber(d) (d)->bus->number +#define PciDeviceFn(d) (d)->devfn +#define PciVendorId(d) (d)->vendor +#define PciDeviceId(d) (d)->device +#define PciIrqLine(d) (d)->irq + +#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,12) + +static int __init +pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) +{ + *base = pdev->resource[index].start; + if ((pdev->resource[index].flags & 0x7) == 0x4) + ++index; + return ++index; +} +#else +static int __init +pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) +{ + *base = pdev->base_address[index++]; + if ((*base & 0x7) == 0x4) { +#if BITS_PER_LONG > 32 + *base |= (((u_long)pdev->base_address[index]) << 32); +#endif + ++index; + } + return index; +} +#endif + +#else /* Incomplete emulation of current PCI code for pre-2.2 kernels */ + +typedef unsigned int pcidev_t; +#define PCIDEV_NULL (~0u) +#define PciBusNumber(d) ((d)>>8) +#define PciDeviceFn(n) ((d)&0xff) +#define __PciDev(busn, devfn) (((busn)<<8)+(devfn)) + +#define pci_present pcibios_present + +#define pci_read_config_byte(d, w, v) \ + pcibios_read_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v) +#define pci_read_config_word(d, w, v) \ + pcibios_read_config_word(PciBusNumber(d), PciDeviceFn(d), w, v) +#define pci_read_config_dword(d, w, v) \ + pcibios_read_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v) + +#define pci_write_config_byte(d, w, v) \ + pcibios_write_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v) +#define pci_write_config_word(d, w, v) \ + pcibios_write_config_word(PciBusNumber(d), PciDeviceFn(d), w, v) +#define pci_write_config_dword(d, w, v) \ + pcibios_write_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v) + +static pcidev_t __init +pci_find_device(unsigned int vendor, unsigned int device, pcidev_t prev) +{ + static unsigned short pci_index; + int retv; + unsigned char bus_number, device_fn; + + if (prev == PCIDEV_NULL) + pci_index = 0; + else + ++pci_index; + retv = pcibios_find_device (vendor, device, pci_index, + &bus_number, &device_fn); + return retv ? PCIDEV_NULL : __PciDev(bus_number, device_fn); +} + +static u_short __init PciVendorId(pcidev_t dev) +{ + u_short vendor_id; + pcibios_read_config_word(dev, PCI_VENDOR_ID, &vendor_id); + return vendor_id; +} + +static u_short __init PciDeviceId(pcidev_t dev) +{ + u_short device_id; + pci_read_config_word(dev, PCI_DEVICE_ID, &device_id); + return device_id; +} + +static u_int __init PciIrqLine(pcidev_t dev) +{ + u_short irq; + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); + return irq; +} + +static int __init +pci_get_base_address(pcidev_t dev, int offset, u_long *base) +{ + u_int32 tmp; + + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp); + *base = tmp; + offset += sizeof(u_int32); + if ((tmp & 0x7) == 0x4) { +#if BITS_PER_LONG > 32 + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp); + *base |= (((u_long)tmp) << 32); +#endif + offset += sizeof(u_int32); + } + return offset; +} + +#endif /* LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) */ + /*========================================================== ** ** Debugging tags @@ -504,20 +632,11 @@ #define NCR_LOCK_NCB(np, flags) spin_lock_irqsave(&np->smp_lock, flags) #define NCR_UNLOCK_NCB(np, flags) spin_unlock_irqrestore(&np->smp_lock, flags) -# if LINUX_VERSION_CODE < LinuxVersionCode(2,3,99) - -# define NCR_LOCK_SCSI_DONE(np, flags) \ +#define NCR_LOCK_SCSI_DONE(np, flags) \ spin_lock_irqsave(&io_request_lock, flags) -# define NCR_UNLOCK_SCSI_DONE(np, flags) \ +#define NCR_UNLOCK_SCSI_DONE(np, flags) \ spin_unlock_irqrestore(&io_request_lock, flags) -# else - -# define NCR_LOCK_SCSI_DONE(np, flags) do {;} while (0) -# define NCR_UNLOCK_SCSI_DONE(np, flags) do {;} while (0) - -# endif - #else #define NCR_LOCK_DRIVER(flags) do { save_flags(flags); cli(); } while (0) @@ -560,10 +679,11 @@ #endif #ifdef __sparc__ +#include # define ioremap(base, size) ((u_long) __va(base)) # define iounmap(vaddr) # define pcivtobus(p) ((p) & pci_dvma_mask) -# define memcpy_to_pci(a, b, c) memcpy_toio((u_long) (a), (b), (c)) +# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c)) #elif defined(__alpha__) # define pcivtobus(p) ((p) & 0xfffffffful) # define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c)) @@ -635,9 +755,9 @@ #endif #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) -#define sym53c8xx_get_pages(order) __get_free_pages(GFP_ATOMIC | GFP_DMA_32BIT, order) +#define GetPages(order) __get_free_pages(GFP_ATOMIC | GFP_DMA_32BIT, order) #else -#define sym53c8xx_get_pages(order) __get_free_pages(GFP_ATOMIC | GFP_DMA_32BIT, order, 0) +#define GetPages(order) __get_free_pages(GFP_ATOMIC | GFP_DMA_32BIT, order, 0) #endif /* @@ -668,7 +788,7 @@ j = i; while (!h[j].next) { if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) { - h[j].next = (struct m_link *)sym53c8xx_get_pages(MEMO_PAGE_ORDER); + h[j].next = (struct m_link *) GetPages(MEMO_PAGE_ORDER); if (h[j].next) h[j].next->next = 0; break; @@ -827,7 +947,12 @@ /* ** /proc directory entry and proc_info function */ - +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27) +static struct proc_dir_entry proc_scsi_sym53c8xx = { + PROC_SCSI_SYM53C8XX, 9, NAME53C8XX, + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; +#endif #ifdef SCSI_NCR_PROC_INFO_SUPPORT static int sym53c8xx_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int func); @@ -859,6 +984,17 @@ #define SetScsiResult(cmd, h_sts, s_sts) \ cmd->result = (((h_sts) << 16) + ((s_sts) & 0x7f)) +/* We may have to remind our amnesiac SCSI layer of the reason of the abort */ +#if 0 +#define SetScsiAbortResult(cmd) \ + SetScsiResult( \ + cmd, \ + (cmd)->abort_reason == DID_TIME_OUT ? DID_TIME_OUT : DID_ABORT, \ + 0xff) +#else +#define SetScsiAbortResult(cmd) SetScsiResult(cmd, DID_ABORT, 0xff) +#endif + static void sym53c8xx_select_queue_depths( struct Scsi_Host *host, struct scsi_device *devlist); static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs); @@ -868,7 +1004,8 @@ #define bootverbose (np->verbose) #ifdef SCSI_NCR_NVRAM_SUPPORT -static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21}; +static u_char Tekram_sync[16] __initdata = + {25,31,37,43, 50,62,75,125, 12,15,18,21, 6,7,9,10}; #endif /* SCSI_NCR_NVRAM_SUPPORT */ /* @@ -1775,7 +1912,7 @@ **---------------------------------------------------------------- */ u_char sv_scntl0, sv_scntl3, sv_dmode, sv_dcntl, sv_ctest3, sv_ctest4, - sv_ctest5, sv_gpcntl, sv_stest2, sv_stest4; + sv_ctest5, sv_gpcntl, sv_stest2, sv_stest4, sv_stest1; /*---------------------------------------------------------------- ** Actual initial value of IO register bits used by the @@ -1831,7 +1968,8 @@ */ u_short device_id; /* PCI device id */ u_char revision_id; /* PCI device revision id */ - u_int features; /* Chip features map */ + u_char bus; /* PCI BUS number */ + u_char device_fn; /* PCI BUS device and function */ u_char myaddr; /* SCSI id of the adapter */ u_char maxburst; /* log base 2 of dwords burst */ u_char maxwide; /* Maximum transfer width */ @@ -1841,6 +1979,7 @@ u_char multiplier; /* Clock multiplier (1,2,4) */ u_char clock_divn; /* Number of clock divisors */ u_long clock_khz; /* SCSI clock frequency in KHz */ + u_int features; /* Chip features map */ /*---------------------------------------------------------------- ** Range for the PCI clock frequency measurement result @@ -2213,6 +2352,7 @@ static int ncr_wakeup_done (ncb_p np); static void ncr_start_next_ccb (ncb_p np, lcb_p lp, int maxn); static void ncr_put_start_queue(ncb_p np, ccb_p cp); +static void ncr_chip_reset (ncb_p np); static void ncr_soft_reset (ncb_p np); static void ncr_start_reset (ncb_p np); static int ncr_reset_scsi_bus (ncb_p np, int enab_int, int settle_delay); @@ -2235,9 +2375,10 @@ #define reset_waiting_list(np) process_waiting_list((np), DID_RESET) #ifdef SCSI_NCR_NVRAM_SUPPORT -static void ncr_get_nvram (ncr_device *devp, ncr_nvram *nvp); -static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram); -static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram); +static void ncr_get_nvram (ncr_device *devp, ncr_nvram *nvp); +static int sym_read_Tekram_nvram (ncr_slot *np, u_short device_id, + Tekram_nvram *nvram); +static int sym_read_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram); #endif /*========================================================== @@ -4558,7 +4699,7 @@ if (tn->flags & TEKRAM_SYNC_NEGO) { i = tn->sync_index & 0xf; - tp->usrsync = i < 12 ? Tekram_sync[i] : 255; + tp->usrsync = Tekram_sync[i]; } tp->usrwide = (tn->flags & TEKRAM_WIDE_NEGO) ? 1 : 0; @@ -4576,16 +4717,12 @@ } #endif /* SCSI_NCR_NVRAM_SUPPORT */ -static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram) +/* +** Save initial settings of some IO registers. +** Assumed to have been set by BIOS. +*/ +static void __init ncr_save_initial_setting(ncb_p np) { - u_char burst_max; - u_long period; - int i; - - /* - ** Save assumed BIOS setting - */ - np->sv_scntl0 = INB(nc_scntl0) & 0x0a; np->sv_scntl3 = INB(nc_scntl3) & 0x07; np->sv_dmode = INB(nc_dmode) & 0xce; @@ -4596,6 +4733,18 @@ np->sv_gpcntl = INB(nc_gpcntl); np->sv_stest2 = INB(nc_stest2) & 0x20; np->sv_stest4 = INB(nc_stest4); + np->sv_stest1 = INB(nc_stest1); +} + +/* +** Prepare io register values used by ncr_init() +** according to selected and supported features. +*/ +static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram) +{ + u_char burst_max; + u_long period; + int i; /* ** Wide ? @@ -4985,7 +5134,7 @@ int sync, j; struct Tekram_target *tn = &nvram->target[i]; j = tn->sync_index & 0xf; - sync = j < 12 ? Tekram_sync[j] : 255; + sync = Tekram_sync[j]; printk(KERN_DEBUG "%s-%d:%s%s%s%s%s%s PERIOD=%d\n", ncr_name(np), i, (tn->flags & TEKRAM_PARITY_CHECK) ? " PARITY" : "", @@ -5019,16 +5168,21 @@ ncr_nvram *nvram = device->nvram; int i; + printk(KERN_INFO NAME53C "%s-%d: rev 0x%x on pci bus %d device %d function %d " #ifdef __sparc__ -printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=0x%x\n", - device->chip.name, unit, device->chip.revision_id, device->slot.base, - device->slot.io_port, device->slot.irq); -#else -printk(KERN_INFO NAME53C "%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n", - device->chip.name, unit, device->chip.revision_id, device->slot.base, - device->slot.io_port, device->slot.irq); + "irq %s\n", +#else + "irq %d\n", #endif - + device->chip.name, unit, device->chip.revision_id, + device->slot.bus, (device->slot.device_fn & 0xf8) >> 3, + device->slot.device_fn & 7, +#ifdef __sparc__ + __irq_itoa(device->slot.irq)); +#else + device->slot.irq); +#endif + /* ** Allocate host_data structure */ @@ -5054,6 +5208,8 @@ sprintf(np->inst_name, NAME53C "%s-%d", np->chip_name, np->unit); np->device_id = device->chip.device_id; np->revision_id = device->chip.revision_id; + np->bus = device->slot.bus; + np->device_fn = device->slot.device_fn; np->features = device->chip.features; np->clock_divn = device->chip.nr_divisor; np->maxoffs = device->chip.offset_max; @@ -5141,6 +5297,14 @@ #endif /* !defined NCR_IOMAPPED */ /* + ** If on-chip RAM is used, make sure SCRIPTS isn't too large. + */ + if (np->base2_ba && sizeof(struct script) > 4096) { + printk(KERN_ERR "%s: script too large.\n", ncr_name(np)); + goto attach_error; + } + + /* ** Try to map the controller chip into iospace. */ @@ -5171,13 +5335,22 @@ } #endif + /* + ** Save setting of some IO registers, so we will + ** be able to probe specific implementations. + */ + ncr_save_initial_setting (np); + + /* + ** Reset the chip now, since it has been reported + ** that SCSI clock calibration may not work properly + ** if the chip is currently active. + */ + ncr_chip_reset (np); + /* ** Do chip dependent initialization. */ - if (np->base2_ba && sizeof(struct script) > 4096) { - printk(KERN_ERR "%s: script too large.\n", ncr_name(np)); - goto attach_error; - } (void) ncr_prepare_setting(np, nvram); /* @@ -5382,9 +5555,7 @@ ** so, since we may not be safe if ABRT interrupt occurs due ** to the BIOS or previous O/S having enable this interrupt. */ - OUTB (nc_istat, SRST); - UDELAY(10); - OUTB (nc_istat, 0); + ncr_chip_reset(np); /* ** Now check the cache handling of the pci chipset. @@ -5474,7 +5645,11 @@ instance->max_id = np->maxwide ? 16 : 8; instance->max_lun = MAX_LUN; #ifndef NCR_IOMAPPED - instance->base = (unsigned long)np->reg; +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,29) + instance->base = (unsigned long) np->reg; +#else + instance->base = (char *) np->reg; +#endif #endif instance->irq = np->irq; instance->unique_id = np->base_io; @@ -6114,6 +6289,13 @@ **========================================================== */ +static void ncr_chip_reset (ncb_p np) +{ + OUTB (nc_istat, SRST); + UDELAY (10); + OUTB (nc_istat, 0); +} + static void ncr_soft_reset(ncb_p np) { u_char istat; @@ -6135,9 +6317,7 @@ if (!i) printk("%s: unable to abort current chip operation.\n", ncr_name(np)); - OUTB (nc_istat, SRST); - UDELAY(10); - OUTB (nc_istat, 0); + ncr_chip_reset(np); } /*========================================================== @@ -6300,7 +6480,7 @@ * First, look for the scsi command in the waiting list */ if (remove_from_waiting_list(np, cmd)) { - SetScsiResult(cmd, DID_ABORT, 0); + SetScsiAbortResult(cmd); ncr_queue_done_cmd(np, cmd); return SCSI_ABORT_SUCCESS; } @@ -6381,9 +6561,7 @@ */ printk("%s: resetting chip\n", ncr_name(np)); - OUTB (nc_istat, SRST); - UDELAY (100); - OUTB (nc_istat, 0 ); + ncr_chip_reset(np); /* ** Restore bios setting for automatic clock detection. @@ -6486,15 +6664,15 @@ if (cp->xerr_status) { if (cp->xerr_status & XE_PARITY_ERR) { - PRINT_ADDR(cp->cmd); + PRINT_ADDR(cmd); printk ("unrecovered SCSI parity error.\n"); } if (cp->xerr_status & XE_EXTRA_DATA) { - PRINT_ADDR(cp->cmd); + PRINT_ADDR(cmd); printk ("extraneous data discarded.\n"); } if (cp->xerr_status & XE_BAD_PHASE) { - PRINT_ADDR(cp->cmd); + PRINT_ADDR(cmd); printk ("illegal scsi phase (4/5).\n"); } @@ -6602,7 +6780,7 @@ /* ** Transfer aborted */ - SetScsiResult(cmd, DID_ABORT, cp->scsi_status); + SetScsiAbortResult(cmd); } else { int did_status; @@ -6829,22 +7007,23 @@ /* ** DEL 441 - 53C876 Rev 5 - Part Number 609-0392787/2788 - ITEM 2. - ** Disable overlapped arbitration. - ** The 896 Rev 1 needs also this work-around to be applied. + ** Disable overlapped arbitration for all dual-function + ** devices, regardless revision id. + ** We may consider it is a post-chip-design feature. ;-) */ - if (np->device_id == PCI_DEVICE_ID_NCR_53C875 && - np->revision_id >= 0x10 && np->revision_id <= 0x15) + if (np->device_id == PCI_DEVICE_ID_NCR_53C875) OUTB (nc_ctest0, (1<<5)); - else if (np->device_id == PCI_DEVICE_ID_NCR_53C896 && - np->revision_id <= 0x1) + else if (np->device_id == PCI_DEVICE_ID_NCR_53C896) np->rv_ccntl0 |= DPR; /* - ** If 64 bit (53C895A or 53C896) enable 40 bit address table - ** indirect addressing for MOVE. + ** If 64 bit (895A/896/1010) write the CCNTL1 register to + ** enable 40 bit address table indirect addressing for MOVE. + ** Also write CCNTL0 if 64 bit chip, since this register seems + ** to only be used by 64 bit cores. */ - if (np->features & FE_64BIT) { + OUTB (nc_ccntl0, np->rv_ccntl0); OUTB (nc_ccntl1, np->rv_ccntl1); } @@ -6856,7 +7035,6 @@ if (np->features & FE_NOPM) { printk(KERN_INFO "%s: handling phase mismatch from SCRIPTS.\n", ncr_name(np)); - OUTB (nc_ccntl0, np->rv_ccntl0); OUTL (nc_pmjad1, NCB_SCRIPTH_PHYS (np, pm_handle)); OUTL (nc_pmjad2, NCB_SCRIPTH_PHYS (np, pm_handle)); } @@ -9216,18 +9394,20 @@ pm = 0; if (pm) { - dp_scr = pm->ret; - dp_ofs -= pm->sg.size; + dp_scr = scr_to_cpu(pm->ret); + dp_ofs -= scr_to_cpu(pm->sg.size); } /* ** Deduce the index of the sg entry. ** Keep track of the index of the first valid entry. ** If result is dp_sg = MAX_SCATTER, then we are at the - ** end of the data. + ** end of the data and vice-versa. */ tmp = scr_to_cpu(cp->phys.header.goalp); - dp_sg = MAX_SCATTER - (tmp - 8 - (int)dp_scr) / (SCR_SG_SIZE*4); + dp_sg = MAX_SCATTER; + if (dp_scr != tmp) + dp_sg -= (tmp - 8 - (int)dp_scr) / (SCR_SG_SIZE*4); dp_sgmin = MAX_SCATTER - cp->segments; /* @@ -9257,9 +9437,9 @@ } else if (dp_ofs > 0) { while (dp_sg < MAX_SCATTER) { - ++dp_sg; tmp = scr_to_cpu(cp->phys.data[dp_sg].size); dp_ofs -= (tmp & 0xffffff); + ++dp_sg; if (dp_ofs <= 0) break; } @@ -9278,7 +9458,7 @@ ** Save the extreme pointer if needed. */ if (dp_sg > cp->ext_sg || - (dp_sg == cp->ext_sg && dp_ofs < cp->ext_ofs)) { + (dp_sg == cp->ext_sg && dp_ofs > cp->ext_ofs)) { cp->ext_sg = dp_sg; cp->ext_ofs = dp_ofs; } @@ -9312,6 +9492,7 @@ int dp_ofs = ofs; u_int32 dp_scr = INL (nc_temp); u_int32 dp_ret; + u_int32 tmp; u_char hflags; int dp_sg; struct pm_ctx *pm; @@ -9375,8 +9556,10 @@ ** to the main data script. */ pm->ret = cpu_to_scr(dp_ret); - pm->sg.addr = cp->phys.data[dp_sg-1].addr + dp_ofs; - pm->sg.size = cp->phys.data[dp_sg-1].size - dp_ofs; + tmp = scr_to_cpu(cp->phys.data[dp_sg-1].addr); + tmp += scr_to_cpu(cp->phys.data[dp_sg-1].size) + dp_ofs; + pm->sg.addr = cpu_to_scr(tmp); + pm->sg.size = cpu_to_scr(-dp_ofs); out_ok: OUTL (nc_temp, dp_scr); @@ -10935,8 +11118,8 @@ */ static void __init ncr_getclock (ncb_p np, int mult) { - unsigned char scntl3 = INB(nc_scntl3); - unsigned char stest1 = INB(nc_stest1); + unsigned char scntl3 = np->sv_scntl3; + unsigned char stest1 = np->sv_stest1; unsigned f1; np->multiplier = 1; @@ -11238,8 +11421,8 @@ #endif #endif -static int sym53c8xx_pci_init(Scsi_Host_Template *tpnt, - uchar bus, uchar device_fn, ncr_device *device); +static int +sym53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device); /* ** Linux entry point for SYM53C8XX devices detection routine. @@ -11318,26 +11501,27 @@ static void __init ncr_detect_pqs_pds(void) { short index; + pcidev_t dev = PCIDEV_NULL; - for(index=0; index < SCSI_NCR_MAX_PQS_BUS; index ++) { - u_char tmp, bus, device_fn; + for(index=0; index < SCSI_NCR_MAX_PQS_BUS; index++) { + u_char tmp; - if (pcibios_find_device(0x101a, 0x0009, index, &bus, - &device_fn) != PCIBIOS_SUCCESSFUL) { + dev = pci_find_device(0x101a, 0x0009, dev); + if (dev == PCIDEV_NULL) { pqs_bus[index] = -1; break; } - printk(KERN_INFO NAME53C8XX ": NCR PQS/PDS memory controller detected on bus %d\n", bus); - pcibios_read_config_byte(bus, device_fn, 0x44, &tmp); + printk(KERN_INFO NAME53C8XX ": NCR PQS/PDS memory controller detected on bus %d\n", PciBusNumber(dev)); + pci_read_config_byte(dev, 0x44, &tmp); /* bit 1: allow individual 875 configuration */ tmp |= 0x2; - pcibios_write_config_byte(bus, device_fn, 0x44, tmp); - pcibios_read_config_byte(bus, device_fn, 0x45, &tmp); + pci_write_config_byte(dev, 0x44, tmp); + pci_read_config_byte(dev, 0x45, &tmp); /* bit 2: drive individual 875 interrupts to the bus */ tmp |= 0x4; - pcibios_write_config_byte(bus, device_fn, 0x45, tmp); + pci_write_config_byte(dev, 0x45, tmp); - pqs_bus[index] = bus; + pqs_bus[index] = PciBusNumber(dev); } } #endif /* SCSI_NCR_PQS_PDS_SUPPORT */ @@ -11355,9 +11539,8 @@ */ int __init sym53c8xx_detect(Scsi_Host_Template *tpnt) { + pcidev_t pcidev; int i, j, chips, hosts, count; - u_char bus, device_fn; - short index; int attach_count = 0; ncr_device *devtbl, *devp; #ifdef SCSI_NCR_NVRAM_SUPPORT @@ -11367,18 +11550,18 @@ /* ** PCI is required. */ -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,92) if (!pci_present()) -#else - if (!pcibios_present()) -#endif return 0; /* ** Initialize driver general stuff. */ #ifdef SCSI_NCR_PROC_INFO_SUPPORT - tpnt->proc_name = NAME53C8XX; +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27) + tpnt->proc_dir = &proc_scsi_sym53c8xx; +#else + tpnt->proc_name = NAME53C8XX; +#endif tpnt->proc_info = sym53c8xx_proc_info; #endif @@ -11420,8 +11603,8 @@ nvp = (driver_setup.use_nvram & 0x1) ? &nvram0 : 0; #endif j = 0; - index = 0; count = 0; + pcidev = PCIDEV_NULL; while (1) { char *msg = ""; if (count >= hosts) @@ -11429,17 +11612,16 @@ if (j >= chips) break; i = driver_setup.reverse_probe ? chips - 1 - j : j; - if (pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i], - index, &bus, &device_fn)) { + pcidev = pci_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i], + pcidev); + if (pcidev == PCIDEV_NULL) { ++j; - index = 0; continue; } - ++index; /* Some HW as the HP LH4 may report twice PCI devices */ for (i = 0; i < count ; i++) { - if (devtbl[i].slot.bus == bus && - devtbl[i].slot.device_fn == device_fn) + if (devtbl[i].slot.bus == PciBusNumber(pcidev) && + devtbl[i].slot.device_fn == PciDeviceFn(pcidev)) break; } if (i != count) /* Ignore this device if we already have it */ @@ -11447,7 +11629,7 @@ devp = &devtbl[count]; devp->host_id = driver_setup.host_id; devp->attach_done = 0; - if (sym53c8xx_pci_init(tpnt, bus, device_fn, devp)) { + if (sym53c8xx_pci_init(tpnt, pcidev, devp)) { continue; } ++count; @@ -11542,109 +11724,45 @@ } /*=================================================================== -** Generically read a base address from the PCI configuration space. -** Return the offset immediately after the base address that has -** been read. Btw, we blindly assume that the high 32 bits of 64 bit -** base addresses are set to zero on 32 bit architectures. -**=================================================================== -*/ -#if LINUX_VERSION_CODE <= LinuxVersionCode(2,1,92) -static int __init -pci_read_base_address(u_char bus, u_char device_fn, int offset, u_long *base) -{ - u_int32 tmp; - - pcibios_read_config_dword(bus, device_fn, offset, &tmp); - *base = tmp; - offset += sizeof(u_int32); - if ((tmp & 0x7) == 0x4) { -#if BITS_PER_LONG > 32 - pcibios_read_config_dword(bus, device_fn, offset, &tmp); - *base |= (((u_long)tmp) << 32); -#endif - offset += sizeof(u_int32); - } - return offset; -} -#elif LINUX_VERSION_CODE <= LinuxVersionCode(2,3,12) -static int __init -pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) -{ - *base = pdev->base_address[index++]; - if ((*base & 0x7) == 0x4) { -#if BITS_PER_LONG > 32 - *base |= (((u_long)pdev->base_address[index]) << 32); -#endif - ++index; - } - return index; -} -#else /* LINUX_VERSION_CODE > LinuxVersionCode(2,3,12) */ -static int __init -pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) -{ - *base = pdev->resource[index].start; - if ((pdev->resource[index].flags & 0x7) == 0x4) - ++index; - return ++index; -} -#endif - -/*=================================================================== ** Read and check the PCI configuration for any detected NCR ** boards and save data for attaching after all boards have ** been detected. **=================================================================== */ -static int __init sym53c8xx_pci_init(Scsi_Host_Template *tpnt, - uchar bus, uchar device_fn, ncr_device *device) +static int __init +sym53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device) { u_short vendor_id, device_id, command; u_char cache_line_size, latency_timer; u_char suggested_cache_line_size = 0; - u_char pci_fix_up; + u_char pci_fix_up = driver_setup.pci_fix_up; u_char revision; -#if LINUX_VERSION_CODE > LinuxVersionCode(2,1,92) - struct pci_dev *pdev; u_int irq; -#else - u_char irq; -#endif u_long base, base_2, io_port; int i; ncr_chip *chip; printk(KERN_INFO NAME53C8XX ": at PCI bus %d, device %d, function %d\n", - bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7); + PciBusNumber(pdev), + (int) (PciDeviceFn(pdev) & 0xf8) >> 3, + (int) (PciDeviceFn(pdev) & 7)); /* ** Read info from the PCI config space. - ** pcibios_read_config_xxx() functions are assumed to be used for + ** pci_read_config_xxx() functions are assumed to be used for ** successfully detected PCI devices. */ -#if LINUX_VERSION_CODE > LinuxVersionCode(2,1,92) - pdev = pci_find_slot(bus, device_fn); - vendor_id = pdev->vendor; - device_id = pdev->device; - irq = pdev->irq; + vendor_id = PciVendorId(pdev); + device_id = PciDeviceId(pdev); + irq = PciIrqLine(pdev); i = 0; i = pci_get_base_address(pdev, i, &io_port); i = pci_get_base_address(pdev, i, &base); (void) pci_get_base_address(pdev, i, &base_2); -#else - pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id); - pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id); - pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq); - i = PCI_BASE_ADDRESS_0; - i = pci_read_base_address(bus, device_fn, i, &io_port); - i = pci_read_base_address(bus, device_fn, i, &base); - (void) pci_read_base_address(bus, device_fn, i, &base_2); -#endif - pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command); - pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION, &revision); - pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, - &cache_line_size); - pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER, - &latency_timer); + + pci_read_config_word(pdev, PCI_COMMAND, &command); + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size); + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer); #ifdef SCSI_NCR_PQS_PDS_SUPPORT /* @@ -11655,8 +11773,8 @@ */ for(i = 0; i < SCSI_NCR_MAX_PQS_BUS && pqs_bus[i] != -1; i++) { u_char tmp; - if (pqs_bus[i] == bus) { - pcibios_read_config_byte(bus, device_fn, 0x84, &tmp); + if (pqs_bus[i] == PciBusNumber(pdev)) { + pci_read_config_byte(pdev, 0x84, &tmp); device->pqs_pds = 1; device->host_id = tmp; break; @@ -11733,7 +11851,7 @@ (command & PCI_COMMAND_IO) ? "" : " PCI_COMMAND_IO", (command & PCI_COMMAND_MEMORY) ? "" : " PCI_COMMAND_MEMORY"); command |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY); - pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command); + pci_write_config_word(pdev, PCI_COMMAND, command); } #if LINUX_VERSION_CODE < LinuxVersionCode(2,2,0) @@ -11741,20 +11859,20 @@ if (io_port >= 0x10000000) { printk(NAME53C8XX ": reallocating io_port (Wacky IBM)"); io_port = (io_port & 0x00FFFFFF) | 0x01000000; - pcibios_write_config_dword(bus, device_fn, - PCI_BASE_ADDRESS_0, io_port); + pci_write_config_dword(pdev, + PCI_BASE_ADDRESS_0, io_port); } if (base >= 0x10000000) { printk(NAME53C8XX ": reallocating base (Wacky IBM)"); base = (base & 0x00FFFFFF) | 0x01000000; - pcibios_write_config_dword(bus, device_fn, - PCI_BASE_ADDRESS_1, base); + pci_write_config_dword(pdev, + PCI_BASE_ADDRESS_1, base); } if (base_2 >= 0x10000000) { printk(NAME53C8XX ": reallocating base2 (Wacky IBM)"); base_2 = (base_2 & 0x00FFFFFF) | 0x01000000; - pcibios_write_config_dword(bus, device_fn, - PCI_BASE_ADDRESS_2, base_2); + pci_write_config_dword(pdev, + PCI_BASE_ADDRESS_2, base_2); } } #endif @@ -11763,25 +11881,12 @@ #ifdef __sparc__ /* ** Fix-ups for sparc. - ** - ** I wrote: Should not be performed by the driver, - ** Guy wrote: but how can OBP know each and every PCI card, - ** if they don't use Fcode? - ** I replied: no need to know each and every PCI card, just - ** be skilled enough to understand the PCI specs. - */ - - /* - ** PCI configuration is based on configuration registers being - ** coherent with hardware and software resource identifications. - ** This is fairly simple, but seems still too complex for Sparc. */ base = __pa(base); base_2 = __pa(base_2); if (!cache_line_size) suggested_cache_line_size = 16; - #endif /* __sparc__ */ #if defined(__i386__) && !defined(MODULE) @@ -11852,7 +11957,7 @@ (command & PCI_COMMAND_MASTER) ? "" : " PCI_COMMAND_MASTER", (command & PCI_COMMAND_PARITY) ? "" : " PCI_COMMAND_PARITY"); command |= (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY); - pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command); + pci_write_config_word(pdev, PCI_COMMAND, command); } /* @@ -11882,7 +11987,6 @@ ** We must ensure the chip will use WRITE AND INVALIDATE. ** The revision number limit is for now arbitrary. */ - pci_fix_up = driver_setup.pci_fix_up; if (device_id == PCI_DEVICE_ID_NCR_53C896 && revision <= 0x10) { chip->features |= (FE_WRIE | FE_CLSE); pci_fix_up |= 3; /* Force appropriate PCI fix-up */ @@ -11895,8 +11999,8 @@ if ((pci_fix_up & 1) && (chip->features & FE_CLSE) && !cache_line_size && suggested_cache_line_size) { cache_line_size = suggested_cache_line_size; - pcibios_write_config_byte(bus, device_fn, - PCI_CACHE_LINE_SIZE, cache_line_size); + pci_write_config_byte(pdev, + PCI_CACHE_LINE_SIZE, cache_line_size); printk(NAME53C8XX ": PCI_CACHE_LINE_SIZE set to %d (fix-up).\n", cache_line_size); } @@ -11905,7 +12009,7 @@ (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) { printk(NAME53C8XX": setting PCI_COMMAND_INVALIDATE (fix-up)\n"); command |= PCI_COMMAND_INVALIDATE; - pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command); + pci_write_config_word(pdev, PCI_COMMAND, command); } /* @@ -11913,15 +12017,15 @@ ** (latency timer >= burst length + 6, we add 10 to be quite sure) */ - if ((pci_fix_up & 4) && chip->burst_max) { + if (chip->burst_max && (latency_timer == 0 || (pci_fix_up & 4))) { uchar lt = (1 << chip->burst_max) + 6 + 10; if (latency_timer < lt) { - latency_timer = lt; printk(NAME53C8XX - ": setting PCI_LATENCY_TIMER to %d (fix-up).\n", - latency_timer); - pcibios_write_config_byte(bus, device_fn, - PCI_LATENCY_TIMER, latency_timer); + ": changing PCI_LATENCY_TIMER from %d to %d.\n", + (int) latency_timer, (int) lt); + latency_timer = lt; + pci_write_config_byte(pdev, + PCI_LATENCY_TIMER, latency_timer); } } @@ -11930,8 +12034,8 @@ /* ** Initialise ncr_device structure with items required by ncr_attach. */ - device->slot.bus = bus; - device->slot.device_fn = device_fn; + device->slot.bus = PciBusNumber(pdev); + device->slot.device_fn = PciDeviceFn(pdev); device->slot.base = base; device->slot.base_2 = base_2; device->slot.io_port = io_port; @@ -11976,9 +12080,10 @@ ** Try to read SYMBIOS nvram. ** Try to read TEKRAM nvram if Symbios nvram not found. */ - if (!ncr_get_Symbios_nvram(&devp->slot, &nvp->data.Symbios)) + if (!sym_read_Symbios_nvram(&devp->slot, &nvp->data.Symbios)) nvp->type = SCSI_NCR_SYMBIOS_NVRAM; - else if (!ncr_get_Tekram_nvram(&devp->slot, &nvp->data.Tekram)) + else if (!sym_read_Tekram_nvram(&devp->slot, devp->chip.device_id, + &nvp->data.Tekram)) nvp->type = SCSI_NCR_TEKRAM_NVRAM; else { nvp->type = 0; @@ -12690,24 +12795,28 @@ info.pos = 0; copy_info(&info, "General information:\n"); - copy_info(&info, " Chip " NAME53C "%s, ", np->chip_name); - copy_info(&info, "device id 0x%x, ", np->device_id); - copy_info(&info, "revision id 0x%x\n", np->revision_id); - - copy_info(&info, " IO port address 0x%lx, ", (u_long) np->base_io); - copy_info(&info, "IRQ number %d\n", (int) np->irq); - -#ifndef NCR_IOMAPPED - if (np->reg) - copy_info(&info, " Using memory mapped IO at virtual address 0x%lx\n", - (u_long) np->reg); + copy_info(&info, " Chip " NAME53C "%s, device id 0x%x, " + "revision id 0x%x\n", + np->chip_name, np->device_id, np->revision_id); + copy_info(&info, " On PCI bus %d, device %d, function %d, " +#ifdef __sparc__ + "IRQ %s\n", +#else + "IRQ %d\n", +#endif + np->bus, (np->device_fn & 0xf8) >> 3, np->device_fn & 7, +#ifdef __sparc__ + __irq_itoa(np->irq)); +#else + (int) np->irq); #endif - copy_info(&info, " Synchronous period factor %d, ", (int) np->minsync); - copy_info(&info, "max commands per lun %d\n", MAX_TAGS); + copy_info(&info, " Synchronous period factor %d, " + "max commands per lun %d\n", + (int) np->minsync, MAX_TAGS); if (driver_setup.debug || driver_setup.verbose > 1) { - copy_info(&info, " Debug flags 0x%x, ", driver_setup.debug); - copy_info(&info, "verbosity level %d\n", driver_setup.verbose); + copy_info(&info, " Debug flags 0x%x, verbosity level %d\n", + driver_setup.debug, driver_setup.verbose); } #ifdef SCSI_NCR_PROFILE_SUPPORT @@ -12794,255 +12903,212 @@ #ifdef SCSI_NCR_NVRAM_SUPPORT -/* --------------------------------------------------------------------- -** -** Try reading Symbios format nvram -** -** --------------------------------------------------------------------- -** -** GPOI0 - data in/data out -** GPIO1 - clock -** -** return 0 if NVRAM data OK, 1 if NVRAM data not OK -** --------------------------------------------------------------------- -*/ +/* + * 24C16 EEPROM reading. + * + * GPOI0 - data in/data out + * GPIO1 - clock + * Symbios NVRAM wiring now also used by Tekram. + */ #define SET_BIT 0 #define CLR_BIT 1 #define SET_CLK 2 #define CLR_CLK 3 -static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl); -static void nvram_start(ncr_slot *np, u_char *gpreg); -static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl); -static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl); -static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl); -static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl); -static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg); -static void nvram_stop(ncr_slot *np, u_char *gpreg); -static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode); - -static int __init ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram) +/* + * Set/clear data/clock bit in GPIO0 + */ +static void __init +S24C16_set_bit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode) { - static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0}; - u_char gpcntl, gpreg; - u_char old_gpcntl, old_gpreg; - u_short csum; - u_char ack_data; - int retv = 1; + UDELAY (5); + switch (bit_mode){ + case SET_BIT: + *gpreg |= write_bit; + break; + case CLR_BIT: + *gpreg &= 0xfe; + break; + case SET_CLK: + *gpreg |= 0x02; + break; + case CLR_CLK: + *gpreg &= 0xfd; + break; - /* save current state of GPCNTL and GPREG */ - old_gpreg = INB (nc_gpreg); - old_gpcntl = INB (nc_gpcntl); - gpcntl = old_gpcntl & 0xfc; + } + OUTB (nc_gpreg, *gpreg); + UDELAY (5); +} - /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */ - OUTB (nc_gpreg, old_gpreg); - OUTB (nc_gpcntl, gpcntl); +/* + * Send START condition to NVRAM to wake it up. + */ +static void __init S24C16_start(ncr_slot *np, u_char *gpreg) +{ + S24C16_set_bit(np, 1, gpreg, SET_BIT); + S24C16_set_bit(np, 0, gpreg, SET_CLK); + S24C16_set_bit(np, 0, gpreg, CLR_BIT); + S24C16_set_bit(np, 0, gpreg, CLR_CLK); +} - /* this is to set NVRAM into a known state with GPIO0/1 both low */ - gpreg = old_gpreg; - nvram_setBit(np, 0, &gpreg, CLR_CLK); - nvram_setBit(np, 0, &gpreg, CLR_BIT); - - /* now set NVRAM inactive with GPIO0/1 both high */ - nvram_stop(np, &gpreg); - - /* activate NVRAM */ - nvram_start(np, &gpreg); - - /* write device code and random address MSB */ - nvram_write_byte(np, &ack_data, - 0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl); - if (ack_data & 0x01) - goto out; - - /* write random address LSB */ - nvram_write_byte(np, &ack_data, - (SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl); - if (ack_data & 0x01) - goto out; - - /* regenerate START state to set up for reading */ - nvram_start(np, &gpreg); - - /* rewrite device code and address MSB with read bit set (lsb = 0x01) */ - nvram_write_byte(np, &ack_data, - 0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl); - if (ack_data & 0x01) - goto out; - - /* now set up GPIO0 for inputting data */ - gpcntl |= 0x01; - OUTB (nc_gpcntl, gpcntl); - - /* input all active data - only part of total NVRAM */ - csum = nvram_read_data(np, - (u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl); - - /* finally put NVRAM back in inactive mode */ - gpcntl &= 0xfe; - OUTB (nc_gpcntl, gpcntl); - nvram_stop(np, &gpreg); - -#ifdef SCSI_NCR_DEBUG_NVRAM -printk("sym53c8xx: NvRAM type=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n", - nvram->type, - nvram->trailer[0], nvram->trailer[1], nvram->trailer[2], - nvram->trailer[3], nvram->trailer[4], nvram->trailer[5], - nvram->byte_count, sizeof(*nvram) - 12, - nvram->checksum, csum); -#endif - - /* check valid NVRAM signature, verify byte count and checksum */ - if (nvram->type == 0 && - !memcmp(nvram->trailer, Symbios_trailer, 6) && - nvram->byte_count == sizeof(*nvram) - 12 && - csum == nvram->checksum) - retv = 0; -out: - /* return GPIO0/1 to original states after having accessed NVRAM */ - OUTB (nc_gpcntl, old_gpcntl); - OUTB (nc_gpreg, old_gpreg); - - return retv; +/* + * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!! + */ +static void __init S24C16_stop(ncr_slot *np, u_char *gpreg) +{ + S24C16_set_bit(np, 0, gpreg, SET_CLK); + S24C16_set_bit(np, 1, gpreg, SET_BIT); } /* - * Read Symbios NvRAM data and compute checksum. + * Read or write a bit to the NVRAM, + * read if GPIO0 input else write if GPIO0 output */ -static u_short __init -nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl) +static void __init +S24C16_do_bit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg) { - int x; - u_short csum; - - for (x = 0; x < len; x++) - nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl); - - for (x = 6, csum = 0; x < len - 6; x++) - csum += data[x]; + S24C16_set_bit(np, write_bit, gpreg, SET_BIT); + S24C16_set_bit(np, 0, gpreg, SET_CLK); + if (read_bit) + *read_bit = INB (nc_gpreg); + S24C16_set_bit(np, 0, gpreg, CLR_CLK); + S24C16_set_bit(np, 0, gpreg, CLR_BIT); +} - return csum; +/* + * Output an ACK to the NVRAM after reading, + * change GPIO0 to output and when done back to an input + */ +static void __init +S24C16_write_ack(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl) +{ + OUTB (nc_gpcntl, *gpcntl & 0xfe); + S24C16_do_bit(np, 0, write_bit, gpreg); + OUTB (nc_gpcntl, *gpcntl); } /* - * Send START condition to NVRAM to wake it up. + * Input an ACK from NVRAM after writing, + * change GPIO0 to input and when done back to an output */ -static void __init nvram_start(ncr_slot *np, u_char *gpreg) +static void __init +S24C16_read_ack(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl) { - nvram_setBit(np, 1, gpreg, SET_BIT); - nvram_setBit(np, 0, gpreg, SET_CLK); - nvram_setBit(np, 0, gpreg, CLR_BIT); - nvram_setBit(np, 0, gpreg, CLR_CLK); + OUTB (nc_gpcntl, *gpcntl | 0x01); + S24C16_do_bit(np, read_bit, 1, gpreg); + OUTB (nc_gpcntl, *gpcntl); } /* - * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK, - * GPIO0 must already be set as an output + * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK, + * GPIO0 must already be set as an output */ static void __init -nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl) +S24C16_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, + u_char *gpreg, u_char *gpcntl) { int x; for (x = 0; x < 8; x++) - nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg); + S24C16_do_bit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg); - nvram_readAck(np, ack_data, gpreg, gpcntl); + S24C16_read_ack(np, ack_data, gpreg, gpcntl); } /* - * READ a byte from the NVRAM and then send an ACK to say we have got it, - * GPIO0 must already be set as an input + * READ a byte from the NVRAM and then send an ACK to say we have got it, + * GPIO0 must already be set as an input */ static void __init -nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl) +S24C16_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, + u_char *gpreg, u_char *gpcntl) { int x; u_char read_bit; *read_data = 0; for (x = 0; x < 8; x++) { - nvram_doBit(np, &read_bit, 1, gpreg); + S24C16_do_bit(np, &read_bit, 1, gpreg); *read_data |= ((read_bit & 0x01) << (7 - x)); } - nvram_writeAck(np, ack_data, gpreg, gpcntl); + S24C16_write_ack(np, ack_data, gpreg, gpcntl); } /* - * Output an ACK to the NVRAM after reading, - * change GPIO0 to output and when done back to an input + * Read 'len' bytes starting at 'offset'. */ -static void __init -nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl) +static int __init +sym_read_S24C16_nvram (ncr_slot *np, int offset, u_char *data, int len) { - OUTB (nc_gpcntl, *gpcntl & 0xfe); - nvram_doBit(np, 0, write_bit, gpreg); - OUTB (nc_gpcntl, *gpcntl); -} + u_char gpcntl, gpreg; + u_char old_gpcntl, old_gpreg; + u_char ack_data; + int retv = 1; + int x; -/* - * Input an ACK from NVRAM after writing, - * change GPIO0 to input and when done back to an output - */ -static void __init -nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl) -{ - OUTB (nc_gpcntl, *gpcntl | 0x01); - nvram_doBit(np, read_bit, 1, gpreg); - OUTB (nc_gpcntl, *gpcntl); -} + /* save current state of GPCNTL and GPREG */ + old_gpreg = INB (nc_gpreg); + old_gpcntl = INB (nc_gpcntl); + gpcntl = old_gpcntl & 0xfc; -/* - * Read or write a bit to the NVRAM, - * read if GPIO0 input else write if GPIO0 output - */ -static void __init -nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg) -{ - nvram_setBit(np, write_bit, gpreg, SET_BIT); - nvram_setBit(np, 0, gpreg, SET_CLK); - if (read_bit) - *read_bit = INB (nc_gpreg); - nvram_setBit(np, 0, gpreg, CLR_CLK); - nvram_setBit(np, 0, gpreg, CLR_BIT); -} + /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */ + OUTB (nc_gpreg, old_gpreg); + OUTB (nc_gpcntl, gpcntl); -/* - * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!! - */ -static void __init nvram_stop(ncr_slot *np, u_char *gpreg) -{ - nvram_setBit(np, 0, gpreg, SET_CLK); - nvram_setBit(np, 1, gpreg, SET_BIT); -} + /* this is to set NVRAM into a known state with GPIO0/1 both low */ + gpreg = old_gpreg; + S24C16_set_bit(np, 0, &gpreg, CLR_CLK); + S24C16_set_bit(np, 0, &gpreg, CLR_BIT); + + /* now set NVRAM inactive with GPIO0/1 both high */ + S24C16_stop(np, &gpreg); + + /* activate NVRAM */ + S24C16_start(np, &gpreg); -/* - * Set/clear data/clock bit in GPIO0 - */ -static void __init -nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode) -{ - UDELAY (5); - switch (bit_mode){ - case SET_BIT: - *gpreg |= write_bit; - break; - case CLR_BIT: - *gpreg &= 0xfe; - break; - case SET_CLK: - *gpreg |= 0x02; - break; - case CLR_CLK: - *gpreg &= 0xfd; - break; + /* write device code and random address MSB */ + S24C16_write_byte(np, &ack_data, + 0xa0 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl); + if (ack_data & 0x01) + goto out; - } - OUTB (nc_gpreg, *gpreg); - UDELAY (5); + /* write random address LSB */ + S24C16_write_byte(np, &ack_data, + offset & 0xff, &gpreg, &gpcntl); + if (ack_data & 0x01) + goto out; + + /* regenerate START state to set up for reading */ + S24C16_start(np, &gpreg); + + /* rewrite device code and address MSB with read bit set (lsb = 0x01) */ + S24C16_write_byte(np, &ack_data, + 0xa1 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl); + if (ack_data & 0x01) + goto out; + + /* now set up GPIO0 for inputting data */ + gpcntl |= 0x01; + OUTB (nc_gpcntl, gpcntl); + + /* input all requested data - only part of total NVRAM */ + for (x = 0; x < len; x++) + S24C16_read_byte(np, &data[x], (x == (len-1)), &gpreg, &gpcntl); + + /* finally put NVRAM back in inactive mode */ + gpcntl &= 0xfe; + OUTB (nc_gpcntl, gpcntl); + S24C16_stop(np, &gpreg); + retv = 0; +out: + /* return GPIO0/1 to original states after having accessed NVRAM */ + OUTB (nc_gpcntl, old_gpcntl); + OUTB (nc_gpreg, old_gpreg); + + return retv; } #undef SET_BIT 0 @@ -13050,115 +13116,126 @@ #undef SET_CLK 2 #undef CLR_CLK 3 - -/* --------------------------------------------------------------------- -** -** Try reading Tekram format nvram -** -** --------------------------------------------------------------------- -** -** GPOI0 - data in -** GPIO1 - data out -** GPIO2 - clock -** GPIO4 - chip select -** -** return 0 if NVRAM data OK, 1 if NVRAM data not OK -** --------------------------------------------------------------------- -*/ - -static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg); -static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg); -static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg); -static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg); -static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg); -static void Tnvram_Stop(ncr_slot *np, u_char *gpreg); -static void Tnvram_Clk(ncr_slot *np, u_char *gpreg); - -static int __init ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram) +/* + * Try reading Symbios NVRAM. + * Return 0 if OK. + */ +static int __init sym_read_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram) { - u_char gpcntl, gpreg; - u_char old_gpcntl, old_gpreg; - u_short csum; - - /* save current state of GPCNTL and GPREG */ - old_gpreg = INB (nc_gpreg); - old_gpcntl = INB (nc_gpcntl); + static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0}; + u_char *data = (u_char *) nvram; + int len = sizeof(*nvram); + u_short csum; + int x; - /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in, - 1/2/4 out */ - gpreg = old_gpreg & 0xe9; - OUTB (nc_gpreg, gpreg); - gpcntl = (old_gpcntl & 0xe9) | 0x09; - OUTB (nc_gpcntl, gpcntl); + /* probe the 24c16 and read the SYMBIOS 24c16 area */ + if (sym_read_S24C16_nvram (np, SYMBIOS_NVRAM_ADDRESS, data, len)) + return 1; - /* input all of NVRAM, 64 words */ - csum = Tnvram_read_data(np, (u_short *) nvram, - sizeof(*nvram) / sizeof(short), &gpreg); - - /* return GPIO0/1/2/4 to original states after having accessed NVRAM */ - OUTB (nc_gpcntl, old_gpcntl); - OUTB (nc_gpreg, old_gpreg); + /* check valid NVRAM signature, verify byte count and checksum */ + if (nvram->type != 0 || + memcmp(nvram->trailer, Symbios_trailer, 6) || + nvram->byte_count != len - 12) + return 1; - /* check data valid */ - if (csum != 0x1234) + /* verify checksum */ + for (x = 6, csum = 0; x < len - 6; x++) + csum += data[x]; + if (csum != nvram->checksum) return 1; return 0; } /* - * Read Tekram NvRAM data and compute checksum. + * 93C46 EEPROM reading. + * + * GPOI0 - data in + * GPIO1 - data out + * GPIO2 - clock + * GPIO4 - chip select + * + * Used by Tekram. + */ + +/* + * Pulse clock bit in GPIO0 */ -static u_short __init -Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg) +static void __init T93C46_Clk(ncr_slot *np, u_char *gpreg) { - u_char read_bit; - u_short csum; - int x; + OUTB (nc_gpreg, *gpreg | 0x04); + UDELAY (2); + OUTB (nc_gpreg, *gpreg); +} - for (x = 0, csum = 0; x < len; x++) { +/* + * Read bit from NVRAM + */ +static void __init T93C46_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg) +{ + UDELAY (2); + T93C46_Clk(np, gpreg); + *read_bit = INB (nc_gpreg); +} - /* output read command and address */ - Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg); - if (read_bit & 0x01) - return 0; /* Force bad checksum */ +/* + * Write bit to GPIO0 + */ +static void __init T93C46_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg) +{ + if (write_bit & 0x01) + *gpreg |= 0x02; + else + *gpreg &= 0xfd; + + *gpreg |= 0x10; + + OUTB (nc_gpreg, *gpreg); + UDELAY (2); - Tnvram_Read_Word(np, &data[x], gpreg); - csum += data[x]; + T93C46_Clk(np, gpreg); +} - Tnvram_Stop(np, gpreg); - } +/* + * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!! + */ +static void __init T93C46_Stop(ncr_slot *np, u_char *gpreg) +{ + *gpreg &= 0xef; + OUTB (nc_gpreg, *gpreg); + UDELAY (2); - return csum; + T93C46_Clk(np, gpreg); } /* - * Send read command and address to NVRAM + * Send read command and address to NVRAM */ static void __init -Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg) +T93C46_Send_Command(ncr_slot *np, u_short write_data, + u_char *read_bit, u_char *gpreg) { int x; /* send 9 bits, start bit (1), command (2), address (6) */ for (x = 0; x < 9; x++) - Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg); + T93C46_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg); *read_bit = INB (nc_gpreg); } /* - * READ a byte from the NVRAM + * READ 2 bytes from the NVRAM */ static void __init -Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg) +T93C46_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg) { int x; u_char read_bit; *nvram_data = 0; for (x = 0; x < 16; x++) { - Tnvram_Read_Bit(np, &read_bit, gpreg); + T93C46_Read_Bit(np, &read_bit, gpreg); if (read_bit & 0x01) *nvram_data |= (0x01 << (15 - x)); @@ -13167,56 +13244,98 @@ } } -/* - * Read bit from NVRAM - */ -static void __init -Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg) -{ - UDELAY (2); - Tnvram_Clk(np, gpreg); - *read_bit = INB (nc_gpreg); -} - /* - * Write bit to GPIO0 + * Read Tekram NvRAM data. */ -static void __init -Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg) +static int __init +T93C46_Read_Data(ncr_slot *np, u_short *data,int len,u_char *gpreg) { - if (write_bit & 0x01) - *gpreg |= 0x02; - else - *gpreg &= 0xfd; - - *gpreg |= 0x10; - - OUTB (nc_gpreg, *gpreg); - UDELAY (2); + u_char read_bit; + int x; + + for (x = 0; x < len; x++) { + + /* output read command and address */ + T93C46_Send_Command(np, 0x180 | x, &read_bit, gpreg); + if (read_bit & 0x01) + return 1; /* Bad */ + T93C46_Read_Word(np, &data[x], gpreg); + T93C46_Stop(np, gpreg); + } - Tnvram_Clk(np, gpreg); + return 0; } /* - * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!! + * Try reading 93C46 Tekram NVRAM. */ -static void __init Tnvram_Stop(ncr_slot *np, u_char *gpreg) +static int __init +sym_read_T93C46_nvram (ncr_slot *np, Tekram_nvram *nvram) { - *gpreg &= 0xef; - OUTB (nc_gpreg, *gpreg); - UDELAY (2); + u_char gpcntl, gpreg; + u_char old_gpcntl, old_gpreg; + int retv = 1; - Tnvram_Clk(np, gpreg); + /* save current state of GPCNTL and GPREG */ + old_gpreg = INB (nc_gpreg); + old_gpcntl = INB (nc_gpcntl); + + /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in, + 1/2/4 out */ + gpreg = old_gpreg & 0xe9; + OUTB (nc_gpreg, gpreg); + gpcntl = (old_gpcntl & 0xe9) | 0x09; + OUTB (nc_gpcntl, gpcntl); + + /* input all of NVRAM, 64 words */ + retv = T93C46_Read_Data(np, (u_short *) nvram, + sizeof(*nvram) / sizeof(short), &gpreg); + + /* return GPIO0/1/2/4 to original states after having accessed NVRAM */ + OUTB (nc_gpcntl, old_gpcntl); + OUTB (nc_gpreg, old_gpreg); + + return retv; } /* - * Pulse clock bit in GPIO0 + * Try reading Tekram NVRAM. + * Return 0 if OK. */ -static void __init Tnvram_Clk(ncr_slot *np, u_char *gpreg) +static int __init +sym_read_Tekram_nvram (ncr_slot *np, u_short device_id, Tekram_nvram *nvram) { - OUTB (nc_gpreg, *gpreg | 0x04); - UDELAY (2); - OUTB (nc_gpreg, *gpreg); + u_char *data = (u_char *) nvram; + int len = sizeof(*nvram); + u_short csum; + int x; + + switch (device_id) { + case PCI_DEVICE_ID_NCR_53C885: + case PCI_DEVICE_ID_NCR_53C895: + case PCI_DEVICE_ID_NCR_53C896: + x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS, + data, len); + break; + case PCI_DEVICE_ID_NCR_53C875: + x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS, + data, len); + if (!x) + break; + default: + x = sym_read_T93C46_nvram(np, nvram); + break; + } + if (x) + return 1; + + /* verify checksum */ + for (x = 0, csum = 0; x < len - 1; x += 2) + csum += data[x] + (data[x+1] << 8); + if (csum != 0x1234) + return 1; + + return 0; } #endif /* SCSI_NCR_NVRAM_SUPPORT */ diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/sym53c8xx_defs.h linux/drivers/scsi/sym53c8xx_defs.h --- v2.3.33/linux/drivers/scsi/sym53c8xx_defs.h Sat Oct 9 11:47:50 1999 +++ linux/drivers/scsi/sym53c8xx_defs.h Mon Dec 20 16:02:56 1999 @@ -458,6 +458,10 @@ #define PCI_DEVICE_ID_NCR_53C895A 0x12 #endif +#ifndef PCI_DEVICE_ID_NCR_53C1510D +#define PCI_DEVICE_ID_NCR_53C1510D 0xa +#endif + /* ** NCR53C8XX devices features table. */ @@ -572,6 +576,10 @@ {PCI_DEVICE_ID_NCR_53C895A, 0xff, "895a", 6, 31, 7, \ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ FE_RAM|FE_RAM8K|FE_64BIT|FE_IO256|FE_NOPM|FE_LEDC} \ + , \ + {PCI_DEVICE_ID_NCR_53C1510D, 0xff, "1510D", 7, 31, 7, \ + FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ + FE_RAM|FE_IO256}\ } /* @@ -589,7 +597,8 @@ PCI_DEVICE_ID_NCR_53C885, \ PCI_DEVICE_ID_NCR_53C895, \ PCI_DEVICE_ID_NCR_53C896, \ - PCI_DEVICE_ID_NCR_53C895A \ + PCI_DEVICE_ID_NCR_53C895A, \ + PCI_DEVICE_ID_NCR_53C1510D \ } /* @@ -787,7 +796,8 @@ ** Tekram NvRAM data format. */ #define TEKRAM_NVRAM_SIZE 64 -#define TEKRAM_NVRAM_ADDRESS 0 +#define TEKRAM_93C46_NVRAM_ADDRESS 0 +#define TEKRAM_24C16_NVRAM_ADDRESS 0x40 struct Tekram_nvram { struct Tekram_target { diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c --- v2.3.33/linux/drivers/scsi/wd7000.c Thu Nov 11 20:11:49 1999 +++ linux/drivers/scsi/wd7000.c Thu Dec 16 13:57:05 1999 @@ -160,6 +160,7 @@ #include #include #include +#include #include "scsi.h" #include "hosts.h" #include "sd.h" @@ -557,6 +558,10 @@ unchar data[18]; } Icb; +#ifdef MODULE +static char * wd7000 = NULL; +MODULE_PARM(wd7000, "s"); +#endif /* * Driver SCB structure pool. @@ -599,108 +604,102 @@ * will configure the driver for a WD-7000 controller * using IRQ 15 with a DMA channel 6, at IO base address 0x350. */ -void wd7000_setup (char *str, int *ints) +static int __init wd7000_setup(char *str) { - static short wd7000_card_num = 0; - short i, j; - - if (wd7000_card_num >= NUM_CONFIGS) { - printk ("wd7000_setup: Too many \"wd7000=\" configurations in " - "command line!\n"); - return; - } - - if ((ints[0] < 3) || (ints[0] > 5)) - printk ("wd7000_setup: Error in command line! " - "Usage: wd7000=,,IO>[,[,]]\n"); - else { - for (i = 0; i < NUM_IRQS; i++) - if (ints[1] == wd7000_irq[i]) - break; - - if (i == NUM_IRQS) { - setup_error ("invalid IRQ.", ints); - return; - } - else - configs[wd7000_card_num].irq = ints[1]; + static short wd7000_card_num = 0; + short i, j; + int ints[6]; - for (i = 0; i < NUM_DMAS; i++) - if (ints[2] == wd7000_dma[i]) - break; + (void)get_options(str, ARRAY_SIZE(ints), ints); - if (i == NUM_DMAS) { - setup_error ("invalid DMA channel.", ints); - return; - } - else - configs[wd7000_card_num].dma = ints[2]; - - for (i = 0; i < NUM_IOPORTS; i++) - if (ints[3] == wd7000_iobase[i]) - break; - - if (i == NUM_IOPORTS) { - setup_error ("invalid I/O base address.", ints); - return; + if (wd7000_card_num >= NUM_CONFIGS) { + printk("wd7000_setup: Too many \"wd7000=\" configurations in " + "command line!\n"); + return 0; } - else - configs[wd7000_card_num].iobase = ints[3]; - if (ints[0] > 3) { - if ((ints[4] < 500) || (ints[4] > 31875)) { - setup_error ("BUS_ON value is out of range (500 to 31875 nanoseconds)!", - ints); - configs[wd7000_card_num].bus_on = BUS_ON; - } - else - configs[wd7000_card_num].bus_on = ints[4] / 125; - } - else - configs[wd7000_card_num].bus_on = BUS_ON; - - if (ints[0] > 4) { - if ((ints[5] < 500) || (ints[5] > 31875)) { - setup_error ("BUS_OFF value is out of range (500 to 31875 nanoseconds)!", - ints); - configs[wd7000_card_num].bus_off = BUS_OFF; - } - else - configs[wd7000_card_num].bus_off = ints[5] / 125; - } - else - configs[wd7000_card_num].bus_off = BUS_OFF; - - if (wd7000_card_num) { - for (i = 0; i < (wd7000_card_num - 1); i++) - for (j = i + 1; j < wd7000_card_num; j++) - if (configs[i].irq == configs[j].irq) { - setup_error ("duplicated IRQ!", ints); - return; - } - else if (configs[i].dma == configs[j].dma) { - setup_error ("duplicated DMA channel!", ints); - return; - } - else if (configs[i].iobase == configs[j].iobase) { - setup_error ("duplicated I/O base address!", ints); - return; - } - } + if ((ints[0] < 3) || (ints[0] > 5)) { + printk("wd7000_setup: Error in command line! " + "Usage: wd7000=,,IO>[,[,]]\n"); + } else { + for (i = 0; i < NUM_IRQS; i++) + if (ints[1] == wd7000_irq[i]) + break; + + if (i == NUM_IRQS) { + setup_error("invalid IRQ.", ints); + return 0; + } else + configs[wd7000_card_num].irq = ints[1]; + + for (i = 0; i < NUM_DMAS; i++) + if (ints[2] == wd7000_dma[i]) + break; + + if (i == NUM_DMAS) { + setup_error("invalid DMA channel.", ints); + return 0; + } else + configs[wd7000_card_num].dma = ints[2]; + + for (i = 0; i < NUM_IOPORTS; i++) + if (ints[3] == wd7000_iobase[i]) + break; + + if (i == NUM_IOPORTS) { + setup_error("invalid I/O base address.", ints); + return 0; + } else + configs[wd7000_card_num].iobase = ints[3]; + + if (ints[0] > 3) { + if ((ints[4] < 500) || (ints[4] > 31875)) { + setup_error("BUS_ON value is out of range (500 to 31875 nanoseconds)!", ints); + configs[wd7000_card_num].bus_on = BUS_ON; + } else + configs[wd7000_card_num].bus_on = ints[4] / 125; + } else + configs[wd7000_card_num].bus_on = BUS_ON; + + if (ints[0] > 4) { + if ((ints[5] < 500) || (ints[5] > 31875)) { + setup_error("BUS_OFF value is out of range (500 to 31875 nanoseconds)!", ints); + configs[wd7000_card_num].bus_off = BUS_OFF; + } else + configs[wd7000_card_num].bus_off = ints[5] / 125; + } else + configs[wd7000_card_num].bus_off = BUS_OFF; + + if (wd7000_card_num) { + for (i = 0; i < (wd7000_card_num - 1); i++) + for (j = i + 1; j < wd7000_card_num; j++) + if (configs[i].irq == configs[j].irq) { + setup_error("duplicated IRQ!", ints); + return 0; + } else if (configs[i].dma == configs[j].dma) { + setup_error("duplicated DMA channel!", ints); + return 0; + } else if (configs[i].iobase == configs[j].iobase) { + setup_error ("duplicated I/O base address!", ints); + return 0; + } + } #ifdef WD7000_DEBUG - printk ("wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x, BUS_ON=%dns, BUS_OFF=%dns\n", - configs[wd7000_card_num].irq, - configs[wd7000_card_num].dma, - configs[wd7000_card_num].iobase, - configs[wd7000_card_num].bus_on * 125, - configs[wd7000_card_num].bus_off * 125); + printk ("wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x, BUS_ON=%dns, BUS_OFF=%dns\n", + configs[wd7000_card_num].irq, + configs[wd7000_card_num].dma, + configs[wd7000_card_num].iobase, + configs[wd7000_card_num].bus_on * 125, + configs[wd7000_card_num].bus_off * 125); #endif - wd7000_card_num++; - } + wd7000_card_num++; + } + return 1; } +__setup("wd7000=", wd7000_setup); #ifdef ANY2SCSI_INLINE /* @@ -1526,6 +1525,11 @@ #ifdef WD7000_DEBUG printk ("wd7000_detect: started\n"); +#endif + +#ifdef MODULE + if (wd7000) + wd7000_setup(wd7000); #endif for (i = 0; i < IRQS; wd7000_host[i++] = NULL) ; diff -u --recursive --new-file v2.3.33/linux/drivers/usb/.indent.pro linux/drivers/usb/.indent.pro --- v2.3.33/linux/drivers/usb/.indent.pro Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/.indent.pro Thu Dec 16 01:27:41 1999 @@ -0,0 +1 @@ +-i8 -br -nce -npsl diff -u --recursive --new-file v2.3.33/linux/drivers/usb/Config.in linux/drivers/usb/Config.in --- v2.3.33/linux/drivers/usb/Config.in Wed Dec 8 14:11:27 1999 +++ linux/drivers/usb/Config.in Fri Dec 17 16:59:34 1999 @@ -2,29 +2,20 @@ # USB device configuration # mainmenu_option next_comment -comment 'Support for USB' +comment 'USB support' tristate 'Support for USB' CONFIG_USB if [ ! "$CONFIG_USB" = "n" ]; then comment 'USB Controllers' dep_tristate ' UHCI (Intel PIIX4 and others) support' CONFIG_USB_UHCI \ $CONFIG_USB - dep_tristate ' OHCI (Compaq and some others) support' CONFIG_USB_OHCI \ - $CONFIG_USB - if [ "$CONFIG_USB_OHCI" != "n" ]; then - bool ' Enable tons of OHCI debugging output' CONFIG_USB_OHCI_DEBUG - fi - dep_tristate ' OHCI-HCD (other OHCI opt. Virt. Root Hub) support' \ + dep_tristate ' OHCI-HCD (compaq and some others) support' \ CONFIG_USB_OHCI_HCD $CONFIG_USB - if [ "$CONFIG_USB_OHCI_HCD" != "n" ]; then - bool ' OHCI-HCD Virtual Root Hub' CONFIG_USB_OHCI_VROOTHUB - fi comment 'Miscellaneous USB options' - bool ' Enable lots of ISOC debugging output' CONFIG_USB_DEBUG_ISOC if [ "$CONFIG_PROC_FS" != "n" ]; then - bool ' Preliminary /proc/bus/usb support' CONFIG_USB_PROC + bool ' /proc/bus/usb support' CONFIG_USB_PROC fi dep_tristate ' EZUSB Firmware downloader' CONFIG_USB_EZUSB $CONFIG_USB @@ -43,6 +34,7 @@ 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 fi endmenu diff -u --recursive --new-file v2.3.33/linux/drivers/usb/Makefile linux/drivers/usb/Makefile --- v2.3.33/linux/drivers/usb/Makefile Wed Dec 8 14:11:27 1999 +++ linux/drivers/usb/Makefile Thu Dec 16 01:27:41 1999 @@ -40,20 +40,12 @@ MI_OBJS += uhci.o uhci-debug.o endif -ifeq ($(CONFIG_USB_OHCI),y) - L_OBJS += ohci.o ohci-debug.o -endif -ifeq ($(CONFIG_USB_OHCI),m) - M_OBJS += usb-ohci.o - MI_OBJS += ohci.o ohci-debug.o -endif - ifeq ($(CONFIG_USB_OHCI_HCD),y) - L_OBJS += ohci-hcd.o ohci-root-hub.o + L_OBJS += ohci-hcd.o endif ifeq ($(CONFIG_USB_OHCI_HCD),m) M_OBJS += usb-ohci-hcd.o - MI_OBJS += ohci-hcd.o ohci-root-hub.o + MI_OBJS += ohci-hcd.o endif ifeq ($(CONFIG_USB_MOUSE),y) @@ -172,6 +164,15 @@ MI_OBJS += uss720.o endif +ifeq ($(CONFIG_USB_DABUSB),y) + L_OBJS += dabusb.o +endif + +ifeq ($(CONFIG_USB_DABUSB),m) + M_OBJS += dabusb.o + MI_OBJS += dabusb.o +endif + include $(TOPDIR)/Rules.make keymap.o: keymap.c @@ -197,11 +198,8 @@ usb-uhci.o: uhci.o uhci-debug.o $(LD) $(LD_RFLAG) -r -o $@ uhci.o uhci-debug.o -usb-ohci.o: ohci.o ohci-debug.o - $(LD) $(LD_RFLAG) -r -o $@ ohci.o ohci-debug.o - -usb-ohci-hcd.o: ohci-hcd.o ohci-root-hub.o - $(LD) $(LD_RFLAG) -r -o $@ ohci-hcd.o ohci-root-hub.o +usb-ohci-hcd.o: ohci-hcd.o + $(LD) $(LD_RFLAG) -r -o $@ ohci-hcd.o ifeq ($(CONFIG_USB_PROC),y) usbcore.o: usb.o usb-debug.o usb-core.o proc_usb.o hub.o diff -u --recursive --new-file v2.3.33/linux/drivers/usb/README.URB linux/drivers/usb/README.URB --- v2.3.33/linux/drivers/usb/README.URB Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/README.URB Thu Dec 16 01:27:41 1999 @@ -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 -u --recursive --new-file v2.3.33/linux/drivers/usb/README.error-codes linux/drivers/usb/README.error-codes --- v2.3.33/linux/drivers/usb/README.error-codes Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/README.error-codes Thu Dec 16 01:27:41 1999 @@ -0,0 +1,108 @@ +$Id: README.error-codes,v 1.1 1999/12/14 14:03:02 fliegl Exp $ + +This is the documentation of (hopefully) all possible error codes (and +their interpretation) that can be returned from the hostcontroller driver +and from usbcore. + +NOTE: +The USB_ST_* codes are deferred and are only listed for compatibility, new +software should use only -E* instead! + + + +************************************************************************** +* Error codes returned by usb_submit_urb * +************************************************************************** + +Non-USB-specific: + +USB_ST_NOERROR +0 URB submission went fine + +-ENOMEM no memory for allocation of internal structures + +USB-specific: + +-ENODEV specified USB-device or bus doesn't exist + +-ENXIO specified endpoint doesn't exist on the device + +USB_ST_URB_INVALID_ERROR +-EINVAL a) Invalid transfer type specified (or not supported) + b) Invalid interrupt interval (0<=n<256) + c) more than one interrupt packet requested + +-EAGAIN a) specified ISO start frame too early + b) (using ISO-ASAP) too much scheduled for the future + wait some time and try again. + +-EFBIG too much ISO frames requested (currently uhci>900) + +-EPIPE specified pipe-handle is already stalled + +-EMSGSIZE endpoint message size is zero, do interface/alternate setting + + +************************************************************************** +* Error codes returned by in urb->status * +* or in iso_frame_desc[n].status (for ISO) * +************************************************************************** + +USB_ST_NOERROR +0 Transfer completed successfully + +USB_ST_URB_KILLED +-ENOENT URB was canceled by unlink_urb + +USB_ST_URB_PENDING +-EINPROGRESS URB still pending, no results yet + (actually no error until now;-) + +USB_ST_BITSTUFF +USB_ST_INTERNALERROR +-EPROTO a) bitstuff error + b) unknown USB error + +USB_ST_CRC +-EILSEQ CRC mismatch + +-EPIPE a) babble detect + b) endpoint stalled + +USB_ST_BUFFERUNDERRUN +-ENOST buffer error + +USB_ST_NORESPONSE +USB_ST_TIMEOUT +-ETIMEDOUT transfer timed out, NAK + +USB_ST_REMOVED +-ENODEV device was removed + +USB_ST_SHORT_PACKET +-EREMOTEIO short packet detected + +USB_ST_PARTIAL_ERROR +-EXDEV ISO transfer only partially completed + look at individual frame status for details + +USB_ST_URB_INVALID_ERROR +-EINVAL ISO madness, if this happens: Log off and go home + +************************************************************************** +* Error codes returned by usbcore-functions * +* (expect also other submit and transfer status codes) * +************************************************************************** + +usb_register(): +USB_ST_NOTSUPPORTED +-EINVAL error during registering new driver + +usb_terminate_bulk(): +USB_ST_REMOVED +-ENODEV urb already removed + +usb_get_*/usb_set_*(): + All USB errors (submit/status) can occur + + diff -u --recursive --new-file v2.3.33/linux/drivers/usb/README.ohci linux/drivers/usb/README.ohci --- v2.3.33/linux/drivers/usb/README.ohci Sat Oct 9 11:47:50 1999 +++ linux/drivers/usb/README.ohci Wed Dec 31 16:00:00 1969 @@ -1,71 +0,0 @@ -[This is the readme for ohci.c, ohci-debug.c and ohci.h] - -September 05, 1999 17:03:21 - -I haven't updated this file in a while. I have started merging good -ideas from the ohci-hcd driver into this code as well as simplifying -the code where I can. Isochronous transfers still need to be ported -over from ohci-hcd. Large control and bulk transfers should be -working much better now. - -A big thanks goes to 3Com and APC for donating me a modem and UPS to -test with! They aren't working with OHCI quite yet but we hope to -change that soon. - -June 23, 1999 00:31:20 PST - -I now have bulk support in a reasonably working state. The only -device I have tested it with at the moment is my Epson Stylus 740 -printer. I can print both small and large files. - -I have included code to support transfers of large amounts of data in -either control or bulk transfers. Check out the build_td_chain() and -add_td_chain_to_ed() functions. - -TODO: - -~ Get Michael Gee's mass storage driver working with my donated - YE-Data floppy drive over OHCI. -~ Drool on the Epson printer because its the new toy around the house. - -June 08, 1999 01:23:34 - -Paul Mackerras went through the OHCI (& USB code) to fix most of the -endianness issues so that the code now works on Linux-PPC. He also -simplified add_td_to_ed to be simpler & atomic to the hardware. - -May 16, 1999 16:20:54 - -EDs are now allocated dynamically from their device's pool. Root hub -status changes should stop the infinite "no device connected" messages -that occurred after removing a device. - -TODO: - -~ Add Isochronous transfer support. These have their own special - format TDs to allow for several DMA data pointers. Kinda neat, but - likely harder to use through a generic interface in practice. -~ Support dynamic allocation & growth of the TD/ED pools. Merge them - into global pools rather than a today's static per device allocation. - -KNOWN BUGS: - -~ Unplugging a hub causes khubd to Oops. I don't think this is - directly related to OHCI, but due to the fact that the interrupt TD - for the hub is never stopped. We need a usb_release_irq() that gets - called using the "IRQ handle" that should be returned by - usb_request_irq(). - -May 09, 1999 16:25:58 PST - -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. - -- greg@electricrain.com - diff -u --recursive --new-file v2.3.33/linux/drivers/usb/README.serial linux/drivers/usb/README.serial --- v2.3.33/linux/drivers/usb/README.serial Thu Nov 11 20:11:49 1999 +++ linux/drivers/usb/README.serial Fri Dec 17 15:50:29 1999 @@ -1,6 +1,6 @@ This serial driver currently only works with the Belkin and Peracom USB Serial devices. It should also work for the Etek converter, but I do -not know the vendor id, and device id of that device (if anyone does, +not know the vendor id and device id of that device (if anyone does, please let me know.) If your device is not compatible with the above models, you can try @@ -19,17 +19,15 @@ Peracom, and eTek devices, and the raw dumps from the Win98 driver are confusing, and eTek keeps giving me the run around, no control signals are currently handled, and the data will most likely come through on a baud -rate that you are not expecting. +rate that you are not expecting. So if you have these devices, do not +expect the correct data to show up at either end. -The major number that the driver uses is 240 (in the local/experimental -range.) It will stay there until some agreements are reached on how to -handle the configuration problem that USB provides. - -To use the driver, create the following nodes: -mknod /dev/ttyUSB0 c 240 0 -mknod /dev/ttyUSB1 c 240 1 -mknod /dev/ttyUSB2 c 240 2 -mknod /dev/ttyUSB3 c 240 3 +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 then plug in a device and use your friendly terminal program to see what happens. diff -u --recursive --new-file v2.3.33/linux/drivers/usb/README.uhci linux/drivers/usb/README.uhci --- v2.3.33/linux/drivers/usb/README.uhci Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/README.uhci Thu Dec 16 01:27:41 1999 @@ -0,0 +1,165 @@ +Specification and Internals for the New UHCI Driver (Whitepaper...) + + brought to you by + + Georg Acher, acher@in.tum.de (executive slave) (base guitar) + Deti Fliegl, deti@fliegl.de (executive slave) (lead voice) + Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader) + + $Id: README.uhci,v 1.1 1999/12/14 14:03:02 fliegl Exp $ + +This document and the new uhci sources can be found on + http://hotswap.in.tum.de/usb + +1. General issues + +1.1 Why a new UHCI driver, we already have one?!? + +Correct, but its internal structure got more and more mixed up by the (still +ongoing) efforts to get isochronous transfers (ISO) to work. +Since there is an increasing need for reliable ISO-transfers (especially +for USB-audio needed by TS and for a DAB-USB-Receiver build by GA and DF), +this state was a bit unsatisfying in our opinion, so we've decided (based +on knowledge and experiences with the old UHCI driver) to start +from scratch with a new approach, much simpler but at the same time more +powerful. +It is inspired by the way Win98/Win2000 handles USB requests via URBs, +but it's definitely 100% free of MS-code and doesn't crash while +unplugging an used ISO-device like Win98 ;-) +Some code for HW setup and root hub management was taken from the +original UHCI driver, but heavily modified to fit into the new code. +The invention of the basic concept, and major coding were completed in two +days (and nights) on the 16th and 17th of October 1999, now known as the +great USB-October-Revolution started by GA, DF, and TS ;-) + +Since the concept is in no way UHCI dependant, we hope that it will also be +transfered to the OHCI-driver, so both drivers share a common API. + +1.2. Advantages and disadvantages + ++ All USB transfer types work now! ++ Asynchronous operation ++ Simple, but powerful interface (only two calls for start and cancel) ++ Easy migration to the new API, simplified by a compatibility API ++ Simple usage of ISO transfers ++ Automatic linking of requests ++ ISO transfers allow variable length for each frame and striping ++ No CPU dependent and non-portable atomic memory access, no asm()-inlines ++ Tested on x86 and Alpha + +- Rewriting for ISO transfers needed + +1.3. Is there some compatibility to the old API? + +Yes, but only for control, bulk and interrupt transfers. We've implemented +some wrapper calls for these transfer types. The usbcore works fine with +these wrappers. For ISO there's no compatibility, because the old ISO-API +and its semantics were unnecessary complicated in our opinion. + +1.4. What's really working? + +As said above, CTRL und BULK already work fine even with the wrappers, +so legacy code wouldn't notice the change. +Regarding to Thomas, ISO transfers now run stable with USB audio. +INT transfers (e.g. mouse driver) work fine, too. + +1.5. Are there any bugs? + +No ;-) +Hm... +Well, of course this implementation needs extensive testing on all available +hardware, but we believe that any fixes shouldn't harm the overall concept. + +1.6. What should be done next? + +A large part of the request handling seems to be identical for UHCI and +OHCI, so it would be a good idea to extract the common parts and have only +the HW specific stuff in uhci.c. Furthermore, all other USB device drivers +should need URBification, if they use isochronous or interrupt transfers. +One thing missing in the current implementation (and the old UHCI driver) +is fair queueing for BULK transfers. Since this would need (in principle) +the alteration of already constructed TD chains (to switch from depth to +breadth execution), another way has to be found. Maybe some simple +heuristics work with the same effect. + +--------------------------------------------------------------------------- + +2. Internal structure and mechanisms + +To get quickly familiar with the internal structures, here's a short +description how the new UHCI driver works. However, the ultimate source of +truth is only uhci.c! + +2.1. Descriptor structure (QHs and TDs) + +During initialization, the following skeleton is allocated in init_skel: + + framespecific | common chain + +framelist[] +[ 0 ]-----> TD --> TD -------\ +[ 1 ]-----> TD --> TD --------> TD ----> QH -------> QH -------> QH ---> NULL + ... TD --> TD -------/ +[1023]-----> TD --> TD ------/ + + ^^ ^^ ^^ ^^ ^^ ^^ + 1024 TDs for 7 TDs for 1 TD for Start of Start of End Chain + ISO INT (2-128ms) 1ms-INT CTRL Chain BULK Chain + +For each CTRL or BULK transfer a new QH is allocated and the containing data +transfers are appended as (vertical) TDs. After building the whole QH with its +dangling TDs, the QH is inserted before the BULK Chain QH (for CTRL) or +before the End Chain QH (for BULK). Since only the QH->next pointers are +affected, no atomic memory operation is required. The three QHs in the +common chain are never equipped with TDs! + +For ISO or INT, the TD for each frame is simply inserted into the apropriate +ISO/INT-TD-chain for the desired frame. The 7 skeleton INT-TDs are scattered +among the 1024 frames similar to the old UHCI driver. + +For CTRL/BULK/ISO, the last TD in the transfer has the IOC-bit set. For INT, +every TD (there is only one...) has the IOC-bit set. + +Besides the data for the UHCI controller (2 or 4 32bit words), the descriptors +are double-linked through the .vertical and .horizontal elements in the +SW data of the descriptor (using the double-linked list structures and +operations), but SW-linking occurs only in closed domains, i.e. for each of +the 1024 ISO-chains and the 8 INT-chains there is a closed cycle. This +simplifies all insertions and unlinking operations and avoids costly +bus_to_virt()-calls. + +2.2. URB structure and linking to QH/TDs + +During assembly of the QH and TDs of the requested action, these descriptors +are stored in urb->urb_list, so the allocated QH/TD descriptors are bound to +this URB. +If the assembly was successful and the descriptors were added to the HW chain, +the corresponding URB is inserted into a global URB list for this controller. +This list stores all pending URBs. + +2.3. Interrupt processing + +Since UHCI provides no means to directly detect completed transactions, the +following is done in each UHCI interrupt (uhci_interrupt()): + +For each URB in the pending queue (process_urb()), the ACTIVE-flag of the +associated TDs are processed (depending on the transfer type +process_{transfer|interrupt|iso}()). If the TDs are not active anymore, +they indicate the completion of the transaction and the status is calculated. +Inactive QH/TDs are removed from the HW chain (since the host controller +already removed the TDs from the QH, no atomic access is needed) and +eventually the URB is marked as completed (OK or errors) and removed from the +pending queue. Then the next linked URB is submitted. After (or immediately +before) that, the completion handler is called. + +2.4. Unlinking URBs + +First, all QH/TDs stored in the URB are unlinked from the HW chain. +To ensure that the host controller really left a vertical TD chain, we +wait for one frame. After that, the TDs are physically destroyed. + +2.5. URB linking and the consequences + +Since URBs can be linked and the corresponding submit_urb is called in +the UHCI-interrupt, all work associated with URB/QH/TD assembly has to be +interrupt save. This forces kmalloc to use GFP_ATOMIC in the interrupt. diff -u --recursive --new-file v2.3.33/linux/drivers/usb/acm.c linux/drivers/usb/acm.c --- v2.3.33/linux/drivers/usb/acm.c Sun Nov 7 16:37:34 1999 +++ linux/drivers/usb/acm.c Fri Dec 17 16:58:12 1999 @@ -139,7 +139,7 @@ { unsigned char *data; struct acm_state *acm = (struct acm_state *)dev_id; - devrequest *dr; + devrequest *dr; info("ACM_USB_IRQ\n"); @@ -153,13 +153,13 @@ if (!acm->active) return 1; - dr = __buffer; + dr = __buffer; data = __buffer; data += sizeof(dr); #if 0 - printk("reqtype: %02X\n",dr->requesttype); - printk("request: %02X\n",dr->request); + printk("reqtype: %02X\n",dr->requesttype); + printk("request: %02X\n",dr->request); printk("wValue: %02X\n",dr->value); printk("wIndex: %02X\n",dr->index); printk("wLength: %02X\n",dr->length); @@ -414,7 +414,7 @@ /* Only use CDC */ if (dev->descriptor.bDeviceClass != 2 || dev->descriptor.bDeviceSubClass != 0 || - dev->descriptor.bDeviceProtocol != 0) + dev->descriptor.bDeviceProtocol != 0) return NULL; #define IFCLASS(if) ((if->bInterfaceClass << 24) | (if->bInterfaceSubClass << 16) | (if->bInterfaceProtocol << 8) | (if->bNumEndpoints)) diff -u --recursive --new-file v2.3.33/linux/drivers/usb/audio.c linux/drivers/usb/audio.c --- v2.3.33/linux/drivers/usb/audio.c Sun Nov 7 16:37:34 1999 +++ linux/drivers/usb/audio.c Fri Dec 17 16:57:48 1999 @@ -1,58 +1,61 @@ /*****************************************************************************/ /* - * audio.c -- USB Audio Class driver + * audio.c -- USB Audio Class driver * - * Copyright (C) 1999 - * Alan Cox (alan@lxorguk.ukuu.org.uk) - * Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1999 + * Alan Cox (alan@lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer@ife.ee.ethz.ch) * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * * * 1999-09-07: Alan Cox - * Parsing Audio descriptor patch + * Parsing Audio descriptor patch * 1999-09-08: Thomas Sailer - * Added OSS compatible data io functions; both parts of the - * driver remain to be glued together + * Added OSS compatible data io functions; both parts of the + * driver remain to be glued together * 1999-09-10: Thomas Sailer - * Beautified the driver. Added sample format conversions. - * Still not properly glued with the parsing code. - * The parsing code seems to have its problems btw, - * Since it parses all available configs but doesn't - * store which iface/altsetting belongs to which config. + * Beautified the driver. Added sample format conversions. + * Still not properly glued with the parsing code. + * The parsing code seems to have its problems btw, + * Since it parses all available configs but doesn't + * store which iface/altsetting belongs to which config. * 1999-09-20: Thomas Sailer - * Threw out Alan's parsing code and implemented my own one. - * You cannot reasonnably linearly parse audio descriptors, - * especially the AudioClass descriptors have to be considered - * pointer lists. Mixer parsing untested, due to lack of device. - * First stab at synch pipe implementation, the Dallas USB DAC - * wants to use an Asynch out pipe. usb_audio_state now basically - * only contains lists of mixer and wave devices. We can therefore - * now have multiple mixer/wave devices per USB device. + * Threw out Alan's parsing code and implemented my own one. + * You cannot reasonnably linearly parse audio descriptors, + * especially the AudioClass descriptors have to be considered + * pointer lists. Mixer parsing untested, due to lack of device. + * First stab at synch pipe implementation, the Dallas USB DAC + * wants to use an Asynch out pipe. usb_audio_state now basically + * only contains lists of mixer and wave devices. We can therefore + * now have multiple mixer/wave devices per USB device. + * 1999-10-28: Thomas Sailer + * Converted to URB API. Fixed a taskstate/wakeup semantics mistake + * that made the driver consume all available CPU cycles. + * Now runs stable on UHCI-Acher/Fliegl/Sailer. * 1999-10-31: Thomas Sailer - * Audio can now be unloaded if it is not in use by any mixer - * or dsp client (formerly you had to disconnect the audio devices - * from the USB port) - * Finally, about three months after ordering, my "Maxxtro SPK222" - * speakers arrived, isn't disdata a great mail order company 8-) - * Parse class specific endpoint descriptor of the audiostreaming - * interfaces and take the endpoint attributes from there. - * Unbelievably, the Philips USB DAC has a sampling rate range - * of over a decade, yet does not support the sampling rate control! - * No wonder it sounds so bad, has very audible sampling rate - * conversion distortion. Don't try to listen to it using - * decent headphones! - * "Let's make things better" -> but please Philips start with your - * own stuff!!!! + * Audio can now be unloaded if it is not in use by any mixer + * or dsp client (formerly you had to disconnect the audio devices + * from the USB port) + * Finally, about three months after ordering, my "Maxxtro SPK222" + * speakers arrived, isn't disdata a great mail order company 8-) + * Parse class specific endpoint descriptor of the audiostreaming + * interfaces and take the endpoint attributes from there. + * Unbelievably, the Philips USB DAC has a sampling rate range + * of over a decade, yet does not support the sampling rate control! + * No wonder it sounds so bad, has very audible sampling rate + * conversion distortion. Don't try to listen to it using + * decent headphones! + * "Let's make things better" -> but please Philips start with your + * own stuff!!!! * 1999-11-02: 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. - * + * 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. * */ @@ -80,11 +83,8 @@ * expensive to be performed in the kernel. * * Current status: - * - The IO code seems to work a couple of frames, but then gets - * UHCI into a "complaining" mode, i.e. uhci won't work again until - * removed and reloaded, it will not even notice disconnect/reconnect - * events. - * It seems to work more stably on OHCI-HCD. + * - 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, @@ -131,7 +131,7 @@ */ /*****************************************************************************/ - +#include #include #include #include @@ -148,7 +148,7 @@ #include #include #include -#include +//#include #include "usb.h" #include "audio.h" @@ -157,6 +157,7 @@ #define SND_DEV_DSP16 5 + /* --------------------------------------------------------------------- */ /* @@ -182,7 +183,8 @@ * - Synchronisation behaviour * Don't touch this if you don't understand all of the above. */ -#define DESCFRAMES 4 +#define DESCFRAMES 5 +#define SYNCFRAMES DESCFRAMES #define MIXFLG_STEREOIN 1 #define MIXFLG_STEREOOUT 2 @@ -232,61 +234,68 @@ struct usb_audio_state; -#define FLG_NEXTID 1 -#define FLG_ID0RUNNING 2 -#define FLG_ID1RUNNING 4 -#define FLG_SYNCNEXTID 8 -#define FLG_SYNC0RUNNING 16 -#define FLG_SYNC1RUNNING 32 -#define FLG_RUNNING 64 +#define FLG_URB0RUNNING 1 +#define FLG_URB1RUNNING 2 +#define FLG_SYNC0RUNNING 4 +#define FLG_SYNC1RUNNING 8 +#define FLG_RUNNING 16 +#define FLG_CONNECTED 32 + +struct my_data_urb { + urb_t urb; + iso_packet_descriptor_t isoframe[DESCFRAMES]; +}; + +struct my_sync_urb { + urb_t urb; + iso_packet_descriptor_t isoframe[SYNCFRAMES]; +}; + struct usb_audiodev { struct list_head list; struct usb_audio_state *state; - - /* soundcore stuff */ - int dev_audio; + int remove_pending; + + /* soundcore stuff */ + int dev_audio; /* wave stuff */ - mode_t open_mode; + mode_t open_mode; spinlock_t lock; /* DMA buffer access spinlock */ struct usbin { - unsigned int interface; /* Interface number */ + int interface; /* Interface number, -1 means not used */ unsigned int format; /* USB data format */ unsigned int datapipe; /* the data input pipe */ unsigned int syncpipe; /* the synchronisation pipe - 0 for anything but adaptive IN mode */ unsigned int syncinterval; /* P for adaptive IN mode, 0 otherwise */ unsigned int freqn; /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */ + unsigned int freqmax; /* maximum sampling rate, used for buffer management */ unsigned int phase; /* phase accumulator */ unsigned int flags; /* see FLG_ defines */ - - struct usb_isoc_desc *dataiso[2]; /* ISO descriptors for the data endpoint */ - unsigned char *data[2]; /* data pages associated with the ISO descriptors */ - - struct usb_isoc_desc *synciso[2]; /* ISO sync pipe descriptor if needed */ - unsigned char *syncdata[2]; /* data page for sync data */ - + + struct my_data_urb durb[2]; /* ISO descriptors for the data endpoint */ + struct my_sync_urb surb[2]; /* ISO sync pipe descriptor if needed */ + struct dmabuf dma; } usbin; struct usbout { - unsigned int interface; /* Interface number */ + int interface; /* Interface number, -1 means not used */ unsigned int format; /* USB data format */ unsigned int datapipe; /* the data input pipe */ unsigned int syncpipe; /* the synchronisation pipe - 0 for anything but asynchronous OUT mode */ unsigned int syncinterval; /* P for asynchronous OUT mode, 0 otherwise */ unsigned int freqn; /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */ unsigned int freqm; /* momentary sampling rate in USB format, i.e. fs/1000 in Q10.14 */ + unsigned int freqmax; /* maximum sampling rate, used for buffer management */ unsigned int phase; /* phase accumulator */ unsigned int flags; /* see FLG_ defines */ - struct usb_isoc_desc *dataiso[2]; /* ISO descriptors for the data endpoint */ - unsigned char *data[2]; /* data pages associated with the ISO descriptors */ - - struct usb_isoc_desc *synciso[2]; /* ISO sync pipe descriptor if needed */ - unsigned char *syncdata[2]; /* data page for sync data */ - + struct my_data_urb durb[2]; /* ISO descriptors for the data endpoint */ + struct my_sync_urb surb[2]; /* ISO sync pipe descriptor if needed */ + struct dmabuf dma; } usbout; @@ -300,13 +309,13 @@ struct list_head list; struct usb_audio_state *state; - /* soundcore stuff */ - int dev_mixer; + /* soundcore stuff */ + int dev_mixer; unsigned char iface; /* interface number of the AudioControl interface */ /* USB format descriptions */ - unsigned int numch, modcnt; + unsigned int numch, modcnt; /* mixch is last and gets allocated dynamically */ struct mixerchannel ch[0]; @@ -336,27 +345,27 @@ extern inline unsigned ld2(unsigned int x) { - unsigned 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; + unsigned 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; } /* --------------------------------------------------------------------- */ @@ -383,38 +392,38 @@ free_page((unsigned long)p); db->sgbuf[nr] = NULL; } - db->mapped = db->ready = 0; + db->mapped = db->ready = 0; } static int dmabuf_init(struct dmabuf *db) { - unsigned int nr, bytepersec, bufs; + unsigned int nr, bytepersec, bufs; void *p; /* initialize some fields */ - db->rdptr = db->wrptr = db->total_bytes = db->count = db->error = 0; + db->rdptr = db->wrptr = db->total_bytes = db->count = db->error = 0; /* calculate required buffer size */ bytepersec = db->srate << AFMT_BYTESSHIFT(db->format); bufs = 1U << DMABUFSHIFT; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < bytepersec) - db->fragshift = ld2(bytepersec/1000); - else - db->fragshift = db->ossfragshift; - } else { - db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); - if (db->fragshift < 3) - db->fragshift = 3; - } - db->numfrag = bufs >> db->fragshift; - while (db->numfrag < 4 && db->fragshift > 3) { - db->fragshift--; - db->numfrag = bufs >> db->fragshift; - } - db->fragsize = 1 << db->fragshift; - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - db->dmasize = db->numfrag << db->fragshift; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->dmasize = db->numfrag << db->fragshift; for(nr = 0; nr < NRSGBUF; nr++) { if (!db->sgbuf[nr]) { p = (void *)get_free_page(GFP_KERNEL); @@ -428,12 +437,12 @@ break; } db->bufsize = nr << PAGE_SHIFT; - db->ready = 1; + db->ready = 1; printk(KERN_DEBUG "dmabuf_init: bytepersec %d bufs %d ossfragshift %d ossmaxfrags %d " "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d\n", bytepersec, bufs, db->ossfragshift, db->ossmaxfrags, db->fragshift, db->fragsize, db->numfrag, db->dmasize, db->bufsize); - return 0; + return 0; } static int dmabuf_mmap(struct dmabuf *db, unsigned long start, unsigned long size, pgprot_t prot) @@ -557,57 +566,59 @@ static void usbin_stop(struct usb_audiodev *as) { struct usbin *u = &as->usbin; - unsigned long flags; - unsigned int i; + unsigned long flags; + unsigned int i, notkilled = 1; - spin_lock_irqsave(&as->lock, flags); + spin_lock_irqsave(&as->lock, flags); u->flags &= ~FLG_RUNNING; i = u->flags; - spin_unlock_irqrestore(&as->lock, flags); - while (i & (FLG_ID0RUNNING|FLG_ID1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) { - set_current_state(TASK_INTERRUPTIBLE); + 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); - if (signal_pending(current)) { - if (i & FLG_ID0RUNNING) - usb_kill_isoc(u->dataiso[0]); - if (i & FLG_ID1RUNNING) - usb_kill_isoc(u->dataiso[1]); - if (i & FLG_SYNC0RUNNING) - usb_kill_isoc(u->synciso[0]); - if (i & FLG_SYNC1RUNNING) - usb_kill_isoc(u->synciso[1]); - break; - } 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->dataiso[0]) - usb_free_isoc(u->dataiso[0]); - if (u->dataiso[1]) - usb_free_isoc(u->dataiso[1]); - if (u->synciso[0]) - usb_free_isoc(u->synciso[0]); - if (u->synciso[1]) - usb_free_isoc(u->synciso[1]); - u->dataiso[0] = u->dataiso[1] = u->synciso[0] = u->synciso[1] = NULL; + 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_release(struct usb_audiodev *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); - if (u->data[0]) - free_page((unsigned long)u->data[0]); - if (u->data[1]) - free_page((unsigned long)u->data[1]); - if (u->syncdata[0]) - free_page((unsigned long)u->syncdata[0]); - if (u->syncdata[1]) - free_page((unsigned long)u->syncdata[1]); - u->data[0] = u->data[1] = u->syncdata[0] = u->syncdata[1] = NULL; } static void usbin_convert(struct usbin *u, unsigned char *buffer, unsigned int samples) @@ -747,14 +758,16 @@ } } -static int usbin_prepare_desc(struct usbin *u, struct usb_isoc_desc *id) +static int usbin_prepare_desc(struct usbin *u, purb_t urb) { - unsigned int i, maxsize; + unsigned int i, maxsize, offs; - maxsize = (u->freqn + 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 = 0; i < DESCFRAMES; i++) - id->frames[i].frame_length = maxsize; + maxsize = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); + //printk(KERN_DEBUG "usbin_prepare_desc: maxsize %d freq 0x%x format 0x%x\n", maxsize, u->freqn, u->format); + for (i = offs = 0; i < DESCFRAMES; i++, offs += maxsize) { + urb->iso_frame_desc[i].length = maxsize; + urb->iso_frame_desc[i].offset = offs; + } return 0; } @@ -762,20 +775,20 @@ * 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, struct usb_isoc_desc *id) +static int usbin_retire_desc(struct usbin *u, purb_t urb) { - unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, dmafree, maxsize; - unsigned char *cp = id->data; + unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, dmafree; + unsigned char *cp; ufmtsh = AFMT_BYTESSHIFT(u->format); - maxsize = (u->freqn + 0x3fff) >> (14 - ufmtsh); dfmtsh = AFMT_BYTESSHIFT(u->dma.format); - for (i = 0; i < DESCFRAMES; i++, cp += maxsize) { - if (id->frames[i].frame_status) { - printk(KERN_DEBUG "usbin_retire_desc: frame %u status %d\n", i, id->frames[i].frame_status); + for (i = 0; i < DESCFRAMES; i++) { + cp = ((unsigned char *)urb->transfer_buffer) + urb->iso_frame_desc[i].offset; + if (urb->iso_frame_desc[i].status) { + printk(KERN_DEBUG "usbin_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status); continue; } - scnt = id->frames[i].frame_length >> ufmtsh; + scnt = urb->iso_frame_desc[i].actual_length >> ufmtsh; if (!scnt) continue; cnt = scnt << dfmtsh; @@ -803,56 +816,52 @@ return err ? -1 : 0; } -static int usbin_completed(int status, void *__buffer, int rval, void *dev_id) +static void usbin_completed(struct urb *urb) { -#if 1 - struct usb_isoc_desc *id = (struct usb_isoc_desc *)dev_id; - struct usb_audiodev *as = (struct usb_audiodev *)id->context; -#else - struct usb_audiodev *as = (struct usb_audiodev *)dev_id; - struct usb_isoc_desc *id; -#endif + struct usb_audiodev *as = (struct usb_audiodev *)urb->context; struct usbin *u = &as->usbin; unsigned long flags; - unsigned int next, idmask; + unsigned int mask; + int suret = USB_ST_NOERROR; #if 0 - printk(KERN_DEBUG "usbin_completed: status %d rval %d flags 0x%x\n", status, rval, u->flags); + 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); - next = !(u->flags & FLG_NEXTID); - idmask = FLG_ID1RUNNING >> next; - u->flags = (u->flags & ~(FLG_NEXTID | idmask)) | next; - id = u->dataiso[!next]; - if (!usbin_retire_desc(u, id) && + if (!usbin_retire_desc(u, urb) && u->flags & FLG_RUNNING && - !usbin_prepare_desc(u, id) && - !usb_run_isoc(id, u->dataiso[next])) { - u->flags |= idmask; + !usbin_prepare_desc(u, urb) && + (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) { + u->flags |= mask; } else { - u->flags &= ~FLG_RUNNING; - printk(KERN_DEBUG "usbin_completed: descriptor not restarted\n"); - } - if (!(u->flags & idmask)) { - printk(KERN_DEBUG "usbin_completed: killing id\n"); - usb_kill_isoc(id); - printk(KERN_DEBUG "usbin_completed: id killed\n"); + 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); - return 0; } /* * we output sync data */ -static int usbin_sync_prepare_desc(struct usbin *u, struct usb_isoc_desc *id) +static int usbin_sync_prepare_desc(struct usbin *u, purb_t urb) { - unsigned char *cp = id->data; - unsigned int i; - - for (i = 0; i < DESCFRAMES; i++, cp += 3) { - id->frames[i].frame_length = 3; + unsigned char *cp = urb->transfer_buffer; + unsigned int i, offs; + + for (i = offs = 0; i < SYNCFRAMES; i++, offs += 3, cp += 3) { + urb->iso_frame_desc[i].length = 3; + urb->iso_frame_desc[i].offset = offs; cp[0] = u->freqn; cp[1] = u->freqn >> 8; cp[2] = u->freqn >> 16; @@ -863,230 +872,219 @@ /* * return value: 0 if descriptor should be restarted, -1 otherwise */ -static int usbin_sync_retire_desc(struct usbin *u, struct usb_isoc_desc *id) +static int usbin_sync_retire_desc(struct usbin *u, purb_t urb) { unsigned int i; - - for (i = 0; i < DESCFRAMES; i++) { - if (id->frames[i].frame_status) { - printk(KERN_DEBUG "usbin_sync_retire_desc: frame %u status %d\n", i, id->frames[i].frame_status); - continue; - } - } + + for (i = 0; i < SYNCFRAMES; i++) + if (urb->iso_frame_desc[0].status) + printk(KERN_DEBUG "usbin_sync_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status); return 0; } -static int usbin_sync_completed(int status, void *__buffer, int rval, void *dev_id) +static void usbin_sync_completed(struct urb *urb) { -#if 1 - struct usb_isoc_desc *id = (struct usb_isoc_desc *)dev_id; - struct usb_audiodev *as = (struct usb_audiodev *)id->context; -#else - struct usb_audiodev *as = (struct usb_audiodev *)dev_id; - struct usb_isoc_desc *id; -#endif + struct usb_audiodev *as = (struct usb_audiodev *)urb->context; struct usbin *u = &as->usbin; unsigned long flags; - unsigned int next, idmask; + unsigned int mask; + int suret = USB_ST_NOERROR; #if 0 - printk(KERN_DEBUG "usbin_sync_completed: status %d rval %d flags 0x%x\n", status, rval, u->flags); + 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; + + 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); - next = !(u->flags & FLG_SYNCNEXTID); - idmask = FLG_SYNC1RUNNING >> next; - u->flags = (u->flags & ~(FLG_SYNCNEXTID | idmask)) | ((-next) & FLG_SYNCNEXTID); - id = u->synciso[!next]; - if (!usbin_sync_retire_desc(u, id) && + if (!usbin_sync_retire_desc(u, urb) && u->flags & FLG_RUNNING && - !usbin_sync_prepare_desc(u, id) && - !usb_run_isoc(id, u->synciso[next])) { - u->flags |= idmask; + !usbin_sync_prepare_desc(u, urb) && + (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) { + u->flags |= mask; } else { - u->flags &= ~FLG_RUNNING; - printk(KERN_DEBUG "usbin_sync_completed: descriptor not restarted\n"); - } - if (!(u->flags & idmask)) { - printk(KERN_DEBUG "usbin_sync_completed: killing id\n"); - usb_kill_isoc(id); - printk(KERN_DEBUG "usbin_sync_completed: id killed\n"); + 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); - return 0; } -static void usbin_start(struct usb_audiodev *as) +static int usbin_start(struct usb_audiodev *as) { struct usb_device *dev = as->state->usbdev; struct usbin *u = &as->usbin; - struct usb_isoc_desc *id; + purb_t urb; unsigned long flags; - unsigned int which, i; + unsigned int maxsze, bufsz; #if 0 printk(KERN_DEBUG "usbin_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n", dev->devnum, u->format, u->dma.format, u->dma.srate); #endif /* allocate USB storage if not already done */ - /* UHCI wants the data to be page aligned - this is silly */ - if (!u->data[0]) - u->data[0] = (void *)get_free_page(GFP_KERNEL); - if (!u->data[1]) - u->data[1] = (void *)get_free_page(GFP_KERNEL); - if (!u->dataiso[0] && usb_init_isoc(dev, u->datapipe, DESCFRAMES, as, u->dataiso+0)) { - printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", - dev->devnum, u->datapipe); - u->dataiso[0] = NULL; - } - if (!u->dataiso[1] && usb_init_isoc(dev, u->datapipe, DESCFRAMES, as, u->dataiso+1)) { - printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", - dev->devnum, u->datapipe); - u->dataiso[1] = NULL; - } - if (u->syncpipe) { - if (!u->syncdata[0]) - u->syncdata[0] = (void *)get_free_page(GFP_KERNEL); - if (!u->syncdata[1]) - u->syncdata[1] = (void *)get_free_page(GFP_KERNEL); - if (!u->synciso[0] && usb_init_isoc(dev, u->syncpipe, DESCFRAMES, as, u->synciso+0)) { - printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", - dev->devnum, u->syncpipe); - u->synciso[0] = NULL; - } - if (!u->synciso[1] && usb_init_isoc(dev, u->syncpipe, DESCFRAMES, as, u->synciso+1)) { - printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", - dev->devnum, u->syncpipe); - u->synciso[1] = NULL; - } - } - if (!u->data[0] || !u->data[1] || !u->dataiso[0] || !u->dataiso[1] || - (u->syncpipe && (!u->syncdata[0] || !u->syncdata[1] || !u->synciso[0] || !u->synciso[1]))) { - printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum); - return; - } spin_lock_irqsave(&as->lock, flags); + if (!(u->flags & FLG_CONNECTED)) { + spin_unlock_irqrestore(&as->lock, flags); + return -EIO; + } if (!(u->flags & FLG_RUNNING)) { + spin_unlock_irqrestore(&as->lock, flags); u->freqn = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */ + u->freqmax = u->freqn + (u->freqn >> 2); u->phase = 0; + maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); + bufsz = DESCFRAMES * maxsze; + if (u->durb[0].urb.transfer_buffer) + kfree(u->durb[0].urb.transfer_buffer); + u->durb[0].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL); + u->durb[0].urb.transfer_buffer_length = bufsz; + if (u->durb[1].urb.transfer_buffer) + kfree(u->durb[1].urb.transfer_buffer); + u->durb[1].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL); + u->durb[1].urb.transfer_buffer_length = bufsz; + if (u->syncpipe) { + if (u->surb[0].urb.transfer_buffer) + kfree(u->surb[0].urb.transfer_buffer); + u->surb[0].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); + u->surb[0].urb.transfer_buffer_length = 3*SYNCFRAMES; + if (u->surb[1].urb.transfer_buffer) + kfree(u->surb[1].urb.transfer_buffer); + u->surb[1].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); + u->surb[1].urb.transfer_buffer_length = 3*SYNCFRAMES; + } + if (!u->durb[0].urb.transfer_buffer || !u->durb[1].urb.transfer_buffer || + (u->syncpipe && (!u->surb[0].urb.transfer_buffer || !u->surb[1].urb.transfer_buffer))) { + printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum); + return 0; + } + spin_lock_irqsave(&as->lock, flags); } - u->flags |= FLG_RUNNING; - if (!(u->flags & (FLG_ID0RUNNING|FLG_ID1RUNNING))) { - id = u->dataiso[0]; - id->start_type = START_ASAP; - id->start_frame = 0; - id->callback_frames = /*0*/DESCFRAMES; - id->callback_fn = usbin_completed; - id->data = u->data[0]; - id->buf_size = PAGE_SIZE; - u->flags &= ~FLG_NEXTID; - if (!usbin_prepare_desc(u, id) && !usb_run_isoc(id, NULL)) - u->flags |= FLG_ID0RUNNING; + 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; } - i = u->flags & (FLG_ID0RUNNING|FLG_ID1RUNNING); - if (u->flags & FLG_RUNNING && (i == FLG_ID0RUNNING || i == FLG_ID1RUNNING)) { - which = !(u->flags & FLG_ID1RUNNING); - id = u->dataiso[which]; - id->callback_frames = /*0*/DESCFRAMES; - id->callback_fn = usbin_completed; - id->data = u->data[which]; - id->buf_size = PAGE_SIZE; - if (!usbin_prepare_desc(u, id) && !usb_run_isoc(id, u->dataiso[!which])) - u->flags |= FLG_ID0RUNNING << which; + 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_SYNC0RUNNING|FLG_SYNC1RUNNING))) { - id = u->synciso[0]; - id->start_type = START_ASAP; - id->start_frame = 0; - id->callback_frames = /*0*/DESCFRAMES; - id->callback_fn = usbin_sync_completed; - id->data = u->syncdata[0]; - id->buf_size = PAGE_SIZE; - u->flags &= ~FLG_SYNCNEXTID; - if (!usbin_sync_prepare_desc(u, id) && !usb_run_isoc(id, NULL)) + 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; } - i = u->flags & (FLG_SYNC0RUNNING|FLG_SYNC1RUNNING); - if (u->flags & FLG_RUNNING && (i == FLG_SYNC0RUNNING || i == FLG_SYNC1RUNNING)) { - which = !(u->flags & FLG_SYNC1RUNNING); - id = u->synciso[which]; - id->callback_frames = /*0*/DESCFRAMES; - id->callback_fn = usbin_sync_completed; - id->data = u->syncdata[which]; - id->buf_size = PAGE_SIZE; - if (!usbin_sync_prepare_desc(u, id) && !usb_run_isoc(id, u->synciso[!which])) - u->flags |= FLG_SYNC0RUNNING << which; + 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); + spin_unlock_irqrestore(&as->lock, flags); + return 0; } static void usbout_stop(struct usb_audiodev *as) { struct usbout *u = &as->usbout; - unsigned long flags; - unsigned int i; + unsigned long flags; + unsigned int i, notkilled = 1; -printk(KERN_DEBUG "usb_audio: usbout_stop (1) flags 0x%04x\n", u->flags); - spin_lock_irqsave(&as->lock, flags); + spin_lock_irqsave(&as->lock, flags); u->flags &= ~FLG_RUNNING; i = u->flags; - spin_unlock_irqrestore(&as->lock, flags); -printk(KERN_DEBUG "usb_audio: usbout_stop (2) flags 0x%04x\n", i); - while (i & (FLG_ID0RUNNING|FLG_ID1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) { - set_current_state(TASK_INTERRUPTIBLE); + 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); - if (signal_pending(current)) { - if (i & FLG_ID0RUNNING) - usb_kill_isoc(u->dataiso[0]); - if (i & FLG_ID1RUNNING) - usb_kill_isoc(u->dataiso[1]); - if (i & FLG_SYNC0RUNNING) - usb_kill_isoc(u->synciso[0]); - if (i & FLG_SYNC1RUNNING) - usb_kill_isoc(u->synciso[1]); - break; - } spin_lock_irqsave(&as->lock, flags); i = u->flags; spin_unlock_irqrestore(&as->lock, flags); -printk(KERN_DEBUG "usb_audio: usbout_stop (3) flags 0x%04x\n", i); + 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->dataiso[0]) - usb_free_isoc(u->dataiso[0]); - if (u->dataiso[1]) - usb_free_isoc(u->dataiso[1]); - if (u->synciso[0]) - usb_free_isoc(u->synciso[0]); - if (u->synciso[1]) - usb_free_isoc(u->synciso[1]); - u->dataiso[0] = u->dataiso[1] = u->synciso[0] = u->synciso[1] = NULL; + 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 void usbout_release(struct usb_audiodev *as) +static inline void usbout_release(struct usb_audiodev *as) +{ + usbout_stop(as); +} + +static void usbout_disc(struct usb_audiodev *as) { struct usbout *u = &as->usbout; + unsigned long flags; + + spin_lock_irqsave(&as->lock, flags); + u->flags &= ~(FLG_RUNNING | FLG_CONNECTED); + spin_unlock_irqrestore(&as->lock, flags); usbout_stop(as); - if (u->data[0]) - free_page((unsigned long)u->data[0]); - if (u->data[1]) - free_page((unsigned long)u->data[1]); - if (u->syncdata[0]) - free_page((unsigned long)u->syncdata[0]); - if (u->syncdata[1]) - free_page((unsigned long)u->syncdata[1]); - u->data[0] = u->data[1] = u->syncdata[0] = u->syncdata[1] = NULL; } static void usbout_convert(struct usbout *u, unsigned char *buffer, unsigned int samples) @@ -1226,18 +1224,19 @@ } } -static int usbout_prepare_desc(struct usbout *u, struct usb_isoc_desc *id) +static int usbout_prepare_desc(struct usbout *u, purb_t urb) { - unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt; - unsigned char *cp = id->data; + unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, offs; + unsigned char *cp = urb->transfer_buffer; ufmtsh = AFMT_BYTESSHIFT(u->format); dfmtsh = AFMT_BYTESSHIFT(u->dma.format); - for (i = 0; i < DESCFRAMES; i++) { + for (i = offs = 0; i < DESCFRAMES; i++) { + urb->iso_frame_desc[i].offset = offs; u->phase = (u->phase & 0x3fff) + u->freqm; scnt = u->phase >> 14; if (!scnt) { - id->frames[i].frame_length = 0; + urb->iso_frame_desc[i].length = 0; continue; } cnt = scnt << dfmtsh; @@ -1258,7 +1257,8 @@ usbout_convert(u, cp, scnt); } cnt = scnt << ufmtsh; - id->frames[i].frame_length = cnt; + urb->iso_frame_desc[i].length = cnt; + offs += cnt; cp += cnt; } if (err) @@ -1276,87 +1276,82 @@ /* * return value: 0 if descriptor should be restarted, -1 otherwise */ -static int usbout_retire_desc(struct usbout *u, struct usb_isoc_desc *id) +static int usbout_retire_desc(struct usbout *u, purb_t urb) { unsigned int i; for (i = 0; i < DESCFRAMES; i++) { - if (id->frames[i].frame_status) { - printk(KERN_DEBUG "usbout_retire_desc: frame %u status %d\n", i, id->frames[i].frame_status); + if (urb->iso_frame_desc[i].status) { + printk(KERN_DEBUG "usbout_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status); continue; } } return 0; } -static int usbout_completed(int status, void *__buffer, int rval, void *dev_id) +static void usbout_completed(struct urb *urb) { -#if 1 - struct usb_isoc_desc *id = (struct usb_isoc_desc *)dev_id; - struct usb_audiodev *as = (struct usb_audiodev *)id->context; -#else - struct usb_audiodev *as = (struct usb_audiodev *)dev_id; - struct usb_isoc_desc *id; -#endif + struct usb_audiodev *as = (struct usb_audiodev *)urb->context; struct usbout *u = &as->usbout; unsigned long flags; - unsigned int next, idmask; + unsigned int mask; + int suret = USB_ST_NOERROR; #if 0 - printk(KERN_DEBUG "usbout_completed: status %d rval %d flags 0x%x\n", status, rval, u->flags); + 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); - next = !(u->flags & FLG_NEXTID); - idmask = FLG_ID1RUNNING >> next; - u->flags = (u->flags & ~(FLG_NEXTID | idmask)) | next; - id = u->dataiso[!next]; - if (!usbout_retire_desc(u, id) && + if (!usbout_retire_desc(u, urb) && u->flags & FLG_RUNNING && - !usbout_prepare_desc(u, id) && - !usb_run_isoc(id, u->dataiso[next])) { - u->flags |= idmask; + !usbout_prepare_desc(u, urb) && + (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) { + u->flags |= mask; } else { - u->flags &= ~FLG_RUNNING; - printk(KERN_DEBUG "usbout_completed: descriptor not restarted\n"); - } - if (!(u->flags & idmask)) { - printk(KERN_DEBUG "usbout_completed: killing id\n"); - usb_kill_isoc(id); - printk(KERN_DEBUG "usbout_completed: id killed\n"); + 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); - return 0; } -static int usbout_sync_prepare_desc(struct usbout *u, struct usb_isoc_desc *id) +static int usbout_sync_prepare_desc(struct usbout *u, purb_t urb) { - unsigned int i; + unsigned int i, offs; - for (i = 0; i < DESCFRAMES; i++) - id->frames[i].frame_length = 3; + for (i = offs = 0; i < SYNCFRAMES; i++, offs += 3) { + urb->iso_frame_desc[i].length = 3; + urb->iso_frame_desc[i].offset = offs; + } return 0; } /* * return value: 0 if descriptor should be restarted, -1 otherwise */ -static int usbout_sync_retire_desc(struct usbout *u, struct usb_isoc_desc *id) +static int usbout_sync_retire_desc(struct usbout *u, purb_t urb) { - unsigned char *cp = id->data; - unsigned int i, f; + unsigned char *cp = urb->transfer_buffer; + unsigned int f, i; - for (i = 0; i < DESCFRAMES; i++, cp += 3) { - if (id->frames[i].frame_status) { - printk(KERN_DEBUG "usbout_sync_retire_desc: frame %u status %d\n", i, id->frames[i].frame_status); + for (i = 0; i < SYNCFRAMES; i++, cp += 3) { + if (urb->iso_frame_desc[i].status) { + printk(KERN_DEBUG "usbout_sync_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status); continue; } - if (id->frames[i].frame_length < 3) { - printk(KERN_DEBUG "usbout_sync_retire_desc: frame %u length %d\n", i, id->frames[i].frame_length); + if (urb->iso_frame_desc[i].actual_length < 3) { + printk(KERN_DEBUG "usbout_sync_retire_desc: frame %u length %d\n", i, urb->iso_frame_desc[i].actual_length); continue; } f = cp[0] | (cp[1] << 8) | (cp[2] << 16); - if (abs(f - u->freqn) > (u->freqn >> 3)) { + if (abs(f - u->freqn) > (u->freqn >> 3) || f > u->freqmax) { printk(KERN_WARNING "usbout_sync_retire_desc: requested frequency %u (nominal %u) out of range!\n", f, u->freqn); continue; } @@ -1365,158 +1360,150 @@ return 0; } -static int usbout_sync_completed(int status, void *__buffer, int rval, void *dev_id) +static void usbout_sync_completed(struct urb *urb) { -#if 1 - struct usb_isoc_desc *id = (struct usb_isoc_desc *)dev_id; - struct usb_audiodev *as = (struct usb_audiodev *)id->context; -#else - struct usb_audiodev *as = (struct usb_audiodev *)dev_id; - struct usb_isoc_desc *id; -#endif + struct usb_audiodev *as = (struct usb_audiodev *)urb->context; struct usbout *u = &as->usbout; unsigned long flags; - unsigned int next, idmask; + unsigned int mask; + int suret = USB_ST_NOERROR; #if 0 - printk(KERN_DEBUG "usbout_sync_completed: status %d rval %d flags 0x%x\n", status, rval, u->flags); + 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); - next = !(u->flags & FLG_SYNCNEXTID); - idmask = FLG_SYNC1RUNNING >> next; - u->flags = (u->flags & ~(FLG_SYNCNEXTID | idmask)) | ((-next) & FLG_SYNCNEXTID); - id = u->synciso[!next]; - if (!usbout_sync_retire_desc(u, id) && + if (!usbout_sync_retire_desc(u, urb) && u->flags & FLG_RUNNING && - !usbout_sync_prepare_desc(u, id) && - !usb_run_isoc(id, u->synciso[next])) { - u->flags |= idmask; + !usbout_sync_prepare_desc(u, urb) && + (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) { + u->flags |= mask; } else { - u->flags &= ~FLG_RUNNING; - printk(KERN_DEBUG "usbout_sync_completed: descriptor not restarted\n"); - } - if (!(u->flags & idmask)) { - printk(KERN_DEBUG "usbout_sync_completed: killing id\n"); - usb_kill_isoc(id); - printk(KERN_DEBUG "usbout_sync_completed: id killed\n"); + 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); - return 0; } -static void usbout_start(struct usb_audiodev *as) +static int usbout_start(struct usb_audiodev *as) { struct usb_device *dev = as->state->usbdev; struct usbout *u = &as->usbout; - struct usb_isoc_desc *id; + purb_t urb; unsigned long flags; - unsigned int which, i; + 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 */ - /* UHCI wants the data to be page aligned - this is silly */ - if (!u->data[0]) - u->data[0] = (void *)get_free_page(GFP_KERNEL); - if (!u->data[1]) - u->data[1] = (void *)get_free_page(GFP_KERNEL); - if (!u->dataiso[0] && usb_init_isoc(dev, u->datapipe, DESCFRAMES, as, u->dataiso+0)) { - printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", - dev->devnum, u->datapipe); - u->dataiso[0] = NULL; - } - if (!u->dataiso[1] && usb_init_isoc(dev, u->datapipe, DESCFRAMES, as, u->dataiso+1)) { - printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", - dev->devnum, u->datapipe); - u->dataiso[1] = NULL; - } - if (u->syncpipe) { - if (!u->syncdata[0]) - u->syncdata[0] = (void *)get_free_page(GFP_KERNEL); - if (!u->syncdata[1]) - u->syncdata[1] = (void *)get_free_page(GFP_KERNEL); - if (!u->synciso[0] && usb_init_isoc(dev, u->syncpipe, DESCFRAMES, as, u->synciso+0)) { - printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", - dev->devnum, u->syncpipe); - u->synciso[0] = NULL; - } - if (!u->synciso[1] && usb_init_isoc(dev, u->syncpipe, DESCFRAMES, as, u->synciso+1)) { - printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", - dev->devnum, u->syncpipe); - u->synciso[1] = NULL; - } - } - if (!u->data[0] || !u->data[1] || !u->dataiso[0] || !u->dataiso[1] || - (u->syncpipe && (!u->syncdata[0] || !u->syncdata[1] || !u->synciso[0] || !u->synciso[1]))) { - printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum); - return; - } spin_lock_irqsave(&as->lock, flags); + if (!(u->flags & FLG_CONNECTED)) { + spin_unlock_irqrestore(&as->lock, flags); + return -EIO; + } if (!(u->flags & FLG_RUNNING)) { + spin_unlock_irqrestore(&as->lock, flags); u->freqn = u->freqm = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */ + u->freqmax = u->freqn + (u->freqn >> 2); u->phase = 0; + maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); + bufsz = DESCFRAMES * maxsze; + if (u->durb[0].urb.transfer_buffer) + kfree(u->durb[0].urb.transfer_buffer); + u->durb[0].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL); + u->durb[0].urb.transfer_buffer_length = bufsz; + if (u->durb[1].urb.transfer_buffer) + kfree(u->durb[1].urb.transfer_buffer); + u->durb[1].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL); + u->durb[1].urb.transfer_buffer_length = bufsz; + if (u->syncpipe) { + if (u->surb[0].urb.transfer_buffer) + kfree(u->surb[0].urb.transfer_buffer); + u->surb[0].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); + u->surb[0].urb.transfer_buffer_length = 3*SYNCFRAMES; + if (u->surb[1].urb.transfer_buffer) + kfree(u->surb[1].urb.transfer_buffer); + u->surb[1].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); + u->surb[1].urb.transfer_buffer_length = 3*SYNCFRAMES; + } + if (!u->durb[0].urb.transfer_buffer || !u->durb[1].urb.transfer_buffer || + (u->syncpipe && (!u->surb[0].urb.transfer_buffer || !u->surb[1].urb.transfer_buffer))) { + printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum); + return 0; + } + spin_lock_irqsave(&as->lock, flags); } u->flags |= FLG_RUNNING; - if (!(u->flags & (FLG_ID0RUNNING|FLG_ID1RUNNING))) { - id = u->dataiso[0]; - id->start_type = START_ASAP; - id->start_frame = 0; - id->callback_frames = /*0*/DESCFRAMES; - id->callback_fn = usbout_completed; - id->data = u->data[0]; - id->buf_size = PAGE_SIZE; - u->flags &= ~FLG_NEXTID; - if (!usbout_prepare_desc(u, id) && !usb_run_isoc(id, NULL)) - u->flags |= FLG_ID0RUNNING; + 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; } - i = u->flags & (FLG_ID0RUNNING|FLG_ID1RUNNING); - if (u->flags & FLG_RUNNING && (i == FLG_ID0RUNNING || i == FLG_ID1RUNNING)) { - which = !(u->flags & FLG_ID1RUNNING); - id = u->dataiso[which]; - id->callback_frames = /*0*/DESCFRAMES; - id->callback_fn = usbout_completed; - id->data = u->data[which]; - id->buf_size = PAGE_SIZE; - if (!usbout_prepare_desc(u, id) && !usb_run_isoc(id, u->dataiso[!which])) - u->flags |= FLG_ID0RUNNING << which; + 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_SYNC0RUNNING|FLG_SYNC1RUNNING))) { - id = u->synciso[0]; - id->start_type = START_ASAP; - id->start_frame = 0; - id->callback_frames = /*0*/DESCFRAMES; - id->callback_fn = usbout_sync_completed; - id->data = u->syncdata[0]; - id->buf_size = PAGE_SIZE; - u->flags &= ~FLG_NEXTID; - if (!usbout_sync_prepare_desc(u, id) && !usb_run_isoc(id, NULL)) + 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; } - i = u->flags & (FLG_SYNC0RUNNING|FLG_SYNC1RUNNING); - if (u->flags & FLG_RUNNING && (i == FLG_SYNC0RUNNING || i == FLG_SYNC1RUNNING)) { - which = !(u->flags & FLG_SYNC1RUNNING); - id = u->synciso[which]; - id->callback_frames = /*0*/DESCFRAMES; - id->callback_fn = usbout_sync_completed; - id->data = u->syncdata[which]; - id->buf_size = PAGE_SIZE; - if (!usbout_sync_prepare_desc(u, id) && !usb_run_isoc(id, u->synciso[!which])) - u->flags |= FLG_SYNC0RUNNING << which; + 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); + spin_unlock_irqrestore(&as->lock, flags); + return 0; } /* --------------------------------------------------------------------- */ @@ -1544,10 +1531,10 @@ static int set_format_in(struct usb_audiodev *as) { - struct usb_device *dev = as->state->usbdev; - struct usb_config_descriptor *config = dev->actconfig; - struct usb_interface_descriptor *alts; - struct usb_interface *iface; + struct usb_device *dev = as->state->usbdev; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface_descriptor *alts; + struct usb_interface *iface; struct usbin *u = &as->usbin; struct dmabuf *d = &u->dma; struct audioformat *fmt; @@ -1580,7 +1567,7 @@ d->srate = fmt->sratelo; if (d->srate > fmt->sratehi) d->srate = fmt->sratehi; - if (usb_set_interface(dev, u->interface, fmt->altsetting) < 0) { + if (usb_set_interface(dev, alts->bInterfaceNumber, fmt->altsetting) < 0) { printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n", dev->devnum, u->interface, fmt->altsetting); return -1; @@ -1624,10 +1611,10 @@ static int set_format_out(struct usb_audiodev *as) { - struct usb_device *dev = as->state->usbdev; - struct usb_config_descriptor *config = dev->actconfig; - struct usb_interface_descriptor *alts; - struct usb_interface *iface; + struct usb_device *dev = as->state->usbdev; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface_descriptor *alts; + struct usb_interface *iface; struct usbout *u = &as->usbout; struct dmabuf *d = &u->dma; struct audioformat *fmt; @@ -1645,6 +1632,13 @@ 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 < 2 || alts->endpoint[1].bmAttributes != 0x01 || alts->endpoint[1].bSynchAddress != 0 || @@ -1656,6 +1650,10 @@ 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 < fmt->sratelo) d->srate = fmt->sratelo; if (d->srate > fmt->sratehi) @@ -1763,13 +1761,13 @@ ch->value = v1 | (v2 << 8); v1 = (v1 * v3) / 100 + ch->minval; v2 = (v2 * v3) / 100 + ch->minval; - switch (ch->selector) { - case 0: /* mixer unit request */ + switch (ch->selector) { + case 0: /* mixer unit request */ data[0] = v1; data[1] = v1 >> 8; - if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - (ch->chnum << 8) | 1, ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) - goto err; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->chnum << 8) | 1, ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) return 0; data[0] = v2; @@ -1777,16 +1775,16 @@ if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, ((ch->chnum + !!(ch->flags & MIXFLG_STEREOIN)) << 8) | (1 + !!(ch->flags & MIXFLG_STEREOOUT)), ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) - goto err; + goto err; return 0; - /* various feature unit controls */ - case VOLUME_CONTROL: + /* various feature unit controls */ + case VOLUME_CONTROL: data[0] = v1; data[1] = v1 >> 8; - if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) - goto err; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; if (ch->chnum == 0) return 0; data[0] = v2; @@ -1795,14 +1793,14 @@ (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) goto err; return 0; - - case BASS_CONTROL: - case MID_CONTROL: - case TREBLE_CONTROL: + + case BASS_CONTROL: + case MID_CONTROL: + case TREBLE_CONTROL: data[0] = v1 >> 8; if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 1, HZ) < 0) - goto err; + (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 1, HZ) < 0) + goto err; if (ch->chnum == 0) return 0; data[0] = v2 >> 8; @@ -1810,15 +1808,15 @@ (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 1, HZ) < 0) goto err; return 0; - - default: - return -1; - } - return 0; + + default: + return -1; + } + return 0; err: - printk(KERN_ERR "usbaudio: mixer request device %u if %u unit %u ch %u selector %u failed\n", - dev->devnum, ms->iface, ch->unitid, ch->chnum, ch->selector); + printk(KERN_ERR "usbaudio: mixer request device %u if %u unit %u ch %u selector %u failed\n", + dev->devnum, ms->iface, ch->unitid, ch->chnum, ch->selector); return -1; } @@ -1840,10 +1838,11 @@ return; } up(&open_sem); - wake_up(&open_wait); + 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); @@ -1852,6 +1851,7 @@ } while (!list_empty(&s->mixerlist)) { ms = list_entry(s->mixerlist.next, struct usb_mixerdev, list); + list_del(&ms->list); kfree(ms); } @@ -1860,33 +1860,33 @@ extern inline int prog_dmabuf_in(struct usb_audiodev *as) { - usbin_stop(as); - return dmabuf_init(&as->usbin.dma); + 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); + 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; + return -ESPIPE; } /* --------------------------------------------------------------------- */ static int usb_audio_open_mixdev(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = MINOR(inode->i_rdev); struct list_head *devs, *mdevs; struct usb_mixerdev *ms; - struct usb_audio_state *s; + struct usb_audio_state *s; - down(&open_sem); + 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) { @@ -1903,185 +1903,189 @@ up(&open_sem); return -EIO; } - file->private_data = ms; + file->private_data = ms; s->count++; + MOD_INC_USE_COUNT; up(&open_sem); - return 0; + 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_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; + 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; + int i, j, val; - 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 */ + 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 < ms->numch; i++) + return put_user(0, (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < ms->numch; i++) val |= 1 << ms->ch[i].osschannel; - return put_user(val, (int *)arg); + return put_user(val, (int *)arg); - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + 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 < ms->numch; i++) + return put_user(0, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < ms->numch; i++) if (ms->ch[i].flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) val |= 1 << ms->ch[i].osschannel; - return put_user(val, (int *)arg); - - case SOUND_MIXER_CAPS: - return put_user(0, (int *)arg); - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES) - return -EINVAL; + 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 < ms->numch; j++) { if (ms->ch[j].osschannel == i) { return put_user(ms->ch[j].value, (int *)arg); } } return -EINVAL; - } - } - if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) - return -EINVAL; - ms->modcnt++; - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - get_user_ret(val, (int *)arg, -EFAULT); - /* set recording source: val */ - return 0; - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES) - 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 < ms->numch && ms->ch[j].osschannel != i; j++); if (j >= ms->numch) return -EINVAL; - get_user_ret(val, (int *)arg, -EFAULT); + get_user_ret(val, (int *)arg, -EFAULT); if (wrmixer(ms, j, val)) return -EIO; - return put_user(ms->ch[j].value, (int *)arg); - } + 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, /* check_media_change */ - NULL, /* revalidate */ - NULL, /* lock */ + &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, /* check_media_change */ + NULL, /* revalidate */ + 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 (;;) { + 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 <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&as->usbout.dma.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * count / as->usbout.dma.srate; + count = as->usbout.dma.count; + spin_unlock_irqrestore(&as->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&as->usbout.dma.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * count / as->usbout.dma.srate; tmo >>= AFMT_BYTESSHIFT(as->usbout.dma.format); - if (!schedule_timeout(tmo + 1)) { - printk(KERN_DEBUG "usbaudio: dma timed out??\n"); + 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; + } + 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; + 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; + 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); @@ -2093,21 +2097,25 @@ spin_unlock_irqrestore(&as->lock, flags); if (cnt > count) cnt = count; - if (cnt <= 0) { - usbin_start(as); - if (file->f_flags & O_NONBLOCK) { + if (cnt <= 0) { + if (usbin_start(as)) { + if (!ret) + ret = -ENODEV; + break; + } + if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; break; } schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; + 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; @@ -2124,60 +2132,64 @@ buffer += cnt; ret += cnt; } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&as->usbin.dma.wait, &wait); + __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; + 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; + 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) { + 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 < 0) { - as->usbout.dma.count = 0; - as->usbout.dma.rdptr = as->usbout.dma.wrptr; - } - ptr = as->usbout.dma.wrptr; - cnt = as->usbout.dma.dmasize - as->usbout.dma.count; + spin_lock_irqsave(&as->lock, flags); + if (as->usbout.dma.count < 0) { + as->usbout.dma.count = 0; + as->usbout.dma.rdptr = as->usbout.dma.wrptr; + } + ptr = as->usbout.dma.wrptr; + cnt = as->usbout.dma.dmasize - as->usbout.dma.count; /* set task state early to avoid wakeup races */ if (cnt <= 0) __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&as->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - usbout_start(as); - if (file->f_flags & O_NONBLOCK) { + spin_unlock_irqrestore(&as->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (usbout_start(as)) { + if (!ret) + ret = -ENODEV; + break; + } + if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; break; } schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; } continue; } @@ -2189,136 +2201,142 @@ 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; - usbout_start(as); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&as->usbout.dma.wait, &wait); + 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; + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + unsigned long flags; + unsigned int mask = 0; - if (file->f_mode & FMODE_WRITE) { + if (file->f_mode & FMODE_WRITE) { if (!as->usbout.dma.ready) prog_dmabuf_out(as); - poll_wait(file, &as->usbout.dma.wait, wait); + poll_wait(file, &as->usbout.dma.wait, wait); } - if (file->f_mode & FMODE_READ) { + if (file->f_mode & FMODE_READ) { if (!as->usbin.dma.ready) prog_dmabuf_in(as); - poll_wait(file, &as->usbin.dma.wait, wait); + 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_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; + 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 (vma->vm_pgoff != 0) - return -EINVAL; + 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_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; + 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; + 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_SETDUPLEX: + return 0; - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_BATCH, (int *)arg); - case SNDCTL_DSP_RESET: - if (file->f_mode & FMODE_WRITE) { - usbout_stop(as); - as->usbout.dma.rdptr = as->usbout.dma.wrptr = as->usbout.dma.count = as->usbout.dma.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - usbin_stop(as); - as->usbin.dma.rdptr = as->usbin.dma.wrptr = as->usbin.dma.count = as->usbin.dma.total_bytes = 0; - } - return 0; - - case SNDCTL_DSP_SPEED: - get_user_ret(val, (int *)arg, -EFAULT); - if (val >= 0) { - if (val < 4000) - val = 4000; - if (val > 100000) - val = 100000; + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + usbout_stop(as); + as->usbout.dma.rdptr = as->usbout.dma.wrptr = as->usbout.dma.count = as->usbout.dma.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + usbin_stop(as); + as->usbin.dma.rdptr = as->usbin.dma.wrptr = as->usbin.dma.count = as->usbin.dma.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (val < 4000) + val = 4000; + if (val > 100000) + val = 100000; if (set_format(as, file->f_mode, AFMT_QUERY, val)) return -EIO; - } - return put_user((file->f_mode & FMODE_READ) ? as->usbin.dma.srate : as->usbout.dma.srate, (int *)arg); + } + return put_user((file->f_mode & FMODE_READ) ? as->usbin.dma.srate : as->usbout.dma.srate, (int *)arg); - case SNDCTL_DSP_STEREO: + 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; + return 0; - case SNDCTL_DSP_CHANNELS: - get_user_ret(val, (int *)arg, -EFAULT); - if (val != 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; @@ -2328,14 +2346,14 @@ 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); + 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 | + 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); + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); if (val != AFMT_QUERY) { if (hweight32(val) != 1) return -EINVAL; @@ -2346,176 +2364,178 @@ 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); + 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_POST: - 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_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; - usbin_start(as); - } 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; - usbout_start(as); - } 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_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&as->lock, flags); + cinfo.bytes = as->usbin.dma.total_bytes; + cinfo.blocks = as->usbin.dma.count >> as->usbin.dma.fragshift; + cinfo.ptr = as->usbin.dma.wrptr; + if (as->usbin.dma.mapped) + as->usbin.dma.count &= as->usbin.dma.fragsize-1; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&as->lock, flags); + cinfo.bytes = as->usbout.dma.total_bytes; + cinfo.blocks = as->usbout.dma.count >> as->usbout.dma.fragshift; + cinfo.ptr = as->usbout.dma.rdptr; + if (as->usbout.dma.mapped) + as->usbout.dma.count &= as->usbout.dma.fragsize-1; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf_out(as))) - return val; - return put_user(as->usbout.dma.fragsize, (int *)arg); - } - if ((val = prog_dmabuf_in(as))) - return val; - return put_user(as->usbin.dma.fragsize, (int *)arg); - - case SNDCTL_DSP_SETFRAGMENT: - get_user_ret(val, (int *)arg, -EFAULT); - if (file->f_mode & FMODE_READ) { - as->usbin.dma.ossfragshift = val & 0xffff; - as->usbin.dma.ossmaxfrags = (val >> 16) & 0xffff; - if (as->usbin.dma.ossfragshift < 4) - as->usbin.dma.ossfragshift = 4; - if (as->usbin.dma.ossfragshift > 15) - as->usbin.dma.ossfragshift = 15; - if (as->usbin.dma.ossmaxfrags < 4) - as->usbin.dma.ossmaxfrags = 4; - } - if (file->f_mode & FMODE_WRITE) { - as->usbout.dma.ossfragshift = val & 0xffff; - as->usbout.dma.ossmaxfrags = (val >> 16) & 0xffff; - if (as->usbout.dma.ossfragshift < 4) - as->usbout.dma.ossfragshift = 4; - if (as->usbout.dma.ossfragshift > 15) - as->usbout.dma.ossfragshift = 15; - if (as->usbout.dma.ossmaxfrags < 4) - as->usbout.dma.ossmaxfrags = 4; - } - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && as->usbin.dma.subdivision) || - (file->f_mode & FMODE_WRITE && as->usbout.dma.subdivision)) - return -EINVAL; - get_user_ret(val, (int *)arg, -EFAULT); - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) - as->usbin.dma.subdivision = val; - if (file->f_mode & FMODE_WRITE) - as->usbout.dma.subdivision = val; - return 0; + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_out(as))) + return val; + return put_user(as->usbout.dma.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_in(as))) + return val; + return put_user(as->usbin.dma.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + as->usbin.dma.ossfragshift = val & 0xffff; + as->usbin.dma.ossmaxfrags = (val >> 16) & 0xffff; + if (as->usbin.dma.ossfragshift < 4) + as->usbin.dma.ossfragshift = 4; + if (as->usbin.dma.ossfragshift > 15) + as->usbin.dma.ossfragshift = 15; + if (as->usbin.dma.ossmaxfrags < 4) + as->usbin.dma.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + as->usbout.dma.ossfragshift = val & 0xffff; + as->usbout.dma.ossmaxfrags = (val >> 16) & 0xffff; + if (as->usbout.dma.ossfragshift < 4) + as->usbout.dma.ossfragshift = 4; + if (as->usbout.dma.ossfragshift > 15) + as->usbout.dma.ossfragshift = 15; + if (as->usbout.dma.ossmaxfrags < 4) + as->usbout.dma.ossmaxfrags = 4; + } + return 0; - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? as->usbin.dma.srate : as->usbout.dma.srate, (int *)arg); + 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_CHANNELS: + 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); + return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, (int *)arg); - case SOUND_PCM_READ_BITS: + 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; + 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); + int minor = MINOR(inode->i_rdev); DECLARE_WAITQUEUE(wait, current); struct list_head *devs, *adevs; struct usb_audiodev *as; - struct usb_audio_state *s; + struct usb_audio_state *s; for (;;) { down(&open_sem); @@ -2540,78 +2560,85 @@ break; if (file->f_flags & O_NONBLOCK) { up(&open_sem); - return -EBUSY; - } + return -EBUSY; + } __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&open_wait, &wait); - up(&open_sem); + up(&open_sem); schedule(); __set_current_state(TASK_RUNNING); remove_wait_queue(&open_wait, &wait); - if (signal_pending(current)) - return -ERESTARTSYS; - } + 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; + 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; + 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); + 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; + 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_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 (s->usbdev) - usb_set_interface(s->usbdev, as->usbout.interface, 0); - dmabuf_release(&as->usbout.dma); + 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 (s->usbdev) - usb_set_interface(s->usbdev, as->usbin.interface, 0); - dmabuf_release(&as->usbin.dma); + } + 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); + } + as->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); release(s); - wake_up(&open_wait); - MOD_DEC_USE_COUNT; - return 0; + 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, /* check_media_change */ - NULL, /* revalidate */ - NULL, /* lock */ + &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, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ }; /* --------------------------------------------------------------------- */ @@ -2646,7 +2673,7 @@ #if 0 struct usb_audio_device *aud = (struct usb_audio_device *)dev_id; - printk("irq on %p\n", aud); + printk(KERN_DEBUG "irq on %p\n", aud); #endif return 1; @@ -2731,6 +2758,7 @@ as->usbout.interface = asifout; /* search for input formats */ if (asifin >= 0) { + as->usbin.flags = FLG_CONNECTED; iface = &config->interface[asifin]; for (i = 0; i < iface->num_altsetting; i++) { alts = &iface->altsetting[i]; @@ -2807,6 +2835,7 @@ } /* search for output formats */ if (asifout >= 0) { + as->usbout.flags = FLG_CONNECTED; iface = &config->interface[asifout]; for (i = 0; i < iface->num_altsetting; i++) { alts = &iface->altsetting[i]; @@ -3211,10 +3240,10 @@ static void usb_audio_featureunit(struct consmixstate *state, unsigned char *ftr) { - struct usb_device *dev = state->s->usbdev; +// struct usb_device *dev = state->s->usbdev; struct mixerchannel *ch; unsigned short chftr, mchftr; - unsigned char data[1]; +// unsigned char data[1]; usb_audio_recurseunit(state, ftr[4]); if (state->nrchannels == 0) { @@ -3298,7 +3327,7 @@ printk(KERN_DEBUG "usbaudio: unmuting feature unit %u interface %u\n", ftr[3], state->ctrlif); data[0] = 0; if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - (MUTE_CONTROL << 8) | 0xff, state->ctrlif | (ftr[3] << 8), data, 1, HZ) < 0) + (MUTE_CONTROL << 8) | 0xff, state->ctrlif | (ftr[3] << 8), data, 1, HZ) < 0) printk(KERN_WARNING "usbaudio: failure to unmute feature unit %u interface %u\n", ftr[3], state->ctrlif); } #endif @@ -3422,7 +3451,7 @@ list_add_tail(&ms->list, &s->mixerlist); } -static void * usb_audio_parsecontrol(struct usb_device *dev, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif) +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; @@ -3430,7 +3459,7 @@ 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)); @@ -3438,6 +3467,7 @@ 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", @@ -3530,7 +3560,7 @@ /* we only care for the currently active configuration */ -static void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum) +static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum) { struct usb_config_descriptor *config = dev->actconfig; unsigned char *buffer; @@ -3538,16 +3568,21 @@ unsigned int i, buflen; int ret; - for (i = 0; i < config->bNumInterfaces; i++) - if (config->interface[i].altsetting[0].bInterfaceClass == USB_CLASS_AUDIO && - config->interface[i].altsetting[0].bInterfaceSubClass == 1) /* audiocontrol interface found */ - goto audioctrlfound; - 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; - - audioctrlfound: - /* find which configuration number is active */ +#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 < dev->descriptor.bNumConfigurations; i++) if (dev->config+i == config) goto configfound; @@ -3555,8 +3590,12 @@ return NULL; configfound: + if (usb_set_configuration(dev, config->bConfigurationValue) < 0) { + printk(KERN_ERR "usbaudio: set_configuration failed (ConfigValue 0x%x)\n", config->bConfigurationValue); + return NULL; + } ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buf, 8); - if (ret) { + if (ret<0) { printk(KERN_ERR "usbaudio: cannot get first 8 bytes of config descriptor %d of device %d\n", i, dev->devnum); return NULL; } @@ -3568,20 +3607,12 @@ if (!(buffer = kmalloc(buflen, GFP_KERNEL))) return NULL; ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buffer, buflen); - if (ret) { + if (ret<0) { kfree(buffer); printk(KERN_ERR "usbaudio: cannot get config descriptor %d of device %d\n", i, dev->devnum); return NULL; } - /* find first audio control interface; we currently cannot handle more than one */ - for (i = 0; i < config->bNumInterfaces; i++) { - if (config->interface[i].altsetting[0].bInterfaceClass != USB_CLASS_AUDIO || - config->interface[i].altsetting[0].bInterfaceSubClass != 1) - continue; - /* audiocontrol interface found */ - return usb_audio_parsecontrol(dev, buffer, buflen, i); - } - return NULL; + return usb_audio_parsecontrol(dev, buffer, buflen, ifnum); } @@ -3593,14 +3624,18 @@ struct list_head *list; struct usb_audiodev *as; struct usb_mixerdev *ms; - - down(&open_sem); + + 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; @@ -3609,6 +3644,7 @@ 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) @@ -3616,12 +3652,13 @@ aud->irq_handle = NULL; #endif release(s); - wake_up(&open_wait); + wake_up(&open_wait); } int usb_audio_init(void) { - return usb_register(&usb_audio_driver); + usb_register(&usb_audio_driver); + return 0; } #ifdef MODULE diff -u --recursive --new-file v2.3.33/linux/drivers/usb/bitstream.h linux/drivers/usb/bitstream.h --- v2.3.33/linux/drivers/usb/bitstream.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/bitstream.h Fri Dec 17 16:49:45 1999 @@ -0,0 +1,1508 @@ +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 -u --recursive --new-file v2.3.33/linux/drivers/usb/cpia.c linux/drivers/usb/cpia.c --- v2.3.33/linux/drivers/usb/cpia.c Tue Nov 23 22:42:21 1999 +++ linux/drivers/usb/cpia.c Fri Dec 17 16:49:45 1999 @@ -201,12 +201,14 @@ (sensorbaserate << 8) + sensorclkdivisor, 0, NULL, 0, HZ); } +#ifdef NOTUSED static int usb_cpia_grab_frame(struct usb_device *dev, int streamstartline) { return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_CPIA_GRAB_FRAME, USB_TYPE_VENDOR | USB_RECIP_DEVICE, streamstartline << 8, 0, NULL, 0, HZ); } +#endif static int usb_cpia_upload_frame(struct usb_device *dev, int forceupload) { @@ -514,23 +516,24 @@ /* * Make all of the blocks of data contiguous */ -static int cpia_compress_isochronous(struct usb_cpia *cpia, struct usb_isoc_desc *isodesc) +static int cpia_compress_isochronous(struct usb_cpia *cpia, urb_t *urb) { unsigned char *cdata, *data; int i, totlen = 0; - cdata = isodesc->data; data = cpia->scratch + cpia->scratchlen; - for (i = 0; i < isodesc->frame_count; i++) { - int n = isodesc->frames[i].frame_length; - int st = isodesc->frames[i].frame_status; + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; if (st && 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!\n"); + printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n",cpia->scratchlen, n ); return totlen; } @@ -540,30 +543,33 @@ totlen += n; cpia->scratchlen += n; } - cdata += isodesc->frame_size; } return totlen; } -static int cpia_isoc_irq(int status, void *__buffer, int len, void *isocdesc) +static void cpia_isoc_irq(struct urb *urb) { - void *dev_id = ((struct usb_isoc_desc *)isocdesc)->context; - struct usb_cpia *cpia = (struct usb_cpia *)dev_id; + 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 0; + return; } sbuf = &cpia->sbuf[cpia->cursbuf]; - usb_kill_isoc(sbuf->isodesc); + // usb_kill_isoc(sbuf->isodesc); /* Copy the data received into our scratch buffer */ - len = cpia_compress_isochronous(cpia, sbuf->isodesc); + len = cpia_compress_isochronous(cpia, urb); /* If we don't have a frame we're current working on, complain */ if (cpia->scratchlen) { @@ -574,22 +580,23 @@ cpia_parse_data(cpia); } - for (i = 0; i < FRAMES_PER_DESC; i++) - sbuf->isodesc->frames[i].frame_length = FRAME_SIZE_PER_DESC; - + for (i = 0; i < FRAMES_PER_DESC; i++) { + sbuf->urb->iso_frame_desc[i].status = 0; + sbuf->urb->iso_frame_desc[i].actual_length = 0; + } /* Move to the next sbuf */ cpia->cursbuf = (cpia->cursbuf + 1) % CPIA_NUMSBUF; /* Reschedule this block of Isochronous desc */ - usb_run_isoc(sbuf->isodesc, cpia->sbuf[cpia->cursbuf].isodesc); + // usb_run_isoc(sbuf->isodesc, cpia->sbuf[cpia->cursbuf].isodesc); - return -1; + return; } static int cpia_init_isoc(struct usb_cpia *cpia) { struct usb_device *dev = cpia->dev; - struct usb_isoc_desc *id; + urb_t *urb; int fx, err; cpia->compress = 0; @@ -598,57 +605,63 @@ cpia->scratchlen = 0; /* Alternate interface 3 is is the biggest frame size */ - if (usb_set_interface(cpia->dev, 1, 3) < 0) { + if (usb_set_interface(cpia->dev, cpia->iface, 3) < 0) { printk(KERN_ERR "usb_set_interface error\n"); return -EBUSY; } /* We double buffer the Iso lists */ - err = usb_init_isoc(dev, usb_rcvisocpipe(dev, 1), FRAMES_PER_DESC, - cpia, &cpia->sbuf[0].isodesc); - if (err) { +// err = usb_init_isoc(dev, usb_rcvisocpipe(dev, 1), FRAMES_PER_DESC, cpia, &cpia->sbuf[0].isodesc); + urb = usb_alloc_urb(FRAMES_PER_DESC); + + if (!urb) { printk(KERN_ERR "cpia_init_isoc: usb_init_isoc ret %d\n", - err); + 0); return -ENOMEM; } - - err = usb_init_isoc(dev, usb_rcvisocpipe(dev, 1), FRAMES_PER_DESC, - cpia, &cpia->sbuf[1].isodesc); - if (err) { + 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 < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + urb = usb_alloc_urb(FRAMES_PER_DESC); + if (!urb) { printk(KERN_ERR "cpia_init_isoc: usb_init_isoc ret %d\n", - err); - usb_free_isoc (cpia->sbuf[0].isodesc); + 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 < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } - /* Set the Isoc. desc. parameters. */ - /* First for desc. [0] */ - id = cpia->sbuf[0].isodesc; - id->start_type = START_ASAP; - id->callback_frames = 10; /* on every 10th frame */ - id->callback_fn = cpia_isoc_irq; - id->data = cpia->sbuf[0].data; - id->buf_size = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; - for (fx = 0; fx < FRAMES_PER_DESC; fx++) - id->frames[fx].frame_length = FRAME_SIZE_PER_DESC; - - /* and for desc. [1] */ - id = cpia->sbuf[1].isodesc; - id->start_type = 0; /* will follow the first desc. */ - id->callback_frames = 10; /* on every 10th frame */ - id->callback_fn = cpia_isoc_irq; - id->data = cpia->sbuf[1].data; - id->buf_size = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; - for (fx = 0; fx < FRAMES_PER_DESC; fx++) - id->frames[fx].frame_length = FRAME_SIZE_PER_DESC; - - err = usb_run_isoc(cpia->sbuf[0].isodesc, NULL); + 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 ret %d\n", + printk(KERN_ERR "cpia_init_isoc: usb_run_isoc(0) ret %d\n", err); - err = usb_run_isoc(cpia->sbuf[1].isodesc, cpia->sbuf[0].isodesc); + err = usb_submit_urb(cpia->sbuf[1].urb); if (err) - printk(KERN_ERR "cpia_init_isoc: usb_run_isoc ret %d\n", + printk(KERN_ERR "cpia_init_isoc: usb_run_isoc(1) ret %d\n", err); cpia->streaming = 1; @@ -668,20 +681,20 @@ } /* Set packet size to 0 */ - if (usb_set_interface(cpia->dev, 1, 0) < 0) { + if (usb_set_interface(cpia->dev, cpia->iface, 0) < 0) { printk(KERN_ERR "usb_set_interface error\n"); return /* -EINVAL */; } /* Unschedule all of the iso td's */ - usb_kill_isoc(cpia->sbuf[1].isodesc); - usb_kill_isoc(cpia->sbuf[0].isodesc); + usb_unlink_urb(cpia->sbuf[1].urb); + usb_unlink_urb(cpia->sbuf[0].urb); cpia->streaming = 0; /* Delete them all */ - usb_free_isoc(cpia->sbuf[1].isodesc); - usb_free_isoc(cpia->sbuf[0].isodesc); + 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) @@ -1197,12 +1210,8 @@ struct usb_device *dev = cpia->dev; unsigned char version[4]; - /* claim interface 1 */ - usb_driver_claim_interface(&cpia_driver, - &dev->actconfig->interface[1], cpia); - - /* Set altsetting 0 on interface 1 */ - if (usb_set_interface(dev, 1, 0) < 0) { + /* Set altsetting 0 */ + if (usb_set_interface(dev, cpia->iface, 0) < 0) { printk(KERN_ERR "usb_set_interface error\n"); return -EBUSY; } @@ -1273,7 +1282,7 @@ error: video_unregister_device(&cpia->vdev); usb_driver_release_interface(&cpia_driver, - &dev->actconfig->interface[1]); + &dev->actconfig->interface[0]); kfree(cpia); @@ -1314,6 +1323,7 @@ memset(cpia, 0, sizeof(*cpia)); cpia->dev = dev; + cpia->iface = interface->bInterfaceNumber; if (!usb_cpia_configure(cpia)) { cpia->user=0; @@ -1329,7 +1339,7 @@ video_unregister_device(&cpia->vdev); usb_driver_release_interface(&cpia_driver, - &cpia->dev->actconfig->interface[1]); + &cpia->dev->actconfig->interface[0]); /* Free the memory */ kfree(cpia); diff -u --recursive --new-file v2.3.33/linux/drivers/usb/cpia.h linux/drivers/usb/cpia.h --- v2.3.33/linux/drivers/usb/cpia.h Tue Nov 23 22:42:21 1999 +++ linux/drivers/usb/cpia.h Fri Dec 17 16:49:45 1999 @@ -137,7 +137,7 @@ struct cpia_sbuf { char *data; - struct usb_isoc_desc *isodesc; + urb_t *urb; }; enum { @@ -178,6 +178,8 @@ /* Device structure */ struct usb_device *dev; + + unsigned char iface; struct semaphore lock; int user; /* user count for exclusive use */ diff -u --recursive --new-file v2.3.33/linux/drivers/usb/dabusb.c linux/drivers/usb/dabusb.c --- v2.3.33/linux/drivers/usb/dabusb.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/dabusb.c Fri Dec 17 16:52:52 1999 @@ -0,0 +1,937 @@ +/*****************************************************************************/ + +/* + * dabusb.c -- dab usb driver. + * + * Copyright (C) 1999 Deti Fliegl (deti@fliegl.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * + * $Id: dabusb.c,v 1.30 1999/12/17 17:50:58 fliegl Exp $ + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usb.h" + +#include "dabusb.h" +#include "bitstream.h" +#include "firmware.h" +/* --------------------------------------------------------------------- */ + +#define NRDABUSB 4 + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define __init +#define __exit +#endif + +/*-------------------------------------------------------------------*/ +static dabusb_t dabusb[NRDABUSB]; +static int buffers = 256; +/*-------------------------------------------------------------------*/ +static int dabusb_add_buf_tail (pdabusb_t s, struct list_head *dst, struct list_head *src) +{ + unsigned long flags; + struct list_head *tmp; + int ret = 0; + + spin_lock_irqsave (&s->lock, flags); + + if (list_empty (src)) { + // no elements in source buffer + ret = -1; + goto err; + } + tmp = src->next; + list_del (tmp); + list_add_tail (tmp, dst); + + err: spin_unlock_irqrestore (&s->lock, flags); + return ret; +} +/*-------------------------------------------------------------------*/ +#ifdef DEBUG +static void dump_urb (purb_t purb) +{ + printk ("urb :%p\n", purb); + printk ("next :%p\n", purb->next); + printk ("dev :%p\n", purb->dev); + printk ("pipe :%08X\n", purb->pipe); + printk ("status :%d\n", purb->status); + printk ("transfer_flags :%08X\n", purb->transfer_flags); + printk ("transfer_buffer :%p\n", purb->transfer_buffer); + printk ("transfer_buffer_length:%d\n", purb->transfer_buffer_length); + printk ("actual_length :%d\n", purb->actual_length); + printk ("setup_packet :%p\n", purb->setup_packet); + printk ("start_frame :%d\n", purb->start_frame); + printk ("number_of_packets :%d\n", purb->number_of_packets); + printk ("interval :%d\n", purb->interval); + printk ("error_count :%d\n", purb->error_count); + printk ("context :%p\n", purb->context); + printk ("complete :%p\n", 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; + +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "dabusb_cancel_queue\n"); +#endif + 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; + +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "dabusb_free_queue\n"); +#endif + 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) +{ +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "dabusb_free_buffers\n"); +#endif + 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 + printk(KERN_DEBUG MODSTR"dabusb_iso_complete\n"); +#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 < purb->number_of_packets; i++) + if (purb->iso_frame_desc[i].status == USB_ST_NOERROR) { + len = purb->iso_frame_desc[i].actual_length; + if (len <= pipesize) { + memcpy (buf + dst, buf + purb->iso_frame_desc[i].offset, len); + dst += len; + } + else + printk (KERN_ERR MODSTR "dabusb_iso_complete: invalid len %d\n", len); + } + if (dst != purb->actual_length) + printk (KERN_ERR MODSTR "dst!=purb->actual_length:%d!=%d\n", dst, purb->actual_length); + } + + if (atomic_dec_and_test (&s->pending_io) && !s->remove_pending && s->state != _stopped) { + s->overruns++; + printk (KERN_ERR MODSTR "overrun (%d)\n", 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); + +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "dabusb_alloc_buffers len:%d pipesize:%d packets:%d transfer_buffer_len:%d\n", + len, pipesize, packets, transfer_buffer_length); +#endif + + while (buffers < (s->total_buffer_size << 10)) { + b = (pbuff_t) kmalloc (sizeof (buff_t), GFP_KERNEL); + if (!b) { + printk (KERN_ERR MODSTR "kmalloc(sizeof(buff_t))==NULL\n"); + goto err; + } + memset (b, sizeof (buff_t), 0); + b->s = s; + b->purb = (purb_t) kmalloc (len, GFP_KERNEL); + if (!b->purb) { + printk (KERN_ERR MODSTR "kmalloc(sizeof(urb_t)+packets*sizeof(iso_packet_descriptor_t))==NULL\n"); + 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); + printk (KERN_ERR MODSTR "kmalloc(%d)==NULL\n", transfer_buffer_length); + goto err; + } + + b->purb->transfer_buffer_length = transfer_buffer_length; + b->purb->number_of_packets = packets; + b->purb->complete = dabusb_iso_complete; + b->purb->context = b; + b->purb->dev = s->usbdev; + b->purb->pipe = pipe; + b->purb->transfer_flags = USB_ISO_ASAP; + + for (i = 0; i < packets; i++) { + b->purb->iso_frame_desc[i].offset = i * pipesize; + b->purb->iso_frame_desc[i].length = pipesize; + } + + buffers += transfer_buffer_length; + list_add_tail (&b->buff_list, &s->free_buff_list); + } + s->got_mem = buffers; + + return 0; + +err: + dabusb_free_buffers (s); + return -ENOMEM; +} +/*-------------------------------------------------------------------*/ +static int dabusb_reset_pipe (struct usb_device *usbdev, unsigned int ep) +{ +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "dabusb_reset_pipe\n"); +#endif + 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 < 0) { + printk (KERN_DEBUG MODSTR "dabusb_bulk: usb_submit_urb returned %d\n", ret); + return -EINVAL; + } + interruptible_sleep_on_timeout (&context.wait, HZ); + if (purb->status == USB_ST_URB_PENDING) { + printk (KERN_ERR MODSTR "dabusb_usb_submit_urb: %p timed out\n", 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 + printk(KERN_DEBUG MODSTR"dabusb_bulk_complete\n"); + 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 + printk(KERN_DEBUG MODSTR"dabusb_bulk\n"); +#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) { + printk (KERN_ERR MODSTR "dabusb_writemem: kmalloc(8) failed.\n"); + return -ENOMEM; + } + transfer_buffer = kmalloc (len, GFP_KERNEL); + if (!transfer_buffer) { + printk (KERN_ERR MODSTR "dabusb_writemem: kmalloc(%d) failed.\n", 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 < 0) + return ret; + return urb.status; +} +/* --------------------------------------------------------------------- */ +static int dabusb_8051_reset (pdabusb_t s, unsigned char reset_bit) +{ +#ifdef DEBUG + printk("dabusb_8051_reset: %d\n",reset_bit); +#endif + return dabusb_writemem (s, CPUCS_REG, &reset_bit, 1); +} +/* --------------------------------------------------------------------- */ +static int dabusb_loadmem (pdabusb_t s, const char *fname) +{ + int ret; + PINTEL_HEX_RECORD ptr = firmware; + +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "Enter dabusb_loadmem (internal)\n"); +#endif + ret = dabusb_8051_reset (s, 1); + while (ptr->Type == 0) { +#ifdef DEBUG_ALL + printk(KERN_ERR MODSTR"dabusb_writemem: %04X %p %d)\n", ptr->Address, ptr->Data, ptr->Length); +#endif + ret = dabusb_writemem (s, ptr->Address, ptr->Data, ptr->Length); + if (ret < 0) { + printk (KERN_ERR MODSTR "dabusb_writemem failed (%04X %p %d)\n", ptr->Address, ptr->Data, ptr->Length); + break; + } + ptr++; + } + ret = dabusb_8051_reset (s, 0); +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "dabusb_loadmem: exit\n"); +#endif + 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; + +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "dabusb_fpga_clear\n"); +#endif + 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; + +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "dabusb_fpga_init\n"); +#endif + 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; + +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "Enter dabusb_fpga_download (internal)\n"); +#endif + if (!b) { + printk (KERN_ERR MODSTR "kmalloc(sizeof(bulk_transfer_t))==NULL\n"); + return -ENOMEM; + } + + b->pipe = 1; + ret = dabusb_fpga_clear (s, b); + mdelay (10); + blen = buf[73] + (buf[72] << 8); +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "Bitstream len: %i\n", blen); +#endif + b->data[0] = 0x2b; + b->data[1] = 0; + b->data[2] = 0; + b->data[3] = 60; + + for (n = 0; n <= blen + 60; n += 60) { + // some cclks for startup + b->size = 64; + memcpy (b->data + 4, buf + 74 + n, 60); + ret = dabusb_bulk (s, b); + if (ret < 0) { + printk (KERN_ERR MODSTR "dabusb_bulk failed.\n"); + break; + } + mdelay (1); + } + + ret = dabusb_fpga_init (s, b); + kfree (b); + +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "exit dabusb_fpga_download\n"); +#endif + return ret; +} + +static loff_t dabusb_llseek (struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static int dabusb_stop (pdabusb_t s) +{ +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "dabusb_stop\n"); +#endif + + s->state = _stopped; + dabusb_cancel_queue (s, &s->rec_buff_list); + +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "pending_io: %d\n", s->pending_io.counter); +#endif + + s->pending_io.counter = 0; + return 0; +} + +static int dabusb_startrek (pdabusb_t s) +{ + if (!s->got_mem && s->state != _started) { +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "dabusb_startrek\n"); +#endif + + if (dabusb_alloc_buffers (s) < 0) + return -ENOMEM; + dabusb_stop (s); + dabusb_reset_pipe (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 + printk("submitting: end:%p s->rec_buff_list:%p\n", 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) { + printk (KERN_ERR MODSTR "usb_submit_urb returned:%d\n", ret); + if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list)) + printk (KERN_ERR MODSTR "startrek: dabusb_add_buf_tail failed"); + } + else + atomic_inc (&s->pending_io); + } +#ifdef DEBUG_ALL + printk(KERN_DEBUG MODSTR"pending_io: %d\n",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 + printk(KERN_DEBUG MODSTR"dabusb_read\n"); +#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)) { + printk (KERN_ERR MODSTR "error: rec_buf_list is empty\n"); + 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)) { + printk (KERN_ERR MODSTR "error: still no buffer available.\n"); + 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 + printk("copy_to_user:%p %p %d\n",buf, purb->transfer_buffer + s->readptr, cnt); +#endif + + if (copy_to_user (buf, purb->transfer_buffer + s->readptr, cnt)) { + printk (KERN_ERR MODSTR "read: copy_to_user failed\n"); + 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)) + printk (KERN_ERR MODSTR "read: dabusb_add_buf_tail failed"); + s->readptr = 0; + } + } +err: //up(&s->mutex); + return ret; +} + +static int dabusb_open (struct inode *inode, struct file *file) +{ + int devnum = MINOR (inode->i_rdev); + pdabusb_t s; + + if (devnum < DABUSB_MINOR || devnum > (DABUSB_MINOR + NRDABUSB)) + return -EIO; + + MOD_INC_USE_COUNT; + s = &dabusb[devnum - DABUSB_MINOR]; + + printk (KERN_DEBUG MODSTR "dabusb_open\n"); + 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) < 0) { + printk (KERN_ERR "dabusb: set_interface failed\n"); + MOD_DEC_USE_COUNT; + return -EINVAL; + } + file->f_pos = 0; + file->private_data = s; + + return 0; +} + +static int dabusb_release (struct inode *inode, struct file *file) +{ + pdabusb_t s = (pdabusb_t) file->private_data; + + printk (KERN_DEBUG MODSTR "dabusb_release\n"); + + down (&s->mutex); + dabusb_stop (s); + dabusb_free_buffers (s); + up (&s->mutex); + + if (!s->remove_pending) { + if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) + printk (KERN_ERR "dabusb: set_interface failed\n"); + } + else + wake_up (&s->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); + +// printk(KERN_DEBUG MODSTR"dabusb_ioctl\n"); + + 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, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +static int dabusb_find_struct (void) +{ + int u; + + for (u = 0; u < NRDABUSB; u++) { + pdabusb_t s = &dabusb[u]; + if (!s->usbdev) + return u; + } + return -1; +} + +/* --------------------------------------------------------------------- */ +static void *dabusb_probe (struct usb_device *usbdev, unsigned int ifnum) +{ + int devnum; + pdabusb_t s; + +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "dabusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum); +#endif + + /* the 1234:5678 is just a self assigned test ID */ + if ((usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x2131) && + (usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x9999)) + return NULL; + + /* We don't handle multiple configurations */ + if (usbdev->descriptor.bNumConfigurations != 1) + return NULL; + + if (ifnum != _DABUSB_IF && usbdev->descriptor.idProduct == 0x9999) + return NULL; + + devnum = dabusb_find_struct (); + if (devnum == -1) + return NULL; + + s = &dabusb[devnum]; + + down (&s->mutex); + s->remove_pending = 0; + s->usbdev = usbdev; + + if (usb_set_configuration (usbdev, usbdev->config[0].bConfigurationValue) < 0) { + printk (KERN_ERR MODSTR "set_configuration failed\n"); + goto reject; + } + if (usbdev->descriptor.idProduct == 0x2131) + dabusb_loadmem (s, NULL); + else { + dabusb_fpga_download (s, NULL); + + if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) { + printk (KERN_ERR MODSTR "set_interface failed\n"); + goto reject; + } + } + printk (KERN_DEBUG MODSTR "bound to interface: %d\n", ifnum); + up (&s->mutex); + MOD_INC_USE_COUNT; + return s; + +reject: + up (&s->mutex); + s->usbdev = NULL; + return NULL; +} + +static void dabusb_disconnect (struct usb_device *usbdev, void *ptr) +{ + pdabusb_t s = (pdabusb_t) ptr; + + printk (KERN_DEBUG MODSTR "dabusb_disconnect\n"); + + 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 < NRDABUSB; u++) { + pdabusb_t s = &dabusb[u]; + memset (s, 0, sizeof (dabusb_t)); + init_MUTEX (&s->mutex); + s->usbdev = NULL; + s->total_buffer_size = buffers; + init_waitqueue_head (&s->wait); + init_waitqueue_head (&s->remove_ok); + spin_lock_init (&s->lock); + INIT_LIST_HEAD (&s->free_buff_list); + INIT_LIST_HEAD (&s->rec_buff_list); + } + + /* register misc device */ + usb_register (&dabusb_driver); + +#ifdef DEBUG + printk (KERN_INFO "dabusb_init: driver registered\n"); +#endif + return 0; +} + +void __exit dabusb_cleanup (void) +{ +#ifdef DEBUG + printk (KERN_DEBUG MODSTR "dabusb_cleanup\n"); +#endif + usb_deregister (&dabusb_driver); +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE +MODULE_AUTHOR ("Deti Fliegl, deti@fliegl.de"); +MODULE_DESCRIPTION ("DAB-USB Interface Driver for Linux (c)1999"); +MODULE_PARM (buffers, "i"); + +int __init init_module (void) +{ + return dabusb_init (); +} + +void __exit cleanup_module (void) +{ + dabusb_cleanup (); +} + +#endif + +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.3.33/linux/drivers/usb/dabusb.h linux/drivers/usb/dabusb.h --- v2.3.33/linux/drivers/usb/dabusb.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/dabusb.h Thu Dec 16 01:27:42 1999 @@ -0,0 +1,91 @@ +#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__ + +#ifdef MODSTR +#undef MODSTR +#endif +#define MODSTR "dabusb: " + +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 \ No newline at end of file diff -u --recursive --new-file v2.3.33/linux/drivers/usb/ezusb.c linux/drivers/usb/ezusb.c --- v2.3.33/linux/drivers/usb/ezusb.c Tue Nov 23 22:42:21 1999 +++ linux/drivers/usb/ezusb.c Fri Dec 17 16:56:55 1999 @@ -29,19 +29,22 @@ * 0.3 01.09.99 Async Bulk and ISO support * 0.4 01.09.99 Set callback_frames to the total number of frames to make * it work with OHCI-HCD - * + * 03.12.99 Now that USB error codes are negative, return them to application + * instead of ENXIO + * $Id: ezusb.c,v 1.22 1999/12/03 15:06:28 tom Exp $ */ /*****************************************************************************/ - +#include #include #include +#include #include #include #include #include -#include +//#include #include "usb.h" #include "ezusb.h" @@ -62,50 +65,64 @@ struct async { struct list_head asynclist; struct ezusb *ez; - void *data; - unsigned dataorder; void *userdata; unsigned datalen; - union { - struct usb_isoc_desc *iso; - void *bulk; - } desc; - unsigned numframes; /* 0 means bulk, > 0 means iso */ - struct ezusb_asynccompleted completed; + void *context; + urb_t urb; }; +/*-------------------------------------------------------------------*/ + +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 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; + 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; } #if 0 /* why doesn't this work properly on i386? */ extern inline unsigned int ld2(unsigned int x) { - unsigned int r; + unsigned int r; __asm__("bsrl %1,%0" : "=r" (r) : "g" (x)); return r; @@ -135,14 +152,14 @@ spin_unlock_irqrestore(&ez->lock, flags); } -extern __inline__ void async_movetocompleted(struct async *as) +extern __inline__ void async_removepending(struct async *as) { struct ezusb *ez = as->ez; unsigned long flags; - + spin_lock_irqsave(&ez->lock, flags); list_del(&as->asynclist); - list_add_tail(&as->asynclist, &ez->async_completed); + INIT_LIST_HEAD(&as->asynclist); spin_unlock_irqrestore(&ez->lock, flags); } @@ -171,7 +188,7 @@ for (p = ez->async_pending.next; p != &ez->async_pending; ) { as = list_entry(p, struct async, asynclist); p = p->next; - if (as->completed.context != context) + if (as->context != context) continue; list_del(&as->asynclist); INIT_LIST_HEAD(&as->asynclist); @@ -184,69 +201,19 @@ /* --------------------------------------------------------------------- */ -static int bulk_completed(int status, void *__buffer, int rval, void *dev_id) -{ - struct async *as = (struct async *)dev_id; - struct ezusb *ez = as->ez; - unsigned cnt; - -printk(KERN_DEBUG "ezusb: bulk_completed: status %d rval %d\n", status, rval); - as->completed.length = rval; - as->completed.status = status; - spin_lock(&ez->lock); - list_del(&as->asynclist); - list_add_tail(&as->asynclist, &ez->async_completed); - spin_unlock(&ez->lock); - wake_up(&ez->wait); - return 0; -} - -static int iso_completed(int status, void *__buffer, int rval, void *dev_id) +static void async_completed(purb_t urb) { -#if 1 - struct async *as = (struct async *)dev_id; -#else - struct usb_isoc_desc *id = (struct usb_isoc_desc *)dev_id; - struct async *as = (struct async *)id->context; -#endif + struct async *as = (struct async *)urb->context; struct ezusb *ez = as->ez; unsigned cnt; -printk(KERN_DEBUG "ezusb: iso_completed: status %d rval %d\n", status, rval); - as->completed.length = rval; - as->completed.status = USB_ST_NOERROR; - for (cnt = 0; cnt < as->numframes; cnt++) { - as->completed.isostat[cnt].status = as->desc.iso->frames[cnt].frame_status; - as->completed.isostat[cnt].length = as->desc.iso->frames[cnt].frame_length; - } +printk(KERN_DEBUG "ezusb: async_completed: status %d errcount %d actlen %d pipe 0x%x\n", + urb->status, urb->error_count, urb->actual_length, urb->pipe); spin_lock(&ez->lock); list_del(&as->asynclist); list_add_tail(&as->asynclist, &ez->async_completed); spin_unlock(&ez->lock); wake_up(&ez->wait); - return 0; -} - -static void remove_async(struct async *as) -{ - if (as->data && as->dataorder) - free_pages((unsigned long)as->data, as->dataorder); - if (as->numframes) - usb_free_isoc(as->desc.iso); - kfree(as); -} - -static void kill_async(struct async *as) -{ - struct ezusb *ez = as->ez; - - if (as->numframes) - /* ISO case */ - usb_kill_isoc(as->desc.iso); - else - usb_terminate_bulk(ez->usbdev, as->desc.bulk); - as->completed.status = USB_ST_REMOVED; - async_movetocompleted(as); } static void destroy_all_async(struct ezusb *ez) @@ -260,12 +227,13 @@ list_del(&as->asynclist); INIT_LIST_HEAD(&as->asynclist); spin_unlock_irqrestore(&ez->lock, flags); - kill_async(as); + /* discard_urb calls the completion handler with status == USB_ST_URB_KILLED */ + usb_unlink_urb(&as->urb); spin_lock_irqsave(&ez->lock, flags); } spin_unlock_irqrestore(&ez->lock, flags); while ((as = async_getcompleted(ez))) - remove_async(as); + free_async(as); } /* --------------------------------------------------------------------- */ @@ -316,7 +284,7 @@ *ppos = pos; if (ret) return ret; - return -ENXIO; + return i; } if (copy_to_user(buf, b, len)) { up(&ez->mutex); @@ -364,6 +332,7 @@ return ret; return -EFAULT; } + printk("writemem: %d %p %d\n",pos,b,len); i = usb_control_msg(ez->usbdev, usb_sndctrlpipe(ez->usbdev, 0), 0xa0, 0x40, pos, 0, b, len, HZ); if (i < 0) { up(&ez->mutex); @@ -371,7 +340,7 @@ *ppos = pos; if (ret) return ret; - return -ENXIO; + return i; } pos += len; buf += len; @@ -453,7 +422,7 @@ free_page((unsigned long)tbuf); printk(KERN_WARNING "ezusb: EZUSB_CONTROL failed rqt %u rq %u len %u ret %d\n", requesttype, request, length, i); - return -ENXIO; + return i; } if (requesttype & 0x80 && length > 0 && copy_to_user(data, tbuf, length)) i = -EFAULT; @@ -531,10 +500,57 @@ return 0; } +#define usb_sndintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) + +char* stuff[512]; + +static void int_compl(purb_t purb) +{ + printk("INT_COMPL\n"); + +} + +static int ezusb_interrupt(struct ezusb *ez, struct ezusb_interrupt *ab) +{ + urb_t *urb; + unsigned int pipe; + + urb=(urb_t*)kmalloc(sizeof(urb_t),GFP_KERNEL); + if (!urb) + { + return -ENOMEM; + } + memset(urb,0,sizeof(urb_t)); + + + if ((ab->ep & ~0x80) >= 16) + return -EINVAL; + if (ab->ep & 0x80) + { + pipe = usb_rcvintpipe(ez->usbdev, ab->ep & 0x7f); + if (ab->len > 0 && !access_ok(VERIFY_WRITE, ab->data, ab->len)) + return -EFAULT; + } + else + pipe = usb_sndintpipe(ez->usbdev, ab->ep & 0x7f); + + memcpy(stuff,ab->data,64); + urb->transfer_buffer=stuff; + urb->transfer_buffer_length=ab->len; + urb->complete=int_compl; + urb->pipe=pipe; + urb->dev=ez->usbdev; + urb->interval=ab->interval; + return usb_submit_urb(urb); +} + static int ezusb_requestbulk(struct ezusb *ez, struct ezusb_asyncbulk *ab) { struct async *as = NULL; + void *data = NULL; unsigned int pipe; + int ret; if (ab->len > PAGE_SIZE) return -EINVAL; @@ -548,58 +564,48 @@ pipe = usb_sndbulkpipe(ez->usbdev, ab->ep & 0x7f); if (!usb_maxpacket(ez->usbdev, pipe, !(ab->ep & 0x80))) return -EINVAL; - if (!(as = kmalloc(sizeof(struct async), GFP_KERNEL))) + if (ab->len > 0 && !(data = kmalloc(ab->len, GFP_KERNEL))) + return -ENOMEM; + if (!(as = alloc_async(0))) { + if (data) + kfree(data); return -ENOMEM; + } INIT_LIST_HEAD(&as->asynclist); as->ez = ez; as->userdata = ab->data; - as->numframes = 0; - as->data = 0; - as->dataorder = 0; as->datalen = ab->len; - as->completed.context = ab->context; - if (ab->len > 0) { - as->dataorder = 1; - if (!(as->data = (unsigned char *)__get_free_page(GFP_KERNEL))) { - kfree(as); - return -ENOMEM; - } - if (!(ab->ep & 0x80)) { - if (copy_from_user(as->data, ab->data, ab->len)) - goto err_fault; - as->datalen = 0; /* no need to copy back at completion */ + as->context = ab->context; + as->urb.dev = ez->usbdev; + as->urb.pipe = pipe; + as->urb.transfer_flags = 0; + as->urb.transfer_buffer = data; + as->urb.transfer_buffer_length = ab->len; + as->urb.context = as; + as->urb.complete = (usb_complete_t)async_completed; + if (ab->len > 0 && !(ab->ep & 0x80)) { + if (copy_from_user(data, ab->data, ab->len)) { + free_async(as); + return -EFAULT; } + as->userdata = NULL; /* no need to copy back at completion */ } async_newpending(as); - if (!(as->desc.bulk = usb_request_bulk(ez->usbdev, pipe, bulk_completed, as->data, ab->len, as))) { - async_removelist(as); - goto err_inval; + if ((ret = usb_submit_urb(&as->urb))) { + printk(KERN_DEBUG "ezusb: bulk: usb_submit_urb returned %d\n", ret); + async_removepending(as); + free_async(as); + return -EINVAL; /* return ret; */ } return 0; - - err_fault: - if (as) { - if (as->data) - free_page((unsigned long)as->data); - kfree(as); - } - return -EFAULT; - - err_inval: - if (as) { - if (as->data) - free_page((unsigned long)as->data); - kfree(as); - } - return -EINVAL; } static int ezusb_requestiso(struct ezusb *ez, struct ezusb_asynciso *ai, unsigned char *cmd) { struct async *as; - unsigned int maxpkt, pipe; - unsigned int dsize, order, assize, j; - int i; + void *data = NULL; + unsigned int maxpkt, pipe, dsize, totsize, i, j; + int ret; if ((ai->ep & ~0x80) >= 16 || ai->framecnt < 1 || ai->framecnt > 128) return -EINVAL; @@ -610,87 +616,62 @@ if (!(maxpkt = usb_maxpacket(ez->usbdev, pipe, !(ai->ep & 0x80)))) return -EINVAL; dsize = maxpkt * ai->framecnt; -printk(KERN_DEBUG "ezusb: iso: dsize %d\n", dsize); +//printk(KERN_DEBUG "ezusb: iso: dsize %d\n", dsize); if (dsize > 65536) return -EINVAL; - order = ld2(dsize >> PAGE_SHIFT); - if (dsize > (PAGE_SIZE << order)) - order++; if (ai->ep & 0x80) if (dsize > 0 && !access_ok(VERIFY_WRITE, ai->data, dsize)) return -EFAULT; - assize = sizeof(struct async) + ai->framecnt * sizeof(struct ezusb_isoframestat); - if (!(as = kmalloc(assize, GFP_KERNEL))) + if (dsize > 0 && !(data = kmalloc(dsize, GFP_KERNEL))) + { + printk("dsize: %d failed\n",dsize); return -ENOMEM; -printk(KERN_DEBUG "ezusb: iso: assize %d\n", assize); - memset(as, 0, assize); + } + if (!(as = alloc_async(ai->framecnt))) { + if (data) + kfree(data); + printk("alloc_async failed\n"); + return -ENOMEM; + } INIT_LIST_HEAD(&as->asynclist); + as->ez = ez; as->userdata = ai->data; - as->numframes = ai->framecnt; - as->data = 0; - as->dataorder = order; as->datalen = dsize; - as->completed.context = ai->context; - as->desc.iso = NULL; - if (dsize > 0) { - if (!(as->data = (unsigned char *)__get_free_pages(GFP_KERNEL, order))) { - kfree(as); - return -ENOMEM; - } - if (!(ai->ep & 0x80)) { - if (copy_from_user(as->data, ai->data, dsize)) - goto err_fault; - as->datalen = 0; /* no need to copy back at completion */ - } - } - if ((i = usb_init_isoc(ez->usbdev, pipe, ai->framecnt, as, &as->desc.iso))) { - printk(KERN_DEBUG "ezusb: usb_init_isoc error %d\n", i); - goto err_inval; - } - as->desc.iso->start_type = START_ABSOLUTE; - as->desc.iso->start_frame = ai->startframe; - as->desc.iso->callback_frames = /*0*/ai->framecnt; - as->desc.iso->callback_fn = iso_completed; - as->desc.iso->data = as->data; - as->desc.iso->buf_size = dsize; - for (j = 0; j < ai->framecnt; j++) { - if (get_user(i, (int *)(cmd + j * sizeof(struct ezusb_isoframestat)))) { - usb_free_isoc(as->desc.iso); - kfree(as); + as->context = ai->context; + + as->urb.dev = ez->usbdev; + as->urb.pipe = pipe; + as->urb.transfer_flags = USB_ISO_ASAP; + as->urb.transfer_buffer = data; + as->urb.transfer_buffer_length = dsize; + as->urb.context = as; + as->urb.complete = (usb_complete_t)async_completed; + + for (i = totsize = 0; i < as->urb.number_of_packets; i++) { + as->urb.iso_frame_desc[i].offset = totsize; + if (get_user(j, (int *)(cmd + i * sizeof(struct ezusb_isoframestat)))) { + free_async(as); return -EFAULT; } - if (i < 0) - i = 0; - as->desc.iso->frames[j].frame_length = i; - } - async_newpending(as); - if ((i = usb_run_isoc(as->desc.iso, NULL))) { - printk(KERN_DEBUG "ezusb: usb_run_isoc error %d\n", i); - async_removelist(as); - goto err_inval; + as->urb.iso_frame_desc[i].length = j; + totsize += j; } - return 0; - - err_fault: - if (as) { - if (as->desc.iso) - usb_free_isoc(as->desc.iso); - if (as->data) - free_page((unsigned long)as->data); - kfree(as); + if (dsize > 0 && totsize > 0 && !(ai->ep & 0x80)) { + if (copy_from_user(data, ai->data, totsize)) { + free_async(as); + return -EFAULT; + } + as->userdata = NULL; /* no need to copy back at completion */ } - return -EFAULT; - - err_inval: - if (as) { - if (as->desc.iso) - usb_free_isoc(as->desc.iso); - if (as->data) - free_page((unsigned long)as->data); - kfree(as); + async_newpending(as); + if ((ret = usb_submit_urb(&as->urb))) { + printk(KERN_DEBUG "ezusb: iso: usb_submit_urb returned %d\n", ret); + async_removepending(as); + free_async(as); + return -EINVAL; /* return ret; */ } - return -EINVAL; + return 0; } static int ezusb_terminateasync(struct ezusb *ez, void *context) @@ -699,7 +680,7 @@ int ret = 0; while ((as = async_getpending(ez, context))) { - kill_async(as); + usb_unlink_urb(&as->urb); ret++; } return ret; @@ -707,26 +688,41 @@ static int ezusb_asynccompl(struct async *as, void *arg) { - if (as->datalen > 0) { - if (copy_to_user(as->userdata, as->data, as->datalen)) { - remove_async(as); + struct ezusb_asynccompleted *cpl; + unsigned int numframes, cplsize, i; + + if (as->userdata) { + if (copy_to_user(as->userdata, as->urb.transfer_buffer, as->datalen)) { + free_async(as); return -EFAULT; } } - if (copy_to_user(arg, &as->completed, - sizeof(struct ezusb_asynccompleted) + - as->numframes * sizeof(struct ezusb_isoframestat))) { - remove_async(as); + numframes = as->urb.number_of_packets; + cplsize = sizeof(struct ezusb_asynccompleted) + numframes * sizeof(struct ezusb_isoframestat); + if (!(cpl = kmalloc(cplsize, GFP_KERNEL))) { + free_async(as); + return -ENOMEM; + } + cpl->status = as->urb.status; + cpl->length = as->urb.actual_length; + cpl->context = as->context; + for (i = 0; i < numframes; i++) { + cpl->isostat[i].length = as->urb.iso_frame_desc[i].length; + cpl->isostat[i].status = as->urb.iso_frame_desc[i].status; + } + free_async(as); + if (copy_to_user(arg, cpl, cplsize)) { + kfree(cpl); return -EFAULT; } - remove_async(as); + kfree(cpl); return 0; } static int ezusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct ezusb *ez = (struct ezusb *)file->private_data; - DECLARE_WAITQUEUE(wait, current); + DECLARE_WAITQUEUE(wait, current); struct usb_proc_ctrltransfer pctrl; struct usb_proc_bulktransfer pbulk; struct usb_proc_old_ctrltransfer opctrl; @@ -739,6 +735,7 @@ struct ezusb_setinterface setintf; struct ezusb_asyncbulk abulk; struct ezusb_asynciso aiso; + struct ezusb_interrupt ezint; struct async *as; void *context; unsigned int ep, cfg; @@ -939,6 +936,14 @@ ret = put_user(i, (int *)arg); break; + case EZUSB_INTERRUPT: + printk("INT START\n"); + if (copy_from_user(&ezint, (void *)arg, sizeof(ezint))) { + ret = -EFAULT; + break; + } + ret=ezusb_interrupt(ez,&ezint); + break; default: ret = -ENOIOCTLCMD; break; @@ -970,27 +975,32 @@ static void * ezusb_probe(struct usb_device *usbdev, unsigned int ifnum) { struct ezusb *ez = &ezusb[0]; - struct usb_interface_descriptor *interface; - struct usb_endpoint_descriptor *endpoint; + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; #undef KERN_DEBUG #define KERN_DEBUG "" - printk(KERN_DEBUG "ezusb: probe: vendor id 0x%x, device id 0x%x\n", - usbdev->descriptor.idVendor, usbdev->descriptor.idProduct); + printk(KERN_DEBUG "ezusb: probe: vendor id 0x%x, device id 0x%x\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct); /* the 1234:5678 is just a self assigned test ID */ - if ((usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x2131) && - (usbdev->descriptor.idVendor != 0x1234 || usbdev->descriptor.idProduct != 0x5678)) - return NULL; - - /* We don't handle multiple configurations */ - if (usbdev->descriptor.bNumConfigurations != 1) - return NULL; + if ((usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x2131) + #if 1 + && + (usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x9999) && + (usbdev->descriptor.idVendor != 0x1234 || usbdev->descriptor.idProduct != 0x5678) + #endif + ) + return NULL; + + /* We don't handle multiple configurations */ + if (usbdev->descriptor.bNumConfigurations != 1) + return NULL; #if 0 - /* We don't handle multiple interfaces */ - if (usbdev->actconfig.bNumInterfaces != 1) - return NULL; + /* We don't handle multiple interfaces */ + if (usbdev->config[0].bNumInterfaces != 1) + return NULL; #endif down(&ez->mutex); @@ -1000,14 +1010,19 @@ return NULL; } ez->usbdev = usbdev; - interface = &usbdev->actconfig->interface[ifnum].altsetting[1]; - if (usb_set_interface(usbdev, ifnum, 1) < 0) { + if (usb_set_configuration(usbdev, usbdev->config[0].bConfigurationValue) < 0) { + printk(KERN_ERR "ezusb: set_configuration failed\n"); + goto err; + } + + interface = &usbdev->config[0].interface[0].altsetting[1]; + if (usb_set_interface(usbdev, 0, 1) < 0) { printk(KERN_ERR "ezusb: set_interface failed\n"); goto err; } up(&ez->mutex); MOD_INC_USE_COUNT; - return ez; + return ez; err: up(&ez->mutex); @@ -1017,7 +1032,7 @@ static void ezusb_disconnect(struct usb_device *usbdev, void *ptr) { - struct ezusb *ez = (struct ezusb *) ptr; + struct ezusb *ez = (struct ezusb *)ptr; down(&ez->mutex); destroy_all_async(ez); @@ -1028,12 +1043,12 @@ } static struct usb_driver ezusb_driver = { - "ezusb", - ezusb_probe, - ezusb_disconnect, - { NULL, NULL }, + "ezusb", + ezusb_probe, + ezusb_disconnect, + { NULL, NULL }, &ezusb_fops, - 32 + 192 }; /* --------------------------------------------------------------------- */ @@ -1051,10 +1066,9 @@ init_waitqueue_head(&ezusb[u].wait); spin_lock_init(&ezusb[u].lock); } - if (usb_register(&ezusb_driver) < 0) - return -1; - - printk(KERN_INFO "ezusb: Anchorchip firmware download driver registered\n"); + /* register misc device */ + usb_register(&ezusb_driver); + printk(KERN_INFO "ezusb: Anchorchip firmware download driver registered\n"); return 0; } @@ -1066,6 +1080,8 @@ /* --------------------------------------------------------------------- */ #ifdef MODULE + +int minor = 192; int init_module(void) { diff -u --recursive --new-file v2.3.33/linux/drivers/usb/ezusb.h linux/drivers/usb/ezusb.h --- v2.3.33/linux/drivers/usb/ezusb.h Sat Oct 9 11:47:50 1999 +++ linux/drivers/usb/ezusb.h Thu Dec 16 01:27:42 1999 @@ -66,6 +66,12 @@ void *data; }; +struct ezusb_interrupt { + unsigned int ep; + unsigned int len; + unsigned int interval; /* in milliseconds */ + void *data; +}; struct ezusb_setinterface { unsigned int interface; unsigned int altsetting; @@ -113,7 +119,8 @@ #define EZUSB_REQUESTBULK _IOR('E', 16, struct ezusb_asyncbulk) #define EZUSB_REQUESTISO _IOR('E', 17, struct ezusb_asynciso) #define EZUSB_TERMINATEASYNC _IOR('E', 18, void *) -#define EZUSB_GETFRAMENUMBER _IOW('E', 18, unsigned int) +#define EZUSB_GETFRAMENUMBER _IOW('E', 19, unsigned int) +#define EZUSB_INTERRUPT _IOWR('E', 20, struct ezusb_interrupt) /* --------------------------------------------------------------------- */ #endif /* _LINUX_EZUSB_H */ diff -u --recursive --new-file v2.3.33/linux/drivers/usb/firmware.h linux/drivers/usb/firmware.h --- v2.3.33/linux/drivers/usb/firmware.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/firmware.h Fri Dec 17 16:49:45 1999 @@ -0,0 +1,3213 @@ +//$Id: firmware.h,v 1.1 1999/12/17 08:55:05 fliegl Exp $ +static INTEL_HEX_RECORD firmware[] = { +{ 2, + 0x0, + 0, + {0x21,0x57} +}, +{ 3, + 0x3, + 0, + {0x02,0x01,0x66} +}, +{ 3, + 0xb, + 0, + {0x02,0x01,0x66} +}, +{ 3, + 0x13, + 0, + {0x02,0x01,0x66} +}, +{ 3, + 0x1b, + 0, + {0x02,0x01,0x66} +}, +{ 3, + 0x23, + 0, + {0x02,0x01,0x66} +}, +{ 3, + 0x2b, + 0, + {0x02,0x01,0x66} +}, +{ 3, + 0x33, + 0, + {0x02,0x03,0x0f} +}, +{ 3, + 0x3b, + 0, + {0x02,0x01,0x66} +}, +{ 3, + 0x43, + 0, + {0x02,0x01,0x00} +}, +{ 3, + 0x4b, + 0, + {0x02,0x01,0x66} +}, +{ 3, + 0x53, + 0, + {0x02,0x01,0x66} +}, +{ 3, + 0x5b, + 0, + {0x02,0x04,0xbd} +}, +{ 3, + 0x63, + 0, + {0x02,0x01,0x67} +}, +{ 3, + 0x100, + 0, + {0x02,0x0c,0x5a} +}, +{ 3, + 0x104, + 0, + {0x02,0x01,0xed} +}, +{ 3, + 0x108, + 0, + {0x02,0x02,0x51} +}, +{ 3, + 0x10c, + 0, + {0x02,0x02,0x7c} +}, +{ 3, + 0x110, + 0, + {0x02,0x02,0xe4} +}, +{ 1, + 0x114, + 0, + {0x32} +}, +{ 1, + 0x118, + 0, + {0x32} +}, +{ 3, + 0x11c, + 0, + {0x02,0x05,0xfd} +}, +{ 3, + 0x120, + 0, + {0x02,0x00,0x00} +}, +{ 3, + 0x124, + 0, + {0x02,0x00,0x00} +}, +{ 3, + 0x128, + 0, + {0x02,0x04,0x3c} +}, +{ 3, + 0x12c, + 0, + {0x02,0x04,0x6a} +}, +{ 3, + 0x130, + 0, + {0x02,0x00,0x00} +}, +{ 3, + 0x134, + 0, + {0x02,0x00,0x00} +}, +{ 3, + 0x138, + 0, + {0x02,0x00,0x00} +}, +{ 3, + 0x13c, + 0, + {0x02,0x00,0x00} +}, +{ 3, + 0x140, + 0, + {0x02,0x00,0x00} +}, +{ 3, + 0x144, + 0, + {0x02,0x00,0x00} +}, +{ 3, + 0x148, + 0, + {0x02,0x00,0x00} +}, +{ 3, + 0x14c, + 0, + {0x02,0x00,0x00} +}, +{ 3, + 0x150, + 0, + {0x02,0x00,0x00} +}, +{ 3, + 0x154, + 0, + {0x02,0x00,0x00} +}, +{ 10, + 0x157, + 0, + {0x75,0x81,0x7f,0xe5,0x82,0x60,0x03,0x02,0x01,0x61} +}, +{ 5, + 0x161, + 0, + {0x12,0x07,0x6f,0x21,0x64} +}, +{ 1, + 0x166, + 0, + {0x32} +}, +{ 14, + 0x167, + 0, + {0xc0,0xd0,0xc0,0x86,0xc0,0x82,0xc0,0x83,0xc0,0xe0,0x90,0x7f,0x97,0xe0} +}, +{ 14, + 0x175, + 0, + {0x44,0x80,0xf0,0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} +}, +{ 14, + 0x183, + 0, + {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} +}, +{ 14, + 0x191, + 0, + {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x90,0x7f,0x97,0xe0} +}, +{ 3, + 0x19f, + 0, + {0x55,0x7f,0xf0} +}, +{ 14, + 0x1a2, + 0, + {0x90,0x7f,0x9a,0xe0,0x30,0xe4,0x23,0x90,0x7f,0x68,0xf0,0xf0,0xf0,0xf0} +}, +{ 14, + 0x1b0, + 0, + {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} +}, +{ 14, + 0x1be, + 0, + {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} +}, +{ 14, + 0x1cc, + 0, + {0xe5,0xd8,0xc2,0xe3,0xf5,0xd8,0xd0,0xe0,0xd0,0x83,0xd0,0x82,0xd0,0x86} +}, +{ 3, + 0x1da, + 0, + {0xd0,0xd0,0x32} +}, +{ 8, + 0x1dd, + 0, + {0x75,0x86,0x00,0x90,0xff,0xc3,0x7c,0x05} +}, +{ 7, + 0x1e5, + 0, + {0xa3,0xe5,0x82,0x45,0x83,0x70,0xf9} +}, +{ 1, + 0x1ec, + 0, + {0x22} +}, +{ 14, + 0x1ed, + 0, + {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0xd0} +}, +{ 14, + 0x1fb, + 0, + {0x75,0xd0,0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91} +}, +{ 13, + 0x209, + 0, + {0x90,0x88,0x00,0xe0,0xf5,0x41,0x90,0x7f,0xab,0x74,0x02,0xf0,0x90} +}, +{ 9, + 0x216, + 0, + {0x7f,0xab,0x74,0x02,0xf0,0xe5,0x32,0x60,0x21} +}, +{ 4, + 0x21f, + 0, + {0x7a,0x00,0x7b,0x00} +}, +{ 11, + 0x223, + 0, + {0xc3,0xea,0x94,0x18,0xeb,0x64,0x80,0x94,0x80,0x50,0x12} +}, +{ 14, + 0x22e, + 0, + {0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x0a,0xba,0x00} +}, +{ 2, + 0x23c, + 0, + {0x01,0x0b} +}, +{ 2, + 0x23e, + 0, + {0x80,0xe3} +}, +{ 2, + 0x240, + 0, + {0xd0,0x86} +}, +{ 14, + 0x242, + 0, + {0xd0,0xd0,0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0} +}, +{ 1, + 0x250, + 0, + {0x32} +}, +{ 14, + 0x251, + 0, + {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} +}, +{ 14, + 0x25f, + 0, + {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} +}, +{ 4, + 0x26d, + 0, + {0x04,0xf0,0xd0,0x86} +}, +{ 11, + 0x271, + 0, + {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} +}, +{ 14, + 0x27c, + 0, + {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} +}, +{ 14, + 0x28a, + 0, + {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} +}, +{ 13, + 0x298, + 0, + {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} +}, +{ 12, + 0x2a5, + 0, + {0x7f,0xab,0x74,0x08,0xf0,0x75,0x6e,0x00,0x75,0x6f,0x02,0x12} +}, +{ 6, + 0x2b1, + 0, + {0x11,0x44,0x75,0x70,0x39,0x75} +}, +{ 6, + 0x2b7, + 0, + {0x71,0x0c,0x75,0x72,0x02,0x12} +}, +{ 12, + 0x2bd, + 0, + {0x11,0x75,0x90,0x7f,0xd6,0xe4,0xf0,0x75,0xd8,0x20,0xd0,0x86} +}, +{ 14, + 0x2c9, + 0, + {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} +}, +{ 13, + 0x2d7, + 0, + {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} +}, +{ 14, + 0x2e4, + 0, + {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} +}, +{ 14, + 0x2f2, + 0, + {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} +}, +{ 4, + 0x300, + 0, + {0x10,0xf0,0xd0,0x86} +}, +{ 11, + 0x304, + 0, + {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} +}, +{ 14, + 0x30f, + 0, + {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} +}, +{ 14, + 0x31d, + 0, + {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} +}, +{ 12, + 0x32b, + 0, + {0x00,0xc0,0x86,0x75,0x86,0x00,0x75,0x6e,0x00,0x75,0x6f,0x02} +}, +{ 7, + 0x337, + 0, + {0x12,0x11,0x44,0x75,0x70,0x40,0x75} +}, +{ 6, + 0x33e, + 0, + {0x71,0x0c,0x75,0x72,0x02,0x12} +}, +{ 14, + 0x344, + 0, + {0x11,0x75,0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0} +}, +{ 5, + 0x352, + 0, + {0x75,0xd8,0x10,0xd0,0x86} +}, +{ 14, + 0x357, + 0, + {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} +}, +{ 13, + 0x365, + 0, + {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} +}, +{ 13, + 0x372, + 0, + {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} +}, +{ 12, + 0x37f, + 0, + {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x42,0xf0,0x12,0x10,0x1b,0x90} +}, +{ 13, + 0x38b, + 0, + {0x7f,0xa6,0xe5,0x43,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40} +}, +{ 1, + 0x398, + 0, + {0xf0} +}, +{ 1, + 0x399, + 0, + {0x22} +}, +{ 13, + 0x39a, + 0, + {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} +}, +{ 12, + 0x3a7, + 0, + {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x44,0xf0,0x12,0x10,0x1b,0x90} +}, +{ 12, + 0x3b3, + 0, + {0x7f,0xa6,0xe5,0x45,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa6,0xe5} +}, +{ 11, + 0x3bf, + 0, + {0x46,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40,0xf0} +}, +{ 1, + 0x3ca, + 0, + {0x22} +}, +{ 10, + 0x3cb, + 0, + {0x75,0x44,0x02,0x75,0x45,0x00,0x75,0x46,0x00,0x12} +}, +{ 9, + 0x3d5, + 0, + {0x03,0x9a,0x75,0x42,0x03,0x75,0x43,0x00,0x12} +}, +{ 2, + 0x3de, + 0, + {0x03,0x72} +}, +{ 1, + 0x3e0, + 0, + {0x22} +}, +{ 12, + 0x3e1, + 0, + {0x90,0x88,0x00,0xe5,0x36,0xf0,0x90,0x88,0x00,0x74,0x10,0x25} +}, +{ 9, + 0x3ed, + 0, + {0x36,0xf0,0x12,0x01,0xdd,0x75,0x42,0x01,0x75} +}, +{ 9, + 0x3f6, + 0, + {0x43,0x18,0x12,0x03,0x72,0x75,0x44,0x02,0x75} +}, +{ 9, + 0x3ff, + 0, + {0x45,0x00,0x75,0x46,0x00,0x12,0x03,0x9a,0x75} +}, +{ 8, + 0x408, + 0, + {0x42,0x03,0x75,0x43,0x44,0x12,0x03,0x72} +}, +{ 1, + 0x410, + 0, + {0x22} +}, +{ 14, + 0x411, + 0, + {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} +}, +{ 14, + 0x41f, + 0, + {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} +}, +{ 4, + 0x42d, + 0, + {0x02,0xf0,0xd0,0x86} +}, +{ 11, + 0x431, + 0, + {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} +}, +{ 14, + 0x43c, + 0, + {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} +}, +{ 14, + 0x44a, + 0, + {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xa9,0x74} +}, +{ 7, + 0x458, + 0, + {0x04,0xf0,0x75,0x30,0x01,0xd0,0x86} +}, +{ 11, + 0x45f, + 0, + {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} +}, +{ 14, + 0x46a, + 0, + {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} +}, +{ 14, + 0x478, + 0, + {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} +}, +{ 7, + 0x486, + 0, + {0x04,0xf0,0x75,0x31,0x01,0xd0,0x86} +}, +{ 11, + 0x48d, + 0, + {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} +}, +{ 14, + 0x498, + 0, + {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} +}, +{ 12, + 0x4a6, + 0, + {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe5,0xf5,0x91,0xd0,0x86} +}, +{ 11, + 0x4b2, + 0, + {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} +}, +{ 14, + 0x4bd, + 0, + {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} +}, +{ 12, + 0x4cb, + 0, + {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe7,0xf5,0x91,0xd0,0x86} +}, +{ 11, + 0x4d7, + 0, + {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} +}, +{ 12, + 0x4e2, + 0, + {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x20,0x90,0x7f,0x96,0xe4,0xf0} +}, +{ 1, + 0x4ee, + 0, + {0x22} +}, +{ 7, + 0x4ef, + 0, + {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x21} +}, +{ 1, + 0x4f6, + 0, + {0x22} +}, +{ 14, + 0x4f7, + 0, + {0x90,0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xe0,0xfb,0x74,0x80,0x2a,0xfa} +}, +{ 14, + 0x505, + 0, + {0x74,0x80,0x2b,0xfb,0xea,0x03,0x03,0x54,0x3f,0xfc,0xea,0xc4,0x23,0x54} +}, +{ 14, + 0x513, + 0, + {0x1f,0xfa,0x2c,0xfa,0xeb,0x03,0x03,0x54,0x3f,0xfc,0xeb,0xc4,0x23,0x54} +}, +{ 11, + 0x521, + 0, + {0x1f,0xfb,0x2c,0xfb,0x90,0x17,0x0a,0xe0,0xfc,0x60,0x02} +}, +{ 2, + 0x52c, + 0, + {0x7a,0x00} +}, +{ 7, + 0x52e, + 0, + {0x90,0x17,0x0c,0xe0,0xfc,0x60,0x02} +}, +{ 2, + 0x535, + 0, + {0x7b,0x00} +}, +{ 11, + 0x537, + 0, + {0xea,0x2b,0xfc,0xc3,0x13,0xf5,0x3a,0x75,0x44,0x02,0x8b} +}, +{ 7, + 0x542, + 0, + {0x45,0x8a,0x46,0x12,0x03,0x9a,0x75} +}, +{ 9, + 0x549, + 0, + {0x6e,0x08,0x75,0x6f,0x00,0x12,0x11,0x44,0x75} +}, +{ 4, + 0x552, + 0, + {0x70,0x47,0x75,0x71} +}, +{ 8, + 0x556, + 0, + {0x0c,0x75,0x72,0x02,0x12,0x11,0x75,0x85} +}, +{ 5, + 0x55e, + 0, + {0x3a,0x73,0x12,0x11,0xa0} +}, +{ 1, + 0x563, + 0, + {0x22} +}, +{ 14, + 0x564, + 0, + {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} +}, +{ 14, + 0x572, + 0, + {0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xef,0xe0,0xfc} +}, +{ 14, + 0x580, + 0, + {0x33,0x95,0xe0,0xfd,0x8c,0x05,0x7c,0x00,0x90,0x7f,0xee,0xe0,0xfe,0x33} +}, +{ 14, + 0x58e, + 0, + {0x95,0xe0,0xff,0xec,0x2e,0xfc,0xed,0x3f,0xfd,0x90,0x7f,0xe9,0xe0,0xfe} +}, +{ 5, + 0x59c, + 0, + {0xbe,0x01,0x02,0x80,0x03} +}, +{ 3, + 0x5a1, + 0, + {0x02,0x05,0xf9} +}, +{ 6, + 0x5a4, + 0, + {0xbc,0x01,0x21,0xbd,0x00,0x1e} +}, +{ 14, + 0x5aa, + 0, + {0xea,0xc4,0x03,0x54,0xf8,0xfc,0xeb,0x25,0xe0,0xfd,0x2c,0x24,0x00,0xfc} +}, +{ 14, + 0x5b8, + 0, + {0xe4,0x34,0x17,0xfd,0x90,0x7e,0xc0,0xe0,0xfe,0x8c,0x82,0x8d,0x83,0xf0} +}, +{ 2, + 0x5c6, + 0, + {0x80,0x31} +}, +{ 14, + 0x5c8, + 0, + {0xea,0xc4,0x03,0x54,0xf8,0xfa,0xeb,0x25,0xe0,0xfb,0x2a,0xfa,0x24,0x00} +}, +{ 14, + 0x5d6, + 0, + {0xfb,0xe4,0x34,0x17,0xfc,0x90,0x7e,0xc0,0xe0,0xfd,0x8b,0x82,0x8c,0x83} +}, +{ 14, + 0x5e4, + 0, + {0xf0,0x74,0x01,0x2a,0x24,0x00,0xfa,0xe4,0x34,0x17,0xfb,0x90,0x7e,0xc1} +}, +{ 7, + 0x5f2, + 0, + {0xe0,0xfc,0x8a,0x82,0x8b,0x83,0xf0} +}, +{ 3, + 0x5f9, + 0, + {0x75,0x38,0x01} +}, +{ 1, + 0x5fc, + 0, + {0x22} +}, +{ 14, + 0x5fd, + 0, + {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} +}, +{ 14, + 0x60b, + 0, + {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} +}, +{ 13, + 0x619, + 0, + {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} +}, +{ 13, + 0x626, + 0, + {0x7f,0xaa,0x74,0x01,0xf0,0x12,0x05,0x64,0x75,0x37,0x00,0xd0,0x86} +}, +{ 14, + 0x633, + 0, + {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} +}, +{ 13, + 0x641, + 0, + {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} +}, +{ 14, + 0x64e, + 0, + {0x90,0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xee,0xe0} +}, +{ 14, + 0x65c, + 0, + {0xfc,0x33,0x95,0xe0,0xfd,0x90,0x7f,0x96,0xe0,0xfe,0x90,0x7f,0x96,0x74} +}, +{ 14, + 0x66a, + 0, + {0x80,0x65,0x06,0xf0,0x90,0x7f,0x00,0x74,0x01,0xf0,0xea,0xc4,0x03,0x54} +}, +{ 14, + 0x678, + 0, + {0xf8,0xfe,0xeb,0x25,0xe0,0xfb,0x2e,0xfe,0x24,0x00,0xfb,0xe4,0x34,0x17} +}, +{ 14, + 0x686, + 0, + {0xff,0x8b,0x82,0x8f,0x83,0xe0,0xfb,0x74,0x01,0x2e,0x24,0x00,0xfe,0xe4} +}, +{ 14, + 0x694, + 0, + {0x34,0x17,0xff,0x8e,0x82,0x8f,0x83,0xe0,0xfe,0x90,0x7f,0xe9,0xe0,0xff} +}, +{ 3, + 0x6a2, + 0, + {0xbf,0x81,0x0a} +}, +{ 10, + 0x6a5, + 0, + {0x90,0x7f,0x00,0xeb,0xf0,0x90,0x7f,0x01,0xee,0xf0} +}, +{ 8, + 0x6af, + 0, + {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x82,0x1a} +}, +{ 3, + 0x6b7, + 0, + {0xba,0x01,0x0c} +}, +{ 12, + 0x6ba, + 0, + {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} +}, +{ 11, + 0x6c6, + 0, + {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0xb5,0xf0} +}, +{ 8, + 0x6d1, + 0, + {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x83,0x1b} +}, +{ 3, + 0x6d9, + 0, + {0xba,0x01,0x0d} +}, +{ 13, + 0x6dc, + 0, + {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} +}, +{ 11, + 0x6e9, + 0, + {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0x12,0xf0} +}, +{ 8, + 0x6f4, + 0, + {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x84,0x1c} +}, +{ 3, + 0x6fc, + 0, + {0xba,0x01,0x0d} +}, +{ 13, + 0x6ff, + 0, + {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0c} +}, +{ 12, + 0x70c, + 0, + {0x90,0x7f,0x00,0x74,0x80,0xf0,0x90,0x7f,0x01,0x74,0x01,0xf0} +}, +{ 5, + 0x718, + 0, + {0x90,0x7f,0xb5,0xec,0xf0} +}, +{ 1, + 0x71d, + 0, + {0x22} +}, +{ 12, + 0x71e, + 0, + {0x75,0x36,0x0d,0x90,0x88,0x00,0x74,0x1d,0xf0,0x75,0x6b,0x80} +}, +{ 10, + 0x72a, + 0, + {0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} +}, +{ 9, + 0x734, + 0, + {0x6c,0x0f,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} +}, +{ 9, + 0x73d, + 0, + {0x6c,0x06,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} +}, +{ 7, + 0x746, + 0, + {0x6c,0x01,0x12,0x10,0xe2,0x7a,0x00} +}, +{ 3, + 0x74d, + 0, + {0xba,0xff,0x00} +}, +{ 2, + 0x750, + 0, + {0x50,0x0a} +}, +{ 10, + 0x752, + 0, + {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xf1} +}, +{ 10, + 0x75c, + 0, + {0x75,0x6b,0x80,0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75} +}, +{ 8, + 0x766, + 0, + {0x6b,0x80,0x75,0x6c,0x0f,0x12,0x10,0xe2} +}, +{ 1, + 0x76e, + 0, + {0x22} +}, +{ 14, + 0x76f, + 0, + {0x90,0x7f,0xa1,0xe4,0xf0,0x90,0x7f,0xaf,0x74,0x01,0xf0,0x90,0x7f,0x92} +}, +{ 14, + 0x77d, + 0, + {0x74,0x02,0xf0,0x75,0x8e,0x31,0x75,0x89,0x21,0x75,0x88,0x00,0x75,0xc8} +}, +{ 14, + 0x78b, + 0, + {0x00,0x75,0x8d,0x40,0x75,0x98,0x40,0x75,0xc0,0x40,0x75,0x87,0x00,0x75} +}, +{ 9, + 0x799, + 0, + {0x20,0x00,0x75,0x21,0x00,0x75,0x22,0x00,0x75} +}, +{ 5, + 0x7a2, + 0, + {0x23,0x00,0x75,0x47,0x00} +}, +{ 7, + 0x7a7, + 0, + {0xc3,0xe5,0x47,0x94,0x20,0x50,0x11} +}, +{ 13, + 0x7ae, + 0, + {0xe5,0x47,0x24,0x00,0xf5,0x82,0xe4,0x34,0x17,0xf5,0x83,0xe4,0xf0} +}, +{ 4, + 0x7bb, + 0, + {0x05,0x47,0x80,0xe8} +}, +{ 9, + 0x7bf, + 0, + {0xe4,0xf5,0x40,0xf5,0x3f,0xe4,0xf5,0x3c,0xf5} +}, +{ 7, + 0x7c8, + 0, + {0x3b,0xe4,0xf5,0x3e,0xf5,0x3d,0x75} +}, +{ 11, + 0x7cf, + 0, + {0x32,0x00,0x75,0x37,0x00,0x75,0x39,0x00,0x90,0x7f,0x93} +}, +{ 14, + 0x7da, + 0, + {0x74,0x3c,0xf0,0x90,0x7f,0x9c,0x74,0xff,0xf0,0x90,0x7f,0x96,0x74,0x80} +}, +{ 14, + 0x7e8, + 0, + {0xf0,0x90,0x7f,0x94,0x74,0x70,0xf0,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} +}, +{ 14, + 0x7f6, + 0, + {0x7f,0x97,0xe4,0xf0,0x90,0x7f,0x95,0x74,0xc2,0xf0,0x90,0x7f,0x98,0x74} +}, +{ 14, + 0x804, + 0, + {0x28,0xf0,0x90,0x7f,0x9e,0x74,0x28,0xf0,0x90,0x7f,0xf0,0xe4,0xf0,0x90} +}, +{ 14, + 0x812, + 0, + {0x7f,0xf1,0xe4,0xf0,0x90,0x7f,0xf2,0xe4,0xf0,0x90,0x7f,0xf3,0xe4,0xf0} +}, +{ 14, + 0x820, + 0, + {0x90,0x7f,0xf4,0xe4,0xf0,0x90,0x7f,0xf5,0xe4,0xf0,0x90,0x7f,0xf6,0xe4} +}, +{ 14, + 0x82e, + 0, + {0xf0,0x90,0x7f,0xf7,0xe4,0xf0,0x90,0x7f,0xf8,0xe4,0xf0,0x90,0x7f,0xf9} +}, +{ 14, + 0x83c, + 0, + {0x74,0x38,0xf0,0x90,0x7f,0xfa,0x74,0xa0,0xf0,0x90,0x7f,0xfb,0x74,0xa0} +}, +{ 14, + 0x84a, + 0, + {0xf0,0x90,0x7f,0xfc,0x74,0xa0,0xf0,0x90,0x7f,0xfd,0x74,0xa0,0xf0,0x90} +}, +{ 14, + 0x858, + 0, + {0x7f,0xfe,0x74,0xa0,0xf0,0x90,0x7f,0xff,0x74,0xa0,0xf0,0x90,0x7f,0xe0} +}, +{ 14, + 0x866, + 0, + {0x74,0x03,0xf0,0x90,0x7f,0xe1,0x74,0x01,0xf0,0x90,0x7f,0xdd,0x74,0x80} +}, +{ 11, + 0x874, + 0, + {0xf0,0x12,0x12,0x43,0x12,0x07,0x1e,0x7a,0x00,0x7b,0x00} +}, +{ 9, + 0x87f, + 0, + {0xc3,0xea,0x94,0x1e,0xeb,0x94,0x00,0x50,0x17} +}, +{ 12, + 0x888, + 0, + {0x90,0x88,0x00,0xe0,0xf5,0x47,0x90,0x88,0x0b,0xe0,0xf5,0x47} +}, +{ 9, + 0x894, + 0, + {0x90,0x7f,0x68,0xf0,0x0a,0xba,0x00,0x01,0x0b} +}, +{ 2, + 0x89d, + 0, + {0x80,0xe0} +}, +{ 12, + 0x89f, + 0, + {0x12,0x03,0xe1,0x90,0x7f,0xd6,0xe4,0xf0,0x7a,0x00,0x7b,0x00} +}, +{ 13, + 0x8ab, + 0, + {0x8a,0x04,0x8b,0x05,0xc3,0xea,0x94,0xe0,0xeb,0x94,0x2e,0x50,0x1a} +}, +{ 14, + 0x8b8, + 0, + {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} +}, +{ 10, + 0x8c6, + 0, + {0x04,0xd0,0x03,0xd0,0x02,0x0a,0xba,0x00,0x01,0x0b} +}, +{ 2, + 0x8d0, + 0, + {0x80,0xd9} +}, +{ 13, + 0x8d2, + 0, + {0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0,0x90} +}, +{ 14, + 0x8df, + 0, + {0x7f,0xde,0x74,0x05,0xf0,0x90,0x7f,0xdf,0x74,0x05,0xf0,0x90,0x7f,0xac} +}, +{ 14, + 0x8ed, + 0, + {0xe4,0xf0,0x90,0x7f,0xad,0x74,0x05,0xf0,0x75,0xa8,0x80,0x75,0xf8,0x10} +}, +{ 13, + 0x8fb, + 0, + {0x90,0x7f,0xae,0x74,0x0b,0xf0,0x90,0x7f,0xe2,0x74,0x88,0xf0,0x90} +}, +{ 12, + 0x908, + 0, + {0x7f,0xab,0x74,0x08,0xf0,0x75,0xe8,0x11,0x75,0x32,0x01,0x75} +}, +{ 12, + 0x914, + 0, + {0x31,0x00,0x75,0x30,0x00,0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7} +}, +{ 10, + 0x920, + 0, + {0xd0,0x05,0xd0,0x04,0x75,0x34,0x00,0x75,0x35,0x01} +}, +{ 13, + 0x92a, + 0, + {0x90,0x7f,0xae,0x74,0x03,0xf0,0x8c,0x02,0xba,0x00,0x02,0x80,0x03} +}, +{ 3, + 0x937, + 0, + {0x02,0x0a,0x3f} +}, +{ 12, + 0x93a, + 0, + {0x85,0x33,0x34,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90,0x7f,0x97} +}, +{ 14, + 0x946, + 0, + {0x74,0x08,0xf0,0x90,0x7f,0x9d,0x74,0x88,0xf0,0x90,0x7f,0x9a,0xe0,0xfa} +}, +{ 12, + 0x954, + 0, + {0x74,0x05,0x5a,0xf5,0x33,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} +}, +{ 13, + 0x960, + 0, + {0x7f,0x97,0x74,0x02,0xf0,0x90,0x7f,0x9d,0x74,0x82,0xf0,0xe5,0x33} +}, +{ 13, + 0x96d, + 0, + {0x25,0xe0,0xfa,0x90,0x7f,0x9a,0xe0,0x54,0x05,0xfb,0x4a,0xf5,0x33} +}, +{ 2, + 0x97a, + 0, + {0x60,0x0c} +}, +{ 12, + 0x97c, + 0, + {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x4a,0xf0} +}, +{ 11, + 0x988, + 0, + {0x75,0x6e,0x00,0x75,0x6f,0x00,0xc0,0x04,0xc0,0x05,0x12} +}, +{ 14, + 0x993, + 0, + {0x11,0x44,0xd0,0x05,0xd0,0x04,0x90,0x17,0x13,0xe0,0xfa,0x74,0x80,0x2a} +}, +{ 6, + 0x9a1, + 0, + {0xfa,0xe5,0x33,0xb4,0x04,0x29} +}, +{ 3, + 0x9a7, + 0, + {0xba,0xa0,0x00} +}, +{ 2, + 0x9aa, + 0, + {0x50,0x24} +}, +{ 13, + 0x9ac, + 0, + {0x90,0x17,0x13,0xe0,0x04,0xfb,0x0b,0x90,0x17,0x13,0xeb,0xf0,0x90} +}, +{ 14, + 0x9b9, + 0, + {0x17,0x13,0xe0,0xfb,0x90,0x17,0x15,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05} +}, +{ 9, + 0x9c7, + 0, + {0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02} +}, +{ 5, + 0x9d0, + 0, + {0xe5,0x33,0xb4,0x02,0x26} +}, +{ 6, + 0x9d5, + 0, + {0xc3,0x74,0x04,0x9a,0x50,0x20} +}, +{ 13, + 0x9db, + 0, + {0x90,0x17,0x13,0xe0,0xfa,0x1a,0x1a,0x90,0x17,0x13,0xea,0xf0,0x90} +}, +{ 13, + 0x9e8, + 0, + {0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xf0,0xc0,0x04,0xc0,0x05,0x12} +}, +{ 6, + 0x9f5, + 0, + {0x04,0xf7,0xd0,0x05,0xd0,0x04} +}, +{ 5, + 0x9fb, + 0, + {0xe5,0x33,0xb4,0x08,0x1d} +}, +{ 4, + 0xa00, + 0, + {0xe5,0x34,0x70,0x19} +}, +{ 10, + 0xa04, + 0, + {0x74,0x01,0x25,0x35,0x54,0x0f,0xf5,0x35,0x85,0x35} +}, +{ 12, + 0xa0e, + 0, + {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} +}, +{ 3, + 0xa1a, + 0, + {0x05,0xd0,0x04} +}, +{ 5, + 0xa1d, + 0, + {0xe5,0x33,0xb4,0x01,0x1d} +}, +{ 4, + 0xa22, + 0, + {0xe5,0x34,0x70,0x19} +}, +{ 10, + 0xa26, + 0, + {0xe5,0x35,0x24,0xff,0x54,0x0f,0xf5,0x35,0x85,0x35} +}, +{ 12, + 0xa30, + 0, + {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} +}, +{ 3, + 0xa3c, + 0, + {0x05,0xd0,0x04} +}, +{ 14, + 0xa3f, + 0, + {0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0,0x04,0x90,0x7f,0x96} +}, +{ 14, + 0xa4d, + 0, + {0xe0,0xfa,0x90,0x7f,0x96,0x74,0x7f,0x5a,0xf0,0x90,0x7f,0x97,0x74,0x08} +}, +{ 10, + 0xa5b, + 0, + {0xf0,0xc3,0xec,0x94,0x00,0xed,0x94,0x02,0x40,0x08} +}, +{ 8, + 0xa65, + 0, + {0x90,0x7f,0x96,0xe0,0xfa,0x20,0xe6,0x08} +}, +{ 8, + 0xa6d, + 0, + {0xc3,0xe4,0x9c,0x74,0x08,0x9d,0x50,0x13} +}, +{ 14, + 0xa75, + 0, + {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x40,0x65,0x02,0xf0,0x7c} +}, +{ 5, + 0xa83, + 0, + {0x00,0x7d,0x00,0x80,0x05} +}, +{ 5, + 0xa88, + 0, + {0x0c,0xbc,0x00,0x01,0x0d} +}, +{ 5, + 0xa8d, + 0, + {0xe5,0x38,0xb4,0x01,0x0e} +}, +{ 13, + 0xa92, + 0, + {0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0x75,0x38} +}, +{ 1, + 0xa9f, + 0, + {0x00} +}, +{ 7, + 0xaa0, + 0, + {0xe5,0x31,0x70,0x03,0x02,0x09,0x2a} +}, +{ 10, + 0xaa7, + 0, + {0x90,0x7f,0xc9,0xe0,0xfa,0x70,0x03,0x02,0x0c,0x2d} +}, +{ 14, + 0xab1, + 0, + {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} +}, +{ 9, + 0xabf, + 0, + {0x7d,0xc0,0xe0,0xfa,0xba,0x2c,0x02,0x80,0x03} +}, +{ 3, + 0xac8, + 0, + {0x02,0x0b,0x36} +}, +{ 5, + 0xacb, + 0, + {0x75,0x32,0x00,0x7b,0x00} +}, +{ 3, + 0xad0, + 0, + {0xbb,0x64,0x00} +}, +{ 2, + 0xad3, + 0, + {0x50,0x1c} +}, +{ 14, + 0xad5, + 0, + {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} +}, +{ 13, + 0xae3, + 0, + {0x04,0xd0,0x03,0xd0,0x02,0x90,0x88,0x0f,0xe0,0xf5,0x47,0x0b,0x80} +}, +{ 1, + 0xaf0, + 0, + {0xdf} +}, +{ 13, + 0xaf1, + 0, + {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x07,0x1e,0x12,0x03,0xe1,0x12} +}, +{ 12, + 0xafe, + 0, + {0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x75,0x6e,0x00,0x75} +}, +{ 13, + 0xb0a, + 0, + {0x6f,0x01,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0x44,0xd0,0x05} +}, +{ 9, + 0xb17, + 0, + {0xd0,0x04,0xd0,0x02,0x75,0x70,0x4d,0x75,0x71} +}, +{ 11, + 0xb20, + 0, + {0x0c,0x75,0x72,0x02,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} +}, +{ 11, + 0xb2b, + 0, + {0x11,0x75,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x02,0x0c,0x2d} +}, +{ 3, + 0xb36, + 0, + {0xba,0x2a,0x3b} +}, +{ 13, + 0xb39, + 0, + {0x90,0x7f,0x98,0x74,0x20,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} +}, +{ 14, + 0xb46, + 0, + {0x01,0xdd,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x90,0x7f,0x98,0x74,0x28,0xf0} +}, +{ 2, + 0xb54, + 0, + {0x7b,0x00} +}, +{ 3, + 0xb56, + 0, + {0xbb,0x0a,0x00} +}, +{ 5, + 0xb59, + 0, + {0x40,0x03,0x02,0x0c,0x2d} +}, +{ 14, + 0xb5e, + 0, + {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} +}, +{ 8, + 0xb6c, + 0, + {0x04,0xd0,0x03,0xd0,0x02,0x0b,0x80,0xe2} +}, +{ 3, + 0xb74, + 0, + {0xba,0x2b,0x1a} +}, +{ 8, + 0xb77, + 0, + {0x90,0x7f,0xc9,0xe0,0xfb,0xbb,0x40,0x12} +}, +{ 14, + 0xb7f, + 0, + {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x12,0x05,0xd0,0x05,0xd0,0x04,0xd0} +}, +{ 4, + 0xb8d, + 0, + {0x02,0x02,0x0c,0x2d} +}, +{ 3, + 0xb91, + 0, + {0xba,0x10,0x1f} +}, +{ 14, + 0xb94, + 0, + {0x90,0x7f,0x96,0xe0,0xfb,0x90,0x7f,0x96,0x74,0x80,0x65,0x03,0xf0,0xc0} +}, +{ 14, + 0xba2, + 0, + {0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x3d,0xd0,0x05,0xd0,0x04,0xd0,0x02} +}, +{ 3, + 0xbb0, + 0, + {0x02,0x0c,0x2d} +}, +{ 3, + 0xbb3, + 0, + {0xba,0x11,0x12} +}, +{ 14, + 0xbb6, + 0, + {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x6a,0xd0,0x05,0xd0,0x04,0xd0} +}, +{ 4, + 0xbc4, + 0, + {0x02,0x02,0x0c,0x2d} +}, +{ 3, + 0xbc8, + 0, + {0xba,0x12,0x12} +}, +{ 14, + 0xbcb, + 0, + {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x8f,0xd0,0x05,0xd0,0x04,0xd0} +}, +{ 4, + 0xbd9, + 0, + {0x02,0x02,0x0c,0x2d} +}, +{ 3, + 0xbdd, + 0, + {0xba,0x13,0x0b} +}, +{ 11, + 0xbe0, + 0, + {0x90,0x7d,0xc1,0xe0,0xfb,0x90,0x88,0x00,0xf0,0x80,0x42} +}, +{ 3, + 0xbeb, + 0, + {0xba,0x14,0x11} +}, +{ 14, + 0xbee, + 0, + {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0xdd,0xd0,0x05,0xd0,0x04,0xd0} +}, +{ 3, + 0xbfc, + 0, + {0x02,0x80,0x2e} +}, +{ 3, + 0xbff, + 0, + {0xba,0x15,0x1d} +}, +{ 12, + 0xc02, + 0, + {0x90,0x7d,0xc1,0xe0,0xf5,0x75,0x90,0x7d,0xc2,0xe0,0xf5,0x76} +}, +{ 14, + 0xc0e, + 0, + {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0,0x05,0xd0,0x04,0xd0} +}, +{ 3, + 0xc1c, + 0, + {0x02,0x80,0x0e} +}, +{ 3, + 0xc1f, + 0, + {0xba,0x16,0x0b} +}, +{ 11, + 0xc22, + 0, + {0xc0,0x04,0xc0,0x05,0x12,0x13,0xa3,0xd0,0x05,0xd0,0x04} +}, +{ 11, + 0xc2d, + 0, + {0x90,0x7f,0xc9,0xe4,0xf0,0x75,0x31,0x00,0x02,0x09,0x2a} +}, +{ 1, + 0xc38, + 0, + {0x22} +}, +{ 7, + 0xc39, + 0, + {0x53,0x55,0x50,0x45,0x4e,0x44,0x00} +}, +{ 7, + 0xc40, + 0, + {0x52,0x45,0x53,0x55,0x4d,0x45,0x00} +}, +{ 6, + 0xc47, + 0, + {0x20,0x56,0x6f,0x6c,0x20,0x00} +}, +{ 13, + 0xc4d, + 0, + {0x44,0x41,0x42,0x55,0x53,0x42,0x20,0x76,0x31,0x2e,0x30,0x30,0x00} +}, +{ 14, + 0xc5a, + 0, + {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} +}, +{ 14, + 0xc68, + 0, + {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} +}, +{ 13, + 0xc76, + 0, + {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} +}, +{ 14, + 0xc83, + 0, + {0x7f,0xab,0x74,0x01,0xf0,0x90,0x7f,0xe8,0xe0,0xfa,0x90,0x7f,0xe9,0xe0} +}, +{ 6, + 0xc91, + 0, + {0xfb,0xbb,0x00,0x02,0x80,0x03} +}, +{ 3, + 0xc97, + 0, + {0x02,0x0d,0x38} +}, +{ 3, + 0xc9a, + 0, + {0xba,0x80,0x14} +}, +{ 14, + 0xc9d, + 0, + {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5} +}, +{ 6, + 0xcab, + 0, + {0x74,0x02,0xf0,0x02,0x0e,0xcd} +}, +{ 5, + 0xcb1, + 0, + {0xba,0x82,0x02,0x80,0x03} +}, +{ 3, + 0xcb6, + 0, + {0x02,0x0d,0x1d} +}, +{ 8, + 0xcb9, + 0, + {0x90,0x7f,0xec,0xe0,0xfc,0xbc,0x01,0x00} +}, +{ 2, + 0xcc1, + 0, + {0x40,0x21} +}, +{ 6, + 0xcc3, + 0, + {0xc3,0x74,0x07,0x9c,0x40,0x1b} +}, +{ 14, + 0xcc9, + 0, + {0xec,0x24,0xff,0x25,0xe0,0xfd,0x24,0xc6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} +}, +{ 13, + 0xcd7, + 0, + {0x83,0xe0,0xfd,0x53,0x05,0x01,0x90,0x7f,0x00,0xed,0xf0,0x80,0x2b} +}, +{ 3, + 0xce4, + 0, + {0xbc,0x81,0x00} +}, +{ 2, + 0xce7, + 0, + {0x40,0x21} +}, +{ 6, + 0xce9, + 0, + {0xc3,0x74,0x87,0x9c,0x40,0x1b} +}, +{ 14, + 0xcef, + 0, + {0xec,0x24,0x7f,0x25,0xe0,0xfc,0x24,0xb6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} +}, +{ 13, + 0xcfd, + 0, + {0x83,0xe0,0xfc,0x53,0x04,0x01,0x90,0x7f,0x00,0xec,0xf0,0x80,0x05} +}, +{ 5, + 0xd0a, + 0, + {0x90,0x7f,0x00,0xe4,0xf0} +}, +{ 14, + 0xd0f, + 0, + {0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74,0x02,0xf0,0x02,0x0e,0xcd} +}, +{ 5, + 0xd1d, + 0, + {0xba,0x81,0x02,0x80,0x03} +}, +{ 3, + 0xd22, + 0, + {0x02,0x0e,0xc5} +}, +{ 14, + 0xd25, + 0, + {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74} +}, +{ 5, + 0xd33, + 0, + {0x02,0xf0,0x02,0x0e,0xcd} +}, +{ 3, + 0xd38, + 0, + {0xbb,0x01,0x2d} +}, +{ 6, + 0xd3b, + 0, + {0xba,0x00,0x03,0x02,0x0e,0xcd} +}, +{ 3, + 0xd41, + 0, + {0xba,0x02,0x11} +}, +{ 13, + 0xd44, + 0, + {0x75,0x59,0x00,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} +}, +{ 4, + 0xd51, + 0, + {0x02,0x02,0x0e,0xcd} +}, +{ 5, + 0xd55, + 0, + {0xba,0x21,0x02,0x80,0x03} +}, +{ 3, + 0xd5a, + 0, + {0x02,0x0e,0xcd} +}, +{ 11, + 0xd5d, + 0, + {0x75,0x37,0x01,0x90,0x7f,0xc5,0xe4,0xf0,0x02,0x0e,0xcd} +}, +{ 3, + 0xd68, + 0, + {0xbb,0x03,0x1f} +}, +{ 6, + 0xd6b, + 0, + {0xba,0x00,0x03,0x02,0x0e,0xcd} +}, +{ 5, + 0xd71, + 0, + {0xba,0x02,0x02,0x80,0x03} +}, +{ 3, + 0xd76, + 0, + {0x02,0x0e,0xcd} +}, +{ 13, + 0xd79, + 0, + {0x75,0x59,0x01,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} +}, +{ 4, + 0xd86, + 0, + {0x02,0x02,0x0e,0xcd} +}, +{ 3, + 0xd8a, + 0, + {0xbb,0x06,0x54} +}, +{ 5, + 0xd8d, + 0, + {0xba,0x80,0x02,0x80,0x03} +}, +{ 3, + 0xd92, + 0, + {0x02,0x0e,0xc5} +}, +{ 8, + 0xd95, + 0, + {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x01,0x15} +}, +{ 12, + 0xd9d, + 0, + {0x7c,0xfb,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} +}, +{ 9, + 0xda9, + 0, + {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} +}, +{ 10, + 0xdb2, + 0, + {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x02,0x02,0x80,0x03} +}, +{ 3, + 0xdbc, + 0, + {0x02,0x0e,0xc5} +}, +{ 10, + 0xdbf, + 0, + {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x02,0x80,0x03} +}, +{ 3, + 0xdc9, + 0, + {0x02,0x0e,0xc5} +}, +{ 12, + 0xdcc, + 0, + {0x7c,0x3b,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} +}, +{ 9, + 0xdd8, + 0, + {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} +}, +{ 6, + 0xde1, + 0, + {0xbb,0x07,0x03,0x02,0x0e,0xc5} +}, +{ 3, + 0xde7, + 0, + {0xbb,0x08,0x10} +}, +{ 13, + 0xdea, + 0, + {0xac,0x48,0x90,0x7f,0x00,0xec,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} +}, +{ 3, + 0xdf7, + 0, + {0x02,0x0e,0xcd} +}, +{ 3, + 0xdfa, + 0, + {0xbb,0x09,0x31} +}, +{ 5, + 0xdfd, + 0, + {0xba,0x00,0x02,0x80,0x03} +}, +{ 3, + 0xe02, + 0, + {0x02,0x0e,0xc5} +}, +{ 14, + 0xe05, + 0, + {0x90,0x7f,0xea,0xe0,0xfc,0xc3,0x74,0x01,0x9c,0x50,0x03,0x02,0x0e,0xc5} +}, +{ 8, + 0xe13, + 0, + {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x0a} +}, +{ 10, + 0xe1b, + 0, + {0x90,0x17,0x21,0xe4,0xf0,0x90,0x17,0x22,0xe4,0xf0} +}, +{ 9, + 0xe25, + 0, + {0x90,0x7f,0xea,0xe0,0xf5,0x48,0x02,0x0e,0xcd} +}, +{ 3, + 0xe2e, + 0, + {0xbb,0x0a,0x27} +}, +{ 5, + 0xe31, + 0, + {0xba,0x81,0x02,0x80,0x03} +}, +{ 3, + 0xe36, + 0, + {0x02,0x0e,0xc5} +}, +{ 14, + 0xe39, + 0, + {0x90,0x7f,0xec,0xe0,0xfa,0x24,0x20,0xfa,0xe4,0x34,0x17,0xfc,0x8a,0x82} +}, +{ 14, + 0xe47, + 0, + {0x8c,0x83,0xe0,0xfa,0x90,0x7f,0x00,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} +}, +{ 3, + 0xe55, + 0, + {0x02,0x0e,0xcd} +}, +{ 5, + 0xe58, + 0, + {0xbb,0x0b,0x02,0x80,0x03} +}, +{ 3, + 0xe5d, + 0, + {0x02,0x0e,0xa9} +}, +{ 13, + 0xe60, + 0, + {0x90,0x17,0x20,0xe4,0xf0,0x90,0x7f,0xec,0xe0,0xfa,0xba,0x01,0x1a} +}, +{ 8, + 0xe6d, + 0, + {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x12} +}, +{ 14, + 0xe75, + 0, + {0x90,0x7f,0xea,0xe0,0xfa,0x90,0x17,0x21,0xf0,0xc0,0x03,0x12,0x04,0xe2} +}, +{ 4, + 0xe83, + 0, + {0xd0,0x03,0x80,0x46} +}, +{ 8, + 0xe87, + 0, + {0x90,0x7f,0xec,0xe0,0xfa,0xba,0x02,0x3e} +}, +{ 8, + 0xe8f, + 0, + {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x36} +}, +{ 13, + 0xe97, + 0, + {0xc0,0x03,0x12,0x04,0xef,0xd0,0x03,0x90,0x7f,0xea,0xe0,0xfa,0x90} +}, +{ 5, + 0xea4, + 0, + {0x17,0x22,0xf0,0x80,0x24} +}, +{ 5, + 0xea9, + 0, + {0xbb,0x12,0x02,0x80,0x17} +}, +{ 5, + 0xeae, + 0, + {0xbb,0x81,0x02,0x80,0x0d} +}, +{ 5, + 0xeb3, + 0, + {0xbb,0x83,0x02,0x80,0x08} +}, +{ 5, + 0xeb8, + 0, + {0xbb,0x82,0x02,0x80,0x03} +}, +{ 3, + 0xebd, + 0, + {0xbb,0x84,0x05} +}, +{ 5, + 0xec0, + 0, + {0x12,0x06,0x4e,0x80,0x08} +}, +{ 8, + 0xec5, + 0, + {0x90,0x7f,0xb4,0x74,0x03,0xf0,0x80,0x06} +}, +{ 6, + 0xecd, + 0, + {0x90,0x7f,0xb4,0x74,0x02,0xf0} +}, +{ 2, + 0xed3, + 0, + {0xd0,0x86} +}, +{ 14, + 0xed5, + 0, + {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} +}, +{ 13, + 0xee3, + 0, + {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} +}, +{ 11, + 0xef0, + 0, + {0x90,0x7f,0xec,0xe0,0xf5,0x5a,0xc3,0x94,0x01,0x40,0x1d} +}, +{ 7, + 0xefb, + 0, + {0xc3,0x74,0x07,0x95,0x5a,0x40,0x16} +}, +{ 13, + 0xf02, + 0, + {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xc6,0xf5,0x82,0xe4,0x34} +}, +{ 9, + 0xf0f, + 0, + {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0,0x80,0x22} +}, +{ 7, + 0xf18, + 0, + {0xc3,0xe5,0x5a,0x94,0x81,0x40,0x1b} +}, +{ 7, + 0xf1f, + 0, + {0xc3,0x74,0x87,0x95,0x5a,0x40,0x14} +}, +{ 13, + 0xf26, + 0, + {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xb6,0xf5,0x82,0xe4,0x34} +}, +{ 7, + 0xf33, + 0, + {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0} +}, +{ 1, + 0xf3a, + 0, + {0x22} +}, +{ 14, + 0xf3b, + 0, + {0x09,0x02,0xba,0x00,0x03,0x01,0x00,0x40,0x00,0x09,0x04,0x00,0x00,0x00} +}, +{ 14, + 0xf49, + 0, + {0x01,0x01,0x00,0x00,0x09,0x24,0x01,0x00,0x01,0x3d,0x00,0x01,0x01,0x0c} +}, +{ 14, + 0xf57, + 0, + {0x24,0x02,0x01,0x10,0x07,0x00,0x02,0x03,0x00,0x00,0x00,0x0d,0x24,0x06} +}, +{ 14, + 0xf65, + 0, + {0x03,0x01,0x02,0x15,0x00,0x03,0x00,0x03,0x00,0x00,0x09,0x24,0x03,0x02} +}, +{ 14, + 0xf73, + 0, + {0x01,0x01,0x00,0x01,0x00,0x09,0x24,0x03,0x04,0x02,0x03,0x00,0x03,0x00} +}, +{ 14, + 0xf81, + 0, + {0x09,0x24,0x03,0x05,0x03,0x06,0x00,0x01,0x00,0x09,0x04,0x01,0x00,0x00} +}, +{ 14, + 0xf8f, + 0, + {0x01,0x02,0x00,0x00,0x09,0x04,0x01,0x01,0x01,0x01,0x02,0x00,0x00,0x07} +}, +{ 14, + 0xf9d, + 0, + {0x24,0x01,0x02,0x01,0x01,0x00,0x0b,0x24,0x02,0x01,0x02,0x02,0x10,0x01} +}, +{ 14, + 0xfab, + 0, + {0x80,0xbb,0x00,0x09,0x05,0x88,0x05,0x00,0x01,0x01,0x00,0x00,0x07,0x25} +}, +{ 14, + 0xfb9, + 0, + {0x01,0x00,0x00,0x00,0x00,0x09,0x04,0x02,0x00,0x02,0x00,0x00,0x00,0x00} +}, +{ 14, + 0xfc7, + 0, + {0x07,0x05,0x82,0x02,0x40,0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00} +}, +{ 14, + 0xfd5, + 0, + {0x09,0x04,0x02,0x01,0x03,0x00,0x00,0x00,0x00,0x07,0x05,0x82,0x02,0x40} +}, +{ 14, + 0xfe3, + 0, + {0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00,0x09,0x05,0x89,0x05,0xa0} +}, +{ 10, + 0xff1, + 0, + {0x01,0x01,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00} +}, +{ 14, + 0xffb, + 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, + 0x0, + 1, + {0} +} +}; diff -u --recursive --new-file v2.3.33/linux/drivers/usb/hub.c linux/drivers/usb/hub.c --- v2.3.33/linux/drivers/usb/hub.c Wed Dec 8 14:11:27 1999 +++ linux/drivers/usb/hub.c Fri Dec 17 16:49:45 1999 @@ -11,7 +11,7 @@ #include #include #include -#include +//#include #include #include @@ -19,6 +19,15 @@ #include "usb.h" #include "hub.h" +#ifdef __alpha +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +extern long __kernel_thread(unsigned long, int (*)(void *), void *); +static inline long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + return __kernel_thread(flags | CLONE_VM, fn, arg); +} +#endif +#endif /* Wakes up khubd */ static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED; @@ -73,10 +82,10 @@ unsigned long flags; switch (status) { - case USB_ST_REMOVED: + case -ENODEV: /* Just ignore it */ break; - case USB_ST_NOERROR: + case 0: /* Something happened, let khubd figure it out */ if (waitqueue_active(&khubd_wait)) { /* Add the hub to the event queue */ @@ -112,8 +121,10 @@ if (!bitmap) return -1; - if (usb_get_hub_descriptor(dev, bitmap, header->bLength) < 0) + if (usb_get_hub_descriptor(dev, bitmap, header->bLength) < 0) { + kfree(bitmap); return -1; + } descriptor = (struct usb_hub_descriptor *)bitmap; @@ -237,12 +248,12 @@ spin_unlock_irqrestore(&hub_event_lock, flags); if (usb_hub_configure(hub) >= 0) { - hub->irqpipe = usb_rcvctrlpipe(dev, endpoint->bEndpointAddress); + hub->irqpipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); ret = usb_request_irq(dev, hub->irqpipe, hub_irq, endpoint->bInterval, hub, &hub->irq_handle); if (ret) { - printk (KERN_WARNING "usb-hub: usb_request_irq failed (0x%x)\n", ret); + printk (KERN_WARNING "hub: usb_request_irq failed (%d)\n", ret); /* free hub, but first clean up its list. */ spin_lock_irqsave(&hub_event_lock, flags); @@ -295,29 +306,27 @@ struct usb_port_status portsts; unsigned short portstatus, portchange; - /* Disconnect anything that may have been there */ - usb_disconnect(&hub->children[port]); - - /* Reset the port */ - usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); - - wait_ms(50); /* FIXME: This is from the *BSD stack, thanks! :) */ - /* Check status */ - if (usb_get_port_status(hub, port + 1, &portsts) < 0) { + if (usb_get_port_status(hub, port + 1, &portsts)<0) { printk(KERN_ERR "get_port_status failed\n"); return; } portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); - + printk("hub.c: portstatus %x, change %x\n",portstatus,portchange); /* If it's not in CONNECT and ENABLE state, we're done */ if ((!(portstatus & USB_PORT_STAT_CONNECTION)) && - (!(portstatus & USB_PORT_STAT_ENABLE))) + (!(portstatus & USB_PORT_STAT_ENABLE))) { + /* Disconnect anything that may have been there */ + usb_disconnect(&hub->children[port]); /* We're done now, we already disconnected the device */ return; - + } + wait_ms(400); + /* Reset the port */ + usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); + wait_ms(100); /* Allocate a new device struct for it */ usb = usb_alloc_dev(hub, hub->bus); if (!usb) { @@ -518,3 +527,6 @@ */ usb_deregister(&hub_driver); } /* usb_hub_cleanup() */ + + + diff -u --recursive --new-file v2.3.33/linux/drivers/usb/inits.h linux/drivers/usb/inits.h --- v2.3.33/linux/drivers/usb/inits.h Mon Nov 1 13:56:26 1999 +++ linux/drivers/usb/inits.h Thu Dec 16 01:27:42 1999 @@ -13,3 +13,5 @@ void proc_usb_cleanup (void); int usb_scsi_init(void); int usb_serial_init (void); +int dabusb_init(void); +void dabusb_cleanup(void); diff -u --recursive --new-file v2.3.33/linux/drivers/usb/keyboard.c linux/drivers/usb/keyboard.c --- v2.3.33/linux/drivers/usb/keyboard.c Tue Dec 7 09:32:46 1999 +++ linux/drivers/usb/keyboard.c Fri Dec 17 16:56:05 1999 @@ -32,226 +32,222 @@ #define USBKBD_REPEAT_DELAY (HZ / 4) #define USBKBD_REPEAT_RATE (HZ / 20) -struct usb_keyboard -{ - struct usb_device *dev; - unsigned long down[2]; - unsigned char repeat_key; - struct timer_list repeat_timer; - struct list_head list; - unsigned int irqpipe; - void *irq_handler; /* host controller's IRQ transfer handle */ +struct usb_keyboard { + struct usb_device *dev; + unsigned long down[2]; + unsigned char repeat_key; + struct timer_list repeat_timer; + struct list_head list; + unsigned int irqpipe; + void *irq_handler; /* host controller's IRQ transfer handle */ }; extern unsigned char usb_kbd_map[]; -static void * usb_kbd_probe(struct usb_device *dev, unsigned int i); +static void *usb_kbd_probe(struct usb_device *dev, unsigned int i); static void usb_kbd_disconnect(struct usb_device *dev, void *ptr); static void usb_kbd_repeat(unsigned long dummy); static LIST_HEAD(usb_kbd_list); -static struct usb_driver usb_kbd_driver = -{ - "keyboard", - usb_kbd_probe, - usb_kbd_disconnect, - {NULL, NULL} +static struct usb_driver usb_kbd_driver = { + "keyboard", + usb_kbd_probe, + usb_kbd_disconnect, + {NULL, NULL} }; -static void -usb_kbd_handle_key(unsigned char key, int down) +static void usb_kbd_handle_key(unsigned char key, int down) { - int scancode = (int) usb_kbd_map[key]; - if(scancode) - { + int scancode = (int) usb_kbd_map[key]; + if (scancode) { #ifndef CONFIG_MAC_KEYBOARD - if(scancode & PCKBD_NEEDS_E0) - { - handle_scancode(0xe0, 1); - } -#endif /* CONFIG_MAC_KEYBOARD */ - handle_scancode((scancode & ~PCKBD_NEEDS_E0), down); - } + if (scancode & PCKBD_NEEDS_E0) { + handle_scancode(0xe0, 1); + } +#endif /* CONFIG_MAC_KEYBOARD */ + handle_scancode((scancode & ~PCKBD_NEEDS_E0), down); + } } -static void -usb_kbd_repeat(unsigned long dev_id) +static void usb_kbd_repeat(unsigned long dev_id) { - struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id; + struct usb_keyboard *kbd = (struct usb_keyboard *) dev_id; - unsigned long flags; - save_flags(flags); - cli(); - - if(kbd->repeat_key) - { - usb_kbd_handle_key(kbd->repeat_key, 1); - - /* reset repeat timer */ - kbd->repeat_timer.function = usb_kbd_repeat; - kbd->repeat_timer.expires = jiffies + USBKBD_REPEAT_RATE; - kbd->repeat_timer.data = (unsigned long) kbd; - kbd->repeat_timer.prev = NULL; - kbd->repeat_timer.next = NULL; - add_timer(&kbd->repeat_timer); - } + unsigned long flags; + save_flags(flags); + cli(); + + if (kbd->repeat_key) { + usb_kbd_handle_key(kbd->repeat_key, 1); + + /* reset repeat timer */ + kbd->repeat_timer.function = usb_kbd_repeat; + kbd->repeat_timer.expires = jiffies + USBKBD_REPEAT_RATE; + kbd->repeat_timer.data = (unsigned long) kbd; + kbd->repeat_timer.prev = NULL; + kbd->repeat_timer.next = NULL; + add_timer(&kbd->repeat_timer); + } - restore_flags(flags); + restore_flags(flags); } -static int -usb_kbd_irq(int state, void *buffer, int len, void *dev_id) +static int usb_kbd_irq(int state, void *buffer, int len, void *dev_id) { - struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id; - unsigned long *down = (unsigned long*) buffer; + struct usb_keyboard *kbd = (struct usb_keyboard *) dev_id; + unsigned long *down = (unsigned long *) buffer; - /* - * USB_ST_NOERROR is the normal case. - * USB_ST_REMOVED occurs if keyboard disconnected - * On other states, ignore - */ + /* + * USB_ST_NOERROR is the normal case. + * USB_ST_REMOVED occurs if keyboard disconnected + * On other states, ignore + */ - switch (state) { + switch (state) { case USB_ST_REMOVED: case USB_ST_INTERNALERROR: - printk(KERN_DEBUG "%s(%d): Suspending\n", __FILE__, __LINE__); - return 0; /* disable */ - case USB_ST_NOERROR: break; - default: return 1; /* ignore */ - } - - if(kbd->down[0] != down[0] || kbd->down[1] != down[1]) { - unsigned char *olddown, *newdown; - unsigned char modsdelta, key; - int i; - - /* handle modifier change */ - modsdelta = (*(unsigned char*) down ^ *(unsigned char*) kbd->down); - if(modsdelta) - { - for(i = 0; i < 8; i++) - { - if(modsdelta & 0x01) - { - int pressed = (*(unsigned char*) down >> i) & 0x01; - usb_kbd_handle_key( - i + USBKBD_MODIFIER_BASE, - pressed); - } - modsdelta >>= 1; - } - } - - olddown = (unsigned char*) kbd->down + USBKBD_KEYCODE_OFFSET; - newdown = (unsigned char*) down + USBKBD_KEYCODE_OFFSET; - - /* handle released keys */ - for(i = 0; i < USBKBD_KEYCODE_COUNT; i++) - { - key = olddown[i]; - if(USBKBD_VALID_KEYCODE(key) - && !USBKBD_FIND_KEYCODE(newdown, key, USBKBD_KEYCODE_COUNT)) - { - usb_kbd_handle_key(key, 0); - } - } - - /* handle pressed keys */ - kbd->repeat_key = 0; - for(i = 0; i < USBKBD_KEYCODE_COUNT; i++) - { - key = newdown[i]; - if(USBKBD_VALID_KEYCODE(key) - && !USBKBD_FIND_KEYCODE(olddown, key, USBKBD_KEYCODE_COUNT)) - { - usb_kbd_handle_key(key, 1); - kbd->repeat_key = key; - } - } - - /* set repeat timer if any keys were pressed */ - if(kbd->repeat_key) - { - del_timer(&kbd->repeat_timer); - kbd->repeat_timer.function = usb_kbd_repeat; - kbd->repeat_timer.expires = jiffies + USBKBD_REPEAT_DELAY; - kbd->repeat_timer.data = (unsigned long) kbd; - kbd->repeat_timer.prev = NULL; - kbd->repeat_timer.next = NULL; - add_timer(&kbd->repeat_timer); - } - - kbd->down[0] = down[0]; - kbd->down[1] = down[1]; - } + printk(KERN_DEBUG "%s(%d): Suspending\n", __FILE__, + __LINE__); + return 0; /* disable */ + case USB_ST_NOERROR: + break; + default: + return 1; /* ignore */ + } + + if (kbd->down[0] != down[0] || kbd->down[1] != down[1]) { + unsigned char *olddown, *newdown; + unsigned char modsdelta, key; + int i; + + /* handle modifier change */ + modsdelta = + (*(unsigned char *) down ^ *(unsigned char *) kbd-> + down); + if (modsdelta) { + for (i = 0; i < 8; i++) { + if (modsdelta & 0x01) { + int pressed = + (*(unsigned char *) down >> i) + & 0x01; + usb_kbd_handle_key(i + + USBKBD_MODIFIER_BASE, + pressed); + } + modsdelta >>= 1; + } + } + + olddown = + (unsigned char *) kbd->down + USBKBD_KEYCODE_OFFSET; + newdown = (unsigned char *) down + USBKBD_KEYCODE_OFFSET; + + /* handle released keys */ + for (i = 0; i < USBKBD_KEYCODE_COUNT; i++) { + key = olddown[i]; + if (USBKBD_VALID_KEYCODE(key) + && !USBKBD_FIND_KEYCODE(newdown, key, + USBKBD_KEYCODE_COUNT)) + { + usb_kbd_handle_key(key, 0); + } + } + + /* handle pressed keys */ + kbd->repeat_key = 0; + for (i = 0; i < USBKBD_KEYCODE_COUNT; i++) { + key = newdown[i]; + if (USBKBD_VALID_KEYCODE(key) + && !USBKBD_FIND_KEYCODE(olddown, key, + USBKBD_KEYCODE_COUNT)) + { + usb_kbd_handle_key(key, 1); + kbd->repeat_key = key; + } + } - return 1; + /* 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 void * -usb_kbd_probe(struct usb_device *dev, unsigned int i) +static void *usb_kbd_probe(struct usb_device *dev, unsigned int i) { - struct usb_interface_descriptor *interface; - struct usb_endpoint_descriptor *endpoint; - struct usb_keyboard *kbd; - int ret; - - interface = &dev->actconfig->interface[i].altsetting[0]; - endpoint = &interface->endpoint[0]; - - if(interface->bInterfaceClass != 3 - || interface->bInterfaceSubClass != 1 - || interface->bInterfaceProtocol != 1) - { - return NULL; - } - - 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; - - usb_set_protocol(dev, 0); - usb_set_idle(dev, 0, 0); - - kbd->irqpipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - ret = usb_request_irq(dev, kbd->irqpipe, - usb_kbd_irq, endpoint->bInterval, - kbd, &kbd->irq_handler); - if (ret) { - printk(KERN_INFO "usb-keyboard failed usb_request_irq (0x%x)\n", ret); - goto probe_err; + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_keyboard *kbd; + int ret; + + interface = &dev->actconfig->interface[i].altsetting[0]; + endpoint = &interface->endpoint[0]; + + if (interface->bInterfaceClass != 3 + || interface->bInterfaceSubClass != 1 + || interface->bInterfaceProtocol != 1) { + return NULL; } - list_add(&kbd->list, &usb_kbd_list); - - return kbd; - } + 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; + + usb_set_protocol(dev, 0); + usb_set_idle(dev, 0, 0); + + kbd->irqpipe = + usb_rcvintpipe(dev, endpoint->bEndpointAddress); + ret = + usb_request_irq(dev, kbd->irqpipe, usb_kbd_irq, + endpoint->bInterval, kbd, + &kbd->irq_handler); + if (ret) { + printk(KERN_INFO + "usb-keyboard failed usb_request_irq (0x%x)\n", + ret); + goto probe_err; + } + + list_add(&kbd->list, &usb_kbd_list); + + return kbd; + } probe_err: - if (kbd) - kfree (kbd); - return NULL; + if (kbd) + kfree(kbd); + return NULL; } -static void -usb_kbd_disconnect(struct usb_device *dev, void *ptr) +static void usb_kbd_disconnect(struct usb_device *dev, void *ptr) { - struct usb_keyboard *kbd = (struct usb_keyboard*) ptr; - if (kbd) - { - usb_release_irq(dev, kbd->irq_handler, kbd->irqpipe); - list_del(&kbd->list); - del_timer(&kbd->repeat_timer); - kfree(kbd); - } + struct usb_keyboard *kbd = (struct usb_keyboard *) ptr; + if (kbd) { + usb_release_irq(dev, kbd->irq_handler, kbd->irqpipe); + list_del(&kbd->list); + del_timer(&kbd->repeat_timer); + kfree(kbd); + } - printk(KERN_INFO "USB HID boot protocol keyboard removed.\n"); + printk(KERN_INFO "USB HID boot protocol keyboard removed.\n"); } int usb_kbd_init(void) @@ -266,15 +262,17 @@ cur = head->next; while (cur != head) { - struct usb_keyboard *kbd = list_entry(cur, struct usb_keyboard, list); + struct usb_keyboard *kbd = + list_entry(cur, struct usb_keyboard, list); cur = cur->next; - list_del(&kbd->list); - INIT_LIST_HEAD(&kbd->list); + list_del(&kbd->list); + INIT_LIST_HEAD(&kbd->list); if (kbd->irq_handler) { - usb_release_irq(kbd->dev, kbd->irq_handler, kbd->irqpipe); + usb_release_irq(kbd->dev, kbd->irq_handler, + kbd->irqpipe); /* never keep a reference to a released IRQ! */ kbd->irq_handler = NULL; } @@ -293,5 +291,5 @@ { usb_kbd_cleanup(); } -#endif +#endif diff -u --recursive --new-file v2.3.33/linux/drivers/usb/mouse.c linux/drivers/usb/mouse.c --- v2.3.33/linux/drivers/usb/mouse.c Tue Dec 7 09:32:46 1999 +++ linux/drivers/usb/mouse.c Fri Dec 17 16:49:45 1999 @@ -4,6 +4,9 @@ * * Brad Keryan 4/3/1999 * + * version 0.? Georg Acher 1999/10/30 + * URBification for UHCI-Acher/Fliegl/Sailer + * * version 0.30? Paul Ashton 1999/08/19 - Fixed behaviour on mouse * disconnect and suspend/resume. Added module parameter "force=1" * to allow opening of the mouse driver before mouse has been plugged @@ -29,7 +32,7 @@ * combos when you hold down a button and drag the mouse around. Probably has * some additional bugs on an SMP machine. */ - +#include #include #include #include @@ -39,10 +42,13 @@ #include #include #include -#include +//#include #include "usb.h" +#define MODSTR "mouse.c: " + + struct mouse_state { unsigned char buttons; /* current button state */ long dx; /* dx, dy, dz are change since last read */ @@ -60,6 +66,8 @@ /* FIXME: move these to a per-mouse structure */ struct usb_device *dev; /* host controller this mouse is on */ void* irq_handle; /* host controller's IRQ transfer handle */ + char* buffer; + urb_t* urb; __u8 bEndpointAddress; /* these are from the endpoint descriptor */ __u8 bInterval; /* ... used when calling usb_request_irq */ }; @@ -70,17 +78,21 @@ static int force=0; /* allow the USB mouse to be opened even if not there (yet) */ MODULE_PARM(force,"i"); +int xxx=0; -static int mouse_irq(int state, void *__buffer, int len, void *dev_id) +//static int mouse_irq(int state, void *__buffer, int len, void *dev_id) +static void mouse_irq(urb_t *urb) { - signed char *data = __buffer; + signed char *data = urb->transfer_buffer; + int state=urb->status; + int len=urb->actual_length; /* finding the mouse is easy when there's only one */ - struct mouse_state *mouse = &static_mouse_state; + struct mouse_state *mouse = urb->context; if (state) - printk(KERN_DEBUG "%s(%d):state %d, bp %p, len %d, dp %p\n", - __FILE__, __LINE__, state, __buffer, len, dev_id); - + printk(KERN_DEBUG "%s(%d):state %d, bp %p, len %d\n", + __FILE__, __LINE__, state, data, len); + //printk("mouseirq: %i\n",xxx++); /* * USB_ST_NOERROR is the normal case. * USB_ST_REMOVED occurs if mouse disconnected or suspend/resume @@ -95,17 +107,20 @@ printk(KERN_DEBUG "%s(%d): Suspending\n", __FILE__, __LINE__); mouse->suspended = 1; - return 0; /* disable */ + // FIXME stop interrupt! + return; case USB_ST_NOERROR: break; - default: return 1; /* ignore */ + default: + return; /* ignore */ } /* if a mouse moves with no one listening, do we care? no */ if(!mouse->active) - return 1; + return; /* if the USB mouse sends an interrupt, then something noteworthy must have happened */ + mouse->buttons = data[0] & 0x0f; mouse->dx += data[1]; /* data[] is signed, so this works */ mouse->dy -= data[2]; /* y-axis is reversed */ @@ -117,9 +132,12 @@ wake_up_interruptible(&mouse->wait); if (mouse->fasync) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,20) kill_fasync(mouse->fasync, SIGIO, POLL_IN); - - return 1; +#else + kill_fasync(mouse->fasync, SIGIO); +#endif + return; } static int fasync_mouse(int fd, struct file *filp, int on) @@ -145,11 +163,8 @@ if (--mouse->active == 0) { mouse->suspended = 0; /* stop polling the mouse while its not in use */ - usb_release_irq(mouse->dev, mouse->irq_handle, - usb_rcvctrlpipe(mouse->dev, mouse->bEndpointAddress)); - /* never keep a reference to a released IRQ! */ - mouse->irq_handle = NULL; - } + usb_unlink_urb(mouse->urb); + } return 0; } @@ -158,6 +173,7 @@ { struct mouse_state *mouse = &static_mouse_state; int ret; + unsigned int pipe; printk(KERN_DEBUG "%s(%d): open_mouse\n", __FILE__, __LINE__); /* @@ -193,14 +209,22 @@ return 0; /* start the usb controller's polling of the mouse */ - ret = usb_request_irq(mouse->dev, usb_rcvctrlpipe(mouse->dev, mouse->bEndpointAddress), - mouse_irq, mouse->bInterval, - NULL, &mouse->irq_handle); + pipe = usb_rcvintpipe(mouse->dev, mouse->bEndpointAddress); + FILL_INT_URB(mouse->urb,mouse->dev,pipe, + mouse->buffer, + usb_maxpacket(mouse->urb->dev, pipe, usb_pipeout(pipe)), + mouse_irq,mouse,mouse->bInterval); + + ret=usb_submit_urb(mouse->urb); + + // ret = usb_request_irq(mouse->dev, usb_rcvctrlpipe(mouse->dev, mouse->bEndpointAddress), + // mouse_irq, mouse->bInterval, + // NULL, &mouse->irq_handle); if (ret) { - printk (KERN_WARNING "usb-mouse: usb_request_irq failed (0x%x)\n", ret); + printk (KERN_WARNING "usb-mouse: usb_submit_urb(INT) failed (0x%x)\n", ret); + MOD_DEC_USE_COUNT; return ret; } - return 0; } @@ -212,8 +236,6 @@ /* * Look like a PS/2 mouse, please.. - * In XFree86 (3.3.5 tested) you must select Protocol "NetMousePS/2", - * then use your wheel as Button 4 and 5 via ZAxisMapping 4 5. * * The PS/2 protocol is fairly strange, but * oh, well, it's at least common.. @@ -224,6 +246,8 @@ static int state = 0; struct mouse_state *mouse = &static_mouse_state; + if (!mouse->present) + return 0; /* * FIXME - Other mouse drivers handle blocking and nonblocking reads * differently here... @@ -318,15 +342,17 @@ fasync_mouse, }; -static void* mouse_probe(struct usb_device *dev, unsigned int i) + void* mouse_probe(struct usb_device *dev,unsigned int ifnum) { struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; struct mouse_state *mouse = &static_mouse_state; int ret; + unsigned int pipe; + printk("mouse probe for if %i\n",ifnum); /* Is it a mouse interface? */ - interface = &dev->actconfig->interface[i].altsetting[0]; + interface = &dev->actconfig->interface[ifnum].altsetting[0]; if (interface->bInterfaceClass != 3) return NULL; if (interface->bInterfaceSubClass != 1) @@ -363,35 +389,37 @@ { printk(KERN_DEBUG "%s(%d): mouse resume\n", __FILE__, __LINE__); /* restart the usb controller's polling of the mouse */ - ret = usb_request_irq(mouse->dev, usb_rcvctrlpipe(mouse->dev, mouse->bEndpointAddress), - mouse_irq, mouse->bInterval, - NULL, &mouse->irq_handle); + + pipe = usb_rcvintpipe(mouse->dev, mouse->bEndpointAddress); + FILL_INT_URB(mouse->urb,mouse->dev,pipe, + mouse->buffer, + usb_maxpacket(mouse->urb->dev, pipe, usb_pipeout(pipe)), + mouse_irq,mouse,mouse->bInterval); + + ret=usb_submit_urb(mouse->urb); if (ret) { - printk (KERN_WARNING "usb-mouse: usb_request_irq failed (0x%x)\n", ret); + printk (KERN_WARNING "usb-mouse: usb_submit_urb(INT) failed (0x%x)\n", ret); return NULL; } mouse->suspended = 0; } + printk("mouse probe2\n"); return mouse; } -static void mouse_disconnect(struct usb_device *dev, void *ptr) +static void mouse_disconnect(struct usb_device *dev, void *priv) { - struct mouse_state *mouse = ptr; + struct mouse_state *mouse = priv; /* stop the usb interrupt transfer */ if (mouse->present) { - usb_release_irq(mouse->dev, mouse->irq_handle, - usb_rcvctrlpipe(mouse->dev, mouse->bEndpointAddress)); - /* never keep a reference to a released IRQ! */ + usb_unlink_urb(mouse->urb); } - mouse->irq_handle = NULL; - /* this might need work */ mouse->present = 0; - printk("USB Mouse disconnected\n"); + printk("Mouse disconnected\n"); } static struct usb_driver mouse_driver = { @@ -408,11 +436,18 @@ struct mouse_state *mouse = &static_mouse_state; mouse->present = mouse->active = mouse->suspended = 0; - mouse->irq_handle = NULL; + mouse->buffer=kmalloc(64,GFP_KERNEL); + + if (!mouse->buffer) + return -ENOMEM; + mouse->urb=usb_alloc_urb(0); + if (!mouse->urb) + printk(KERN_DEBUG MODSTR"URB allocation failed\n"); + init_waitqueue_head(&mouse->wait); mouse->fasync = NULL; - if (usb_register(&mouse_driver) < 0) + if (usb_register(&mouse_driver)<0) return -1; printk(KERN_INFO "USB HID boot protocol mouse driver registered.\n"); @@ -425,12 +460,10 @@ /* stop the usb interrupt transfer */ if (mouse->present) { - usb_release_irq(mouse->dev, mouse->irq_handle, - usb_rcvctrlpipe(mouse->dev, mouse->bEndpointAddress)); - /* never keep a reference to a released IRQ! */ - mouse->irq_handle = NULL; - } - + usb_unlink_urb(mouse->urb); + } + kfree(mouse->urb); + kfree(mouse->buffer); /* this, too, probably needs work */ usb_deregister(&mouse_driver); } diff -u --recursive --new-file v2.3.33/linux/drivers/usb/ohci-debug.c linux/drivers/usb/ohci-debug.c --- v2.3.33/linux/drivers/usb/ohci-debug.c Sat Oct 9 11:47:50 1999 +++ linux/drivers/usb/ohci-debug.c Wed Dec 31 16:00:00 1969 @@ -1,218 +0,0 @@ -/* - * OHCI debugging code. It's gross. - * - * (C) Copyright 1999 Gregory P. Smith - */ - -#include -#include - -#include "ohci.h" - -void show_ohci_status(struct ohci *ohci) -{ - struct ohci_regs regs; - int i; - - regs.revision = readl(&ohci->regs->revision); - regs.control = readl(&ohci->regs->control); - regs.cmdstatus = readl(&ohci->regs->cmdstatus); - regs.intrstatus = readl(&ohci->regs->intrstatus); - regs.intrenable = readl(&ohci->regs->intrenable); - regs.hcca = readl(&ohci->regs->hcca); - regs.ed_periodcurrent = readl(&ohci->regs->ed_periodcurrent); - regs.ed_controlhead = readl(&ohci->regs->ed_controlhead); - regs.ed_controlcurrent = readl(&ohci->regs->ed_controlcurrent); - regs.ed_bulkhead = readl(&ohci->regs->ed_bulkhead); - regs.ed_bulkcurrent = readl(&ohci->regs->ed_bulkcurrent); - regs.current_donehead = readl(&ohci->regs->current_donehead); - regs.fminterval = readl(&ohci->regs->fminterval); - regs.fmremaining = readl(&ohci->regs->fmremaining); - regs.fmnumber = readl(&ohci->regs->fmnumber); - regs.periodicstart = readl(&ohci->regs->periodicstart); - regs.lsthresh = readl(&ohci->regs->lsthresh); - regs.roothub.a = readl(&ohci->regs->roothub.a); - regs.roothub.b = readl(&ohci->regs->roothub.b); - regs.roothub.status = readl(&ohci->regs->roothub.status); - for (i=0; iregs->roothub.portstatus[i]); - - printk(KERN_DEBUG " ohci revision = %x\n", regs.revision); - printk(KERN_DEBUG " ohci control = %x\n", regs.control); - printk(KERN_DEBUG " ohci cmdstatus = %x\n", regs.cmdstatus); - printk(KERN_DEBUG " ohci intrstatus = %x\n", regs.intrstatus); - printk(KERN_DEBUG " ohci intrenable = %x\n", regs.intrenable); - - printk(KERN_DEBUG " ohci hcca = %x\n", regs.hcca); - printk(KERN_DEBUG " ohci ed_pdcur = %x\n", regs.ed_periodcurrent); - printk(KERN_DEBUG " ohci ed_ctrlhead = %x\n", regs.ed_controlhead); - printk(KERN_DEBUG " ohci ed_ctrlcur = %x\n", regs.ed_controlcurrent); - printk(KERN_DEBUG " ohci ed_bulkhead = %x\n", regs.ed_bulkhead); - printk(KERN_DEBUG " ohci ed_bulkcur = %x\n", regs.ed_bulkcurrent); - printk(KERN_DEBUG " ohci curdonehead = %x\n", regs.current_donehead); - - printk(KERN_DEBUG " ohci fminterval = %x\n", regs.fminterval); - printk(KERN_DEBUG " ohci fmremaining = %x\n", regs.fmremaining); - printk(KERN_DEBUG " ohci fmnumber = %x\n", regs.fmnumber); - printk(KERN_DEBUG " ohci pdstart = %x\n", regs.periodicstart); - printk(KERN_DEBUG " ohci lsthresh = %x\n", regs.lsthresh); - - printk(KERN_DEBUG " ohci roothub.a = %x\n", regs.roothub.a); - printk(KERN_DEBUG " ohci roothub.b = %x\n", regs.roothub.b); - printk(KERN_DEBUG " ohci root status = %x\n", regs.roothub.status); - printk(KERN_DEBUG " roothub.port0 = %x\n", regs.roothub.portstatus[0]); - printk(KERN_DEBUG " roothub.port1 = %x\n", regs.roothub.portstatus[1]); -} /* show_ohci_status() */ - - -void show_ohci_ed(struct ohci_ed *ed) -{ - int stat = le32_to_cpup(&ed->status); - int skip = (stat & OHCI_ED_SKIP); - int mps = (stat & OHCI_ED_MPS) >> 16; - int isoc = (stat & OHCI_ED_F_ISOC); - int low_speed = (stat & OHCI_ED_S_LOW); - int dir = (stat & OHCI_ED_D); - int endnum = (stat & OHCI_ED_EN) >> 7; - int funcaddr = (stat & OHCI_ED_FA); - int halted = (le32_to_cpup(&ed->_head_td) & 1); - int toggle = (le32_to_cpup(&ed->_head_td) & 2) >> 1; - - printk(KERN_DEBUG " ohci ED:\n"); - printk(KERN_DEBUG " status = 0x%x\n", stat); - printk(KERN_DEBUG " %sMPS %d%s%s%s%s tc%d e%d fa%d [HCD_%d%s]\n", - skip ? "Skip " : "", - mps, - isoc ? " Isoc." : "", - low_speed ? " LowSpd" : "", - (dir == OHCI_ED_D_IN) ? " Input" : - (dir == OHCI_ED_D_OUT) ? " Output" : "", - halted ? " Halted" : "", - toggle, - endnum, - funcaddr, - ohci_ed_hcdtype(ed) >> 27, - (stat & ED_ALLOCATED) ? ", Allocated" : ""); - printk(KERN_DEBUG " tail_td = 0x%x\n", ed_tail_td(ed)); - printk(KERN_DEBUG " head_td = 0x%x\n", ed_head_td(ed)); - printk(KERN_DEBUG " next_ed = 0x%x\n", le32_to_cpup(&ed->next_ed)); -} /* show_ohci_ed() */ - - -void show_ohci_td(struct ohci_td *td) -{ - int info = le32_to_cpup(&td->info); - int td_round = info & OHCI_TD_ROUND; - int td_dir = info & OHCI_TD_D; - int td_int_delay = (info & OHCI_TD_IOC_DELAY) >> 21; - int td_toggle = (info & OHCI_TD_DT) >> 24; - int td_errcnt = td_errorcount(*td); - int td_cc = OHCI_TD_CC_GET(info); - - printk(KERN_DEBUG " ohci TD hardware fields:\n"); - printk(KERN_DEBUG " info = 0x%x\n", info); - printk(KERN_DEBUG " %s%s%s%d %s %s%d\n", - td_round ? "Rounding " : "", - (td_dir == OHCI_TD_D_IN) ? "Input " : - (td_dir == OHCI_TD_D_OUT) ? "Output " : - (td_dir == OHCI_TD_D_SETUP) ? "Setup " : "", - "IntDelay ", td_int_delay, - (td_toggle < 2) ? " " : - (td_toggle & 1) ? "Data1" : "Data0", - "ErrorCnt ", td_errcnt); - printk(KERN_DEBUG " ComplCode 0x%x, %sAccessed\n", - td_cc, - td_cc_accessed(*td) ? "" : "Not "); - - printk(KERN_DEBUG " %s%s\n", - td_allocated(*td) ? "Allocated" : "Free", - td_dummy(*td) ? " DUMMY" : ""); - - printk(KERN_DEBUG " cur_buf = 0x%x\n", le32_to_cpup(&td->cur_buf)); - printk(KERN_DEBUG " next_td = 0x%x\n", le32_to_cpup(&td->next_td)); - printk(KERN_DEBUG " buf_end = 0x%x\n", le32_to_cpup(&td->buf_end)); - printk(KERN_DEBUG " ohci TD driver fields:\n"); - printk(KERN_DEBUG " flags = %x {", td->hcd_flags); - if (td_allocated(*td)) - printk(" alloc"); - if (td_dummy(*td)) - printk(" dummy"); - if (td_endofchain(*td)) - printk(" endofchain"); - if (!can_auto_free(*td)) - printk(" noautofree"); - printk("}\n"); - printk(KERN_DEBUG " data = %p\n", td->data); - printk(KERN_DEBUG " cmpltd = %p\n", td->completed); - printk(KERN_DEBUG " dev_id = %p\n", td->dev_id); - printk(KERN_DEBUG " ed = %p\n", td->ed); - if (td->data != NULL) { - unsigned char *d = td->data; - printk(KERN_DEBUG " DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n", - d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7] ); - } -} /* show_ohci_td() */ - - -void show_ohci_td_chain(struct ohci_td *td) -{ - struct ohci_td *cur_td; - if (td == NULL) return; - - printk(KERN_DEBUG "+++ OHCI TD Chain %lx: +++\n", virt_to_bus(td)); - - cur_td = td; - for (;;) { - show_ohci_td(cur_td); - if (!cur_td->next_td) break; - cur_td = bus_to_virt(le32_to_cpup(&cur_td->next_td)); - /* we can't trust -anything- we find inside of a dummy TD */ - if (td_dummy(*cur_td)) break; - } - - printk(KERN_DEBUG "--- End TD Chain %lx. ---\n", virt_to_bus(td)); -} /* show_ohci_td_chain () */ - - -void show_ohci_device(struct ohci_device *dev) -{ - int idx; - printk(KERN_DEBUG " ohci_device usb = %p\n", dev->usb); - printk(KERN_DEBUG " ohci_device ohci = %p\n", dev->ohci); - printk(KERN_DEBUG " ohci_device ohci_hcca = %p\n", dev->hcca); - for (idx=0; idx<3 /*NUM_EDS*/; ++idx) { - printk(KERN_DEBUG " [ed num %d] ", idx); - show_ohci_ed(&dev->ed[idx]); - } - for (idx=0; idx<3 /*NUM_TDS*/; ++idx) { - printk(KERN_DEBUG " [td num %d] ", idx); - show_ohci_td(&dev->td[idx]); - } - printk(KERN_DEBUG " ohci_device data\n "); - for (idx=0; idx<4; ++idx) { - printk(KERN_DEBUG " %08lx", dev->data[idx]); - } - printk(KERN_DEBUG "\n"); -} /* show_ohci_device() */ - - -void show_ohci_hcca(struct ohci_hcca *hcca) -{ - int idx; - - printk(KERN_DEBUG " ohci_hcca\n"); - - for (idx=0; idxint_table + idx)); - } - - printk(KERN_DEBUG " frame_no == %d\n", - le16_to_cpup(&hcca->frame_no)); - printk(KERN_DEBUG " donehead == 0x%08x\n", - le32_to_cpup(&hcca->donehead)); -} /* show_ohci_hcca() */ - - -/* vim:sw=8 - */ diff -u --recursive --new-file v2.3.33/linux/drivers/usb/ohci-hcd.c linux/drivers/usb/ohci-hcd.c --- v2.3.33/linux/drivers/usb/ohci-hcd.c Tue Dec 7 09:32:46 1999 +++ linux/drivers/usb/ohci-hcd.c Mon Dec 20 14:07:38 1999 @@ -1,20 +1,21 @@ /* - * OHCI HCD (Host Controller Driver) for USB. + * URB OHCI HCD (Host Controller Driver) for USB. * * (C) Copyright 1999 Roman Weissgaerber - * - * The OHCI HCD layer is a simple but nearly complete implementation of what - * the USB people would call a HCD for the OHCI. - * (ISO alpha , Bulk, INT u. CTRL transfers enabled) - * The layer on top of it, is for interfacing to the alternate-usb - * device-drivers. * - * [ This is based on Linus' UHCI code and gregs OHCI fragments - * (0.03c source tree). ] - * [ Open Host Controller Interface driver for USB. ] - * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ] - * [ (C) Copyright 1999 Gregory P. Smith ] + * [ Initialisation is based on Linus' ] + * [ uhci code and gregs ohci fragments ] + * [ (C) Copyright 1999 Linus Torvalds ] + * [ (C) Copyright 1999 Gregory P. Smith] + * * + * History: + * + * v5.2 1999/12/07 URB 3rd preview, + * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi) + * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume + * i386: HUB, Keyboard, Mouse, Printer + * * v4.3 1999/10/27 multiple HCs, bulk_request * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl. @@ -22,18 +23,10 @@ * v3.0 1999/06/25 * v2.1 1999/05/09 code clean up * v2.0 1999/05/04 - * virtual root hub is now enabled, - * memory allocation based on kmalloc and kfree now, Bus error handling, - * INT, CTRL and BULK transfers enabled, ISO needs testing (alpha) - * - * from Linus Torvalds (uhci.c) (APM not tested; hub, usb_device, bus and related stuff) - * from Greg Smith (ohci.c) (reset controller handling, hub) - * * v1.0 1999/04/27 initial release - * ohci-hcd.c + * + */ - - #include #include @@ -46,584 +39,514 @@ #include #include #include -#include +// #include #include +#include /* for in_interrupt() */ #include #include #include +// #define DEBUG + #include "usb.h" #include "ohci-hcd.h" + +#ifdef DEBUG + #define dbg(format, arg...) printk(format, ## arg) +#else + #define dbg(format, arg...) +#endif + #ifdef CONFIG_APM #include -static int handle_apm_event(apm_event_t event); -static int apm_resume = 0; +static int handle_apm_event (apm_event_t event); #endif -static LIST_HEAD(ohci_hcd_list); - -static int ohci_link_ed(struct ohci * ohci, struct usb_ohci_ed *ed); -static int sohci_kill_isoc (struct usb_isoc_desc *id); -static int sohci_get_current_frame_number (struct usb_device *usb_dev); -static int sohci_run_isoc(struct usb_isoc_desc *id, struct usb_isoc_desc *pr_id); -static DECLARE_WAIT_QUEUE_HEAD(op_wakeup); - -void usb_pipe_to_hcd_ed(struct usb_device *usb_dev, unsigned int pipe, struct usb_hcd_ed *hcd_ed) -{ - hcd_ed->endpoint = usb_pipeendpoint(pipe); - hcd_ed->out = usb_pipeout(pipe); - hcd_ed->function = usb_pipedevice(pipe); - hcd_ed->type = usb_pipetype(pipe); - hcd_ed->slow = usb_pipeslow(pipe); - hcd_ed->maxpack = usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)); - OHCI_DEBUG(printk("******* hcd_ed: pipe: %8x, endpoint: %4x, function: %4x, out: %4x, type: %4x, slow: %4x, maxpack: %4x\n", pipe, hcd_ed->endpoint, hcd_ed->function, hcd_ed->out, hcd_ed->type, hcd_ed->slow, hcd_ed->maxpack); ) -OHCI_DEBUG(printk("******* dev: devnum: %4x, slow: %4x, maxpacketsize: %4x\n",usb_dev->devnum, usb_dev->slow, usb_dev->maxpacketsize); ) -} +#ifdef CONFIG_PMAC_PBOOK +#include +#include +#endif -/******** - **** Interface functions - ***********************************************/ - -static int sohci_blocking_handler(void * ohci_in, struct usb_ohci_td *td, void * data, int data_len, int dlen, int status, __OHCI_BAG lw0, __OHCI_BAG lw1) -{ - struct usb_ohci_ed *ed = td->ed; - if(lw0 != NULL) { - if((status == USB_ST_DATAUNDERRUN || status == USB_ST_NOERROR)) - ((struct ohci_state * )lw0)->status = data_len; - else - ((struct ohci_state * )lw0)->status = status; - ((struct ohci_state * )lw0)->len = data_len; - } - if(lw1 != NULL) { - add_wait_queue(&op_wakeup, lw1); - wake_up(&op_wakeup); - } - - OHCI_DEBUG( { int i; printk("USB HC bh <<<: %x: ", ed->hwINFO);) - OHCI_DEBUG( printk(" data(%d):", data_len);) - OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) - OHCI_DEBUG( printk(" ret_status: %x\n", status); }) - - return 0; -} - -static int sohci_int_handler(void * ohci_in, struct usb_ohci_td *td, void * data, int data_len, int dlen, int status, __OHCI_BAG lw0, __OHCI_BAG lw1) -{ - struct usb_ohci_ed *ed = td->ed; - struct ohci * ohci = ohci_in; - usb_device_irq handler=(void *) lw0; - void *dev_id = (void *) lw1; - int ret; +static DECLARE_WAIT_QUEUE_HEAD (op_wakeup); +static LIST_HEAD (ohci_hcd_list); +spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED; - OHCI_DEBUG({ int i; printk("USB HC IRQ <<<: %x: data(%d):", ed->hwINFO, data_len);) - OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) - OHCI_DEBUG( printk(" ret_status: %x\n", status); }) - - ret = handler(status, data, data_len, dev_id); - if(ret == 0) return 0; /* 0 .. do not requeue */ - if(status < 0) return -1; /* error occured do not requeue ? */ - ohci_trans_req(ohci, ed, 0, NULL, data, (ed->hwINFO >> 16) & 0x3f, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id, INT_IN, sohci_int_handler); /* requeue int request */ +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ - return 0; -} - -static int sohci_ret_handler(void * ohci_in, struct usb_ohci_td *td, void * data, int data_len, int dlen, int status, __OHCI_BAG lw0, __OHCI_BAG lw1) -{ - struct usb_ohci_ed *ed = td->ed; - struct ohci * ohci = ohci_in; - usb_device_irq handler=(void *) lw0; - void *dev_id = (void *) lw1; - int ret; - - OHCI_DEBUG({ int i; printk("USB HC RET <<<: %x: data(%d):", ed->hwINFO, data_len);) - OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) - OHCI_DEBUG( printk(" ret_status: %x\n", status); }) - - ret = handler(status, data, data_len, dev_id); - if(ret == 0) return 0; /* 0 .. do not requeue */ - if(status > 0) return -1; /* error occured do not requeue ? */ - ohci_trans_req(ohci, ed, 0, NULL, data, dlen, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id, td->type & 0x7, sohci_ret_handler); /* requeue int request */ +/* free the private part of an URB */ - return 0; -} - -static int sohci_iso_handler(void * ohci_in, struct usb_ohci_td *td, void * data, int data_len, int dlen, int status, __OHCI_BAG lw0, __OHCI_BAG lw1) { - - // struct ohci * ohci = ohci_in; - struct usb_ohci_ed *ed = td->ed; - unsigned int ix = (unsigned int) lw0; - struct usb_isoc_desc * id = (struct usb_isoc_desc *) lw1; - struct usb_ohci_td **tdp = id->td; - int ret = 0; - int fx; - - OHCI_DEBUG({ int i; printk("USB HC ISO |||: %x: data(%d):", ed->hwINFO, data_len);) - OHCI_DEBUG( for(i=0; i < 16 ; i++ ) printk(" %02x", ((__u8 *) data)[i]);) - OHCI_DEBUG( printk(" ... ret_status: %x\n", status); }) - - tdp[ix] = NULL; - id->frames[ix].frame_length = data_len; - id->frames[ix].frame_status = status; - id->total_length += data_len; - if(status) id->error_count++; - - id->cur_completed_frame++; - id->total_completed_frames++; - - if(id->cur_completed_frame == id->callback_frames) { - id->prev_completed_frame = id->cur_completed_frame; - id->cur_completed_frame = 0; - OHCI_DEBUG(printk("USB HC ISO <<<: %x: \n", ed->hwINFO);) - ret = id->callback_fn(id->error_count, id->data, id->total_length, id); - switch (ret) { - case CB_CONT_RUN: - for (fx = 0; fx < id->frame_count; fx++) - id->frames[fx].frame_length = id->frame_size; - sohci_run_isoc(id, id->prev_isocdesc); - break; - - case CB_CONTINUE: - break; - - case CB_REUSE: - break; - - case CB_RESTART: - break; - - case CB_ABORT: - sohci_kill_isoc(id); - break; +static void urb_rm_priv (urb_t * urb) +{ + urb_priv_t * urb_priv = urb->hcpriv; + int i; + void * wait; + + if (!urb_priv) return; + + wait = urb_priv->wait; + + for (i = 0; i < urb_priv->length; i++) { + if (urb_priv->td [i]) { + OHCI_FREE (urb_priv->td [i]); } } + kfree (urb->hcpriv); + urb->hcpriv = NULL; - return 0; -} - -static int sohci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id, void **handle, long bustime) -{ - struct ohci * ohci = usb_dev->bus->hcpriv; - struct ohci_device * dev = usb_to_ohci(usb_dev); - struct usb_hcd_ed hcd_ed; - struct usb_ohci_ed * ed; - -#ifdef VROOTHUB - if(usb_pipedevice(pipe) == ohci->rh.devnum) - return root_hub_request_irq(usb_dev, pipe, handler, period, dev_id, handle); -#endif - - usb_pipe_to_hcd_ed(usb_dev, pipe, &hcd_ed); - hcd_ed.type = INT; - ed = usb_ohci_add_ep(usb_dev, &hcd_ed, period, 1); - - OHCI_DEBUG( printk("USB HC IRQ>>>: %x: every %d ms\n", ed->hwINFO, period);) - - ohci_trans_req(ohci, ed, 0, NULL, dev->data, hcd_ed.maxpack, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id, INT_IN, sohci_int_handler); - if (ED_STATE(ed) != ED_OPER) ohci_link_ed(ohci, ed); - *handle = ed; - return 0; - + if (wait) { + add_wait_queue (&op_wakeup, wait); + wake_up (&op_wakeup); + } } -static int sohci_release_irq(struct usb_device *usb_dev, void * ed) -{ - // struct usb_device *usb_dev = ((struct ohci_device *) ((unsigned int)ed & 0xfffff000))->usb; - struct ohci * ohci = usb_dev->bus->hcpriv; - - OHCI_DEBUG( printk("USB HC ***** RM_IRQ>>>:%4x\n", (unsigned int) ed);) - - if(ed == NULL) return 0; - -#ifdef VROOTHUB - if(ed == ohci->rh.int_addr) - return root_hub_release_irq(usb_dev, ed); -#endif - ED_setSTATE((struct usb_ohci_ed *)ed, ED_STOP); - - usb_ohci_rm_ep(usb_dev, (struct usb_ohci_ed *) ed, NULL, NULL, NULL, 0); +/*-------------------------------------------------------------------------*/ - return 0; -} +#ifdef DEBUG +static int sohci_get_current_frame_number (struct usb_device * dev); -static int sohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, void *data, int len, int timeout) +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void urb_print (urb_t * urb, char * str, int small) { - DECLARE_WAITQUEUE(wait, current); - struct ohci_state state = {0, TD_NOTACCESSED}; - struct ohci * ohci = usb_dev->bus->hcpriv; - struct usb_hcd_ed hcd_ed; - struct usb_ohci_ed *ed; - -#ifdef VROOTHUB - if(usb_pipedevice(pipe) == ohci->rh.devnum) - return root_hub_control_msg(usb_dev, pipe, cmd, data, len); -#endif - - usb_pipe_to_hcd_ed(usb_dev, pipe, &hcd_ed); - hcd_ed.type = CTRL; - - ed = usb_ohci_add_ep(usb_dev, &hcd_ed, 0, 1); + unsigned int pipe= urb->pipe; + int i, len; - OHCI_DEBUG( { int i; printk("USB HC CTRL>>>: ed:%x-%x: ctrl(%d):", (unsigned int) ed, ed->hwINFO, 8);) - OHCI_DEBUG( for(i=0; i < 8; i++ ) printk(" %02x", ((__u8 *) cmd)[i]);) - OHCI_DEBUG( printk(" data(%d):", len);) - OHCI_DEBUG( for(i=0; i < len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) - OHCI_DEBUG( printk("\n"); }) - current->state = TASK_UNINTERRUPTIBLE; - - ohci_trans_req(ohci, ed, 8, cmd, data, len, (__OHCI_BAG) &state, (__OHCI_BAG) &wait, (usb_pipeout(pipe))?CTRL_OUT:CTRL_IN, sohci_blocking_handler); - - OHCI_DEBUG(printk("USB HC trans req ed %x: %x :", ed->hwINFO, (unsigned int ) ed); ) - OHCI_DEBUG({ int i; for( i= 0; i<8 ;i++) printk(" %4x", ((unsigned int *) ed)[i]) ; printk("\n"); }; ) - if (ED_STATE(ed) != ED_OPER) ohci_link_ed(ohci, ed); - schedule_timeout(timeout); - - if(state.status == TD_NOTACCESSED) { - current->state = TASK_UNINTERRUPTIBLE; - usb_ohci_rm_ep(usb_dev, ed, sohci_blocking_handler, NULL, NULL, 0); - schedule(); - state.status = USB_ST_TIMEOUT; - } - remove_wait_queue(&op_wakeup, &wait); - return state.status; -} - -static int sohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *rval, int timeout) -{ - DECLARE_WAITQUEUE(wait, current); - struct ohci_state state = {0, TD_NOTACCESSED}; - struct ohci * ohci = usb_dev->bus->hcpriv; - struct usb_hcd_ed hcd_ed; - struct usb_ohci_ed *ed; + if (!urb->dev || !urb->dev->bus) { + printk(KERN_DEBUG " %s URB: no dev\n", str); + return; + } - usb_pipe_to_hcd_ed(usb_dev, pipe, &hcd_ed); - hcd_ed.type = BULK; - ed = usb_ohci_add_ep(usb_dev, &hcd_ed, 0, 1); - OHCI_DEBUG( { int i; printk("USB HC BULK>>>: %x: ", ed->hwINFO);) - OHCI_DEBUG( printk(" data(%d):", len);) - OHCI_DEBUG( for(i=0; i < len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) - OHCI_DEBUG( printk("\n"); }) - current->state = TASK_UNINTERRUPTIBLE; - - ohci_trans_req(ohci, ed, 0, NULL, data, len, (__OHCI_BAG) &state, (__OHCI_BAG) &wait,(usb_pipeout(pipe))?BULK_OUT:BULK_IN, sohci_blocking_handler); - if (ED_STATE(ed) != ED_OPER) ohci_link_ed(ohci, ed); + printk (KERN_DEBUG "%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s," + "flags:%4x,len:%d/%d,stat:%d(%x)\n", + str, + sohci_get_current_frame_number (urb->dev), + usb_pipedevice (pipe), + usb_pipeendpoint (pipe), + usb_pipeout (pipe)? 'O': 'I', + usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): + (usb_pipecontrol (pipe)? "CTRL": "BULK"), + urb->transfer_flags, + urb->actual_length, + urb->transfer_buffer_length, + urb->status, urb->status); + if (!small) { + if (usb_pipecontrol (pipe)) { + printk (KERN_DEBUG " cmd(8):"); + for (i = 0; i < 8 ; i++) + printk (" %02x", ((__u8 *) urb->setup_packet) [i]); + printk ("\n"); + } + if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { + printk (KERN_DEBUG " data(%d/%d):", + urb->actual_length, + urb->transfer_buffer_length); + len = usb_pipeout (pipe)? + urb->transfer_buffer_length: urb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]); + printk ("%s stat:%d\n", i < len? "...": "", urb->status); + } + } - schedule_timeout(timeout); - - if(state.status == TD_NOTACCESSED) { - current->state = TASK_UNINTERRUPTIBLE; - usb_ohci_rm_ep(usb_dev, ed, sohci_blocking_handler, NULL, NULL, 0); - schedule(); - state.status = USB_ST_TIMEOUT; +} +/* just for debugging; prints all 32 branches of the int ed tree inclusive iso eds*/ +void ep_print_int_eds (ohci_t * ohci, char * str) { + int i, j; + __u32 * ed_p; + for (i= 0; i < 32; i++) { + j = 5; + printk (KERN_DEBUG " %s branch int %2d(%2x): ", str, i, i); + ed_p = &(ohci->hcca.int_table [i]); + while (*ed_p != 0 && j--) { + printk ("ed: %4x; ", (((ed_t *) bus_to_virt (*ed_p))->hwINFO)); + ed_p = &(((ed_t *) bus_to_virt (*ed_p))->hwNextED); + } + printk ("\n"); } - remove_wait_queue(&op_wakeup, &wait); - *rval = state.len; - return state.status; } + -static void * sohci_request_bulk(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, void *data, int len, void *dev_id) -{ - struct ohci * ohci = usb_dev->bus->hcpriv; - struct usb_hcd_ed hcd_ed; - struct usb_ohci_ed * ed; - - usb_pipe_to_hcd_ed(usb_dev, pipe, &hcd_ed); - hcd_ed.type = BULK; - ed = usb_ohci_add_ep(usb_dev, &hcd_ed, 0, 1); - - OHCI_DEBUG( printk("USB HC BULK_RQ>>>: %x \n", ed->hwINFO);) - - ohci_trans_req(ohci, ed, 0, NULL, data, len, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id, (usb_pipeout(pipe))?BULK_OUT:BULK_IN, sohci_ret_handler); - if (ED_STATE(ed) != ED_OPER) ohci_link_ed(ohci, ed); +#endif - return ed; -} +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* return a request to the completion handler */ -static int sohci_terminate_bulk(struct usb_device *usb_dev, void * ed) +static int sohci_return_urb (urb_t * urb) { + urb_priv_t * urb_priv = urb->hcpriv; + urb_t * urbt; + unsigned int flags; + int i; - OHCI_DEBUG( printk("USB HC TERM_BULK>>>:%4x\n", (unsigned int) ed);) - - ED_setSTATE((struct usb_ohci_ed *)ed, ED_STOP); - - usb_ohci_rm_ep(usb_dev, (struct usb_ohci_ed *) ed, NULL, NULL, NULL, 0); + /* just to be sure */ + if (!urb->complete) { + urb_rm_priv (urb); + usb_dec_dev_use (urb->dev); + return -1; + } + + if (!urb_priv) return -1; /* urb already unlinked */ + +#ifdef DEBUG + urb_print (urb, "RET", usb_pipeout (urb->pipe)); +#endif + + switch (usb_pipetype (urb->pipe)) { + case PIPE_INTERRUPT: + urb->complete (urb); /* call complete and requeue URB */ + urb->actual_length = 0; + urb->status = USB_ST_URB_PENDING; + if (urb_priv->state != URB_DEL) + td_submit_urb (urb); + break; + + case PIPE_ISOCHRONOUS: + for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next); + if (urbt) { /* send the reply and requeue URB */ + urb->complete (urb); + + spin_lock_irqsave (&usb_ed_lock, flags); + urb->actual_length = 0; + urb->status = USB_ST_URB_PENDING; + urb->start_frame = urb_priv->ed->last_iso + 1; + if (urb_priv->state != URB_DEL) { + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = -EXDEV; + } + td_submit_urb (urb); + } + spin_unlock_irqrestore (&usb_ed_lock, flags); + + } else { /* unlink URB, call complete */ + urb_rm_priv (urb); + usb_dec_dev_use (urb->dev); + urb->complete (urb); + } + break; + + case PIPE_BULK: + case PIPE_CONTROL: /* unlink URB, call complete */ + urb_rm_priv (urb); + usb_dec_dev_use (urb->dev); + urb->complete (urb); + break; + } return 0; } -static int sohci_alloc_dev(struct usb_device *usb_dev) -{ - struct ohci_device *dev; - - /* Allocate the OHCI_HCD device private data */ - dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -1; - - /* Initialize "dev" */ - memset(dev, 0, sizeof(*dev)); +/*-------------------------------------------------------------------------*/ - usb_dev->hcpriv = dev; - dev->usb = usb_dev; - atomic_set(&dev->refcnt, 1); +/* get a transfer request */ + +static int sohci_submit_urb (urb_t * urb) +{ + ohci_t * ohci; + ed_t * ed; + urb_priv_t * urb_priv; + unsigned int pipe = urb->pipe; + int i, size = 0; + unsigned int flags; + + if (!urb->dev || !urb->dev->bus) return -EINVAL; + + if (urb->hcpriv) return -EINVAL; /* urb already in use */ - if (usb_dev->parent) - dev->ohci = usb_to_ohci(usb_dev->parent)->ohci; + usb_inc_dev_use (urb->dev); + ohci = (ohci_t *) urb->dev->bus->hcpriv; + +#ifdef DEBUG + urb_print (urb, "SUB", usb_pipein (pipe)); +#endif + + if (usb_pipedevice (pipe) == ohci->rh.devnum) + return rh_submit_urb (urb); /* a request to the virtual root hub */ - return 0; -} + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1))) { + usb_dec_dev_use (urb->dev); + return -ENOMEM; + } -static int sohci_free_dev(struct usb_device *usb_dev) -{ - int cnt; - DECLARE_WAITQUEUE(wait, current); - struct ohci_device *dev = usb_to_ohci(usb_dev); - - OHCI_DEBUG(printk("USB HC ***** free %x\n", usb_dev->devnum);) - wait_ms(10); - if(usb_dev->devnum >= 0) { - current->state = TASK_UNINTERRUPTIBLE; - cnt = usb_ohci_rm_function(usb_dev, sohci_blocking_handler, NULL, &wait); - if(cnt > 0) { - schedule(); - remove_wait_queue(&op_wakeup, &wait); - } - current->state = TASK_INTERRUPTIBLE; + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype (pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (urb->transfer_buffer_length - 1) / 4096 + 1; + break; + case PIPE_ISOCHRONOUS: /* number of packets from URB */ + size = urb->number_of_packets; + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = -EXDEV; + } + break; + case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (urb->transfer_buffer_length == 0)? 2: + (urb->transfer_buffer_length - 1) / 4096 + 3; + break; + case PIPE_INTERRUPT: /* one TD */ + size = 1; + + break; } -if (atomic_dec_and_test(&dev->refcnt)) - kfree(dev); + /* allocate the private part or the URB */ + urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!urb_priv) { + usb_dec_dev_use (urb->dev); + return -ENOMEM; + } + memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *)); + + /* fill the private part of the URB */ + urb->hcpriv = urb_priv; + urb_priv->length = size; + urb_priv->td_cnt = 0; + urb_priv->state = 0; + urb_priv->ed = ed; + urb_priv->wait = NULL; + + /* allocate the TDs */ + for (i = 0; i < size; i++) { + OHCI_ALLOC (urb_priv->td[i], sizeof (td_t)); + if (!urb_priv->td[i]) { + usb_dec_dev_use (urb->dev); + urb_rm_priv (urb); + return -ENOMEM; + } + } + spin_lock_irqsave (&usb_ed_lock, flags); + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_rm_priv(urb); + usb_dec_dev_use (urb->dev); + return -EINVAL; + } + + /* for ISOC transfers calculate start frame index */ + if (urb->transfer_flags & USB_ISO_ASAP) { + urb->start_frame = ((ed->state == ED_OPER)? (ed->last_iso + 1): + (ohci->hcca.frame_no + 10)) & 0xffff; + } + + td_submit_urb (urb); /* fill the TDs and link it to the ed */ + + if (ed->state != ED_OPER) /* link the ed into a chain if is not already */ + ep_link (ohci, ed); + spin_unlock_irqrestore (&usb_ed_lock, flags); + + urb->status = USB_ST_URB_PENDING; + // queue_urb(s, &urb->urb_list); - return 0; + return 0; } - - -/* - * ISO Interface designed by Randy Dunlap - */ - -static int sohci_get_current_frame_number(struct usb_device *usb_dev) { +/*-------------------------------------------------------------------------*/ - struct ohci * ohci = usb_dev->bus->hcpriv; +/* deactivate all TDs and remove the private part of the URB */ + +static int sohci_unlink_urb (urb_t * urb) +{ + unsigned int flags; + ohci_t * ohci; + DECLARE_WAITQUEUE (wait, current); - return ohci->hc_area->hcca.frame_no & 0xffff; + if (!urb) /* just to be sure */ + return -EINVAL; + +#ifdef DEBUG + urb_print (urb, "UNLINK", 1); +#endif + + ohci = (ohci_t *) urb->dev->bus->hcpriv; + + if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) + return rh_unlink_urb (urb); /* a request to the virtual root hub */ + + if (urb->hcpriv) { + if (urb->status == USB_ST_URB_PENDING) { /* URB active? */ + urb_priv_t * urb_priv = urb->hcpriv; + urb_priv->state = URB_DEL; + /* we want to delete the TDs of an URB from an ed + * request the deletion, it will be handled at the next USB-frame */ + urb_priv->wait = &wait; + + spin_lock_irqsave (&usb_ed_lock, flags); + ep_rm_ed (urb->dev, urb_priv->ed); + urb_priv->ed->state |= ED_URB_DEL; + spin_unlock_irqrestore (&usb_ed_lock, flags); + + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout (HZ / 10); /* wait until all TDs are deleted */ + remove_wait_queue (&op_wakeup, &wait); + } else + urb_rm_priv (urb); + usb_dec_dev_use (urb->dev); + } + return 0; } +/*-------------------------------------------------------------------------*/ -static int sohci_init_isoc(struct usb_device *usb_dev, unsigned int pipe, int frame_count, void *context, struct usb_isoc_desc **idp) { +/* allocate private data space for a usb device */ - struct usb_isoc_desc *id; - - *idp = NULL; +static int sohci_alloc_dev (struct usb_device *usb_dev) +{ + struct ohci_device * dev; - id = kmalloc (sizeof (struct usb_isoc_desc) + (sizeof (struct isoc_frame_desc) * frame_count), GFP_KERNEL); - if(!id) return -ENOMEM; - memset (id, 0, sizeof (struct usb_isoc_desc) + (sizeof (struct isoc_frame_desc) * frame_count)); -OHCI_DEBUG(printk("ISO alloc id: %p, size: %d\n", id, sizeof (struct usb_isoc_desc) + (sizeof (struct isoc_frame_desc) * frame_count));) - id->td = kmalloc (sizeof (void *) * frame_count, GFP_KERNEL); - if(!id->td) { - kfree (id); + dev = kmalloc (sizeof (*dev), GFP_KERNEL); + if (!dev) return -ENOMEM; - } - memset (id->td, 0, sizeof (void *) * frame_count); -OHCI_DEBUG(printk("ISO alloc id->td: %p, size: %d\n", id->td, sizeof (void *) * frame_count);) - - id->frame_count = frame_count; - id->frame_size = usb_maxpacket (usb_dev, pipe, usb_pipeout(pipe)); - id->start_frame = -1; - id->end_frame = -1; - id->usb_dev = usb_dev; - id->pipe = pipe; - id->context = context; - - *idp = id; - return 0; -} - -void print_int_eds(struct ohci * ohci); - -static int sohci_run_isoc(struct usb_isoc_desc *id, struct usb_isoc_desc *pr_id) { - - struct ohci * ohci = id->usb_dev->bus->hcpriv; - struct usb_ohci_td ** tdp = (struct usb_ohci_td **) id->td; - struct usb_hcd_ed hcd_ed; - struct usb_ohci_ed * ed; - int ix, frlen; - unsigned char *bufptr; - - if(pr_id) - id->start_frame = pr_id->end_frame + 1; - else { - switch(id->start_type) { - case START_ABSOLUTE: - break; - - case START_RELATIVE: - if(id->start_frame < START_FRAME_FUDGE) id->start_frame = START_FRAME_FUDGE; - id->start_frame += sohci_get_current_frame_number(id->usb_dev); - break; - - case START_ASAP: - id->start_frame = START_FRAME_FUDGE + sohci_get_current_frame_number(id->usb_dev); - break; - } - } + + memset (dev, 0, sizeof (*dev)); - id->start_frame &= 0xffff; - id->end_frame = (id->start_frame + id->frame_count - 1) & 0xffff; + usb_dev->hcpriv = dev; - id->prev_completed_frame = 0; - id->cur_completed_frame = 0; - if (id->frame_spacing <= 0) id->frame_spacing = 1; - - bufptr = id->data; - - usb_pipe_to_hcd_ed(id->usb_dev, id->pipe, &hcd_ed); - hcd_ed.type = ISO; - ed = usb_ohci_add_ep(id->usb_dev, &hcd_ed, 1, 1); - OHCI_DEBUG( printk("USB HC ISO>>>: ed: %x-%x: (%d tds)\n", (unsigned int) ed, ed->hwINFO, id->frame_count);) - - for (ix = 0; ix < id->frame_count; ix++) { - frlen = (id->frames[ix].frame_length > id->frame_size)? id->frame_size : id->frames[ix].frame_length; - printk("ISO run id->td: %p \n", &tdp[ix]); - tdp[ix] = ohci_trans_req(ohci, ed, id->start_frame + ix , NULL, bufptr, frlen, (__OHCI_BAG) ix, (__OHCI_BAG) id, - (usb_pipeout(id->pipe))?ISO_OUT:ISO_IN, - sohci_iso_handler); - bufptr += frlen; - } - - if (ED_STATE(ed) != ED_OPER) ohci_link_ed(ohci, ed); - print_int_eds(ohci); return 0; } -static int sohci_kill_isoc(struct usb_isoc_desc *id) { +/*-------------------------------------------------------------------------*/ - struct usb_ohci_ed *ed = NULL; - struct usb_ohci_td **td = id->td; - int i; - - for (i = 0; i < id->frame_count; i++) { - if(td[i]) { - td[i]->type |= DEL; - ed = td[i]->ed; - OHCI_DEBUG(printk(" %d", i);) - td[i] = NULL; +/* free private data space of usb device */ + +static int sohci_free_dev (struct usb_device * usb_dev) +{ + unsigned int flags; + int i, cnt = 0; + ed_t * ed; + DECLARE_WAITQUEUE (wait, current); + struct ohci_device * dev = usb_to_ohci (usb_dev); + ohci_t * ohci = usb_dev->bus->hcpriv; + + if (!dev) return 0; + + if (usb_dev->devnum >= 0) { + + /* delete all TDs of all EDs */ + spin_lock_irqsave (&usb_ed_lock, flags); + for(i = 0; i < NUM_EDS; i++) { + ed = &(dev->ed[i]); + if (ed->state != ED_NEW) { + if (ed->state == ED_OPER) ep_unlink (ohci, ed); + ep_rm_ed (usb_dev, ed); + ed->state = ED_DEL; + cnt++; + } + } + spin_unlock_irqrestore (&usb_ed_lock, flags); + + if (cnt > 0) { + dev->wait = &wait; + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout (HZ / 10); + remove_wait_queue (&op_wakeup, &wait); } - } - if(ed) usb_ohci_rm_ep(id->usb_dev, ed, NULL, NULL, NULL, TD_RM); - - id->start_frame = -1; + } + kfree (dev); + return 0; } +/*-------------------------------------------------------------------------*/ -static void sohci_free_isoc(struct usb_isoc_desc *id) { +/* tell us the current USB frame number */ - if(id->start_frame >= 0) sohci_kill_isoc(id); - kfree(id->td); - kfree(id); +static int sohci_get_current_frame_number (struct usb_device *usb_dev) +{ + ohci_t * ohci = usb_dev->bus->hcpriv; + + return ohci->hcca.frame_no; } +/*-------------------------------------------------------------------------*/ + struct usb_operations sohci_device_operations = { sohci_alloc_dev, sohci_free_dev, - sohci_control_msg, - sohci_bulk_msg, - sohci_request_irq, - sohci_release_irq, - sohci_request_bulk, - sohci_terminate_bulk, sohci_get_current_frame_number, - sohci_init_isoc, - sohci_free_isoc, - sohci_run_isoc, - sohci_kill_isoc + sohci_submit_urb, + sohci_unlink_urb }; - -/****** - *** ED handling functions - ************************************/ - -/* just for debugging; prints all 32 branches of the int ed tree inclusive iso eds*/ -void print_int_eds(struct ohci * ohci) {int i; __u32 * ed_p; - for(i= 0; i < 32; i++) { - OHCI_DEBUG(printk("branch int %2d(%2x): ", i,i); ) - ed_p = &(ohci->hc_area->hcca.int_table[i]); - while (*ed_p != 0) { - OHCI_DEBUG(printk("ed: %4x; ", (((struct usb_ohci_ed *)bus_to_virt(*ed_p))->hwINFO));) - ed_p = &(((struct usb_ohci_ed *)bus_to_virt(*ed_p))->hwNextED); - } - OHCI_DEBUG(printk("\n");) - } -} +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ -/* - * search for the right branch to insert an interrupt ed into the int tree - * do some load ballancing +/* search for the right branch to insert an interrupt ed into the int tree + * do some load ballancing; * returns the branch and - * sets the interval to interval = 2^integer(ld(interval)) - * */ - -static int usb_ohci_int_ballance(struct ohci * ohci, int * interval, int load) { + * sets the interval to interval = 2^integer (ld (interval)) */ - int i,j; - - j = 0; /* search for the least loaded interrupt endpoint branch of all 32 branches */ - for(i=0; i< 32; i++) if(ohci->ohci_int_load[j] > ohci->ohci_int_load[i]) j=i; - - for(i= 0; ((*interval >> i) > 1 ) && (i < 5); i++ ); /* interval = 2^int(ld(interval)) */ - *interval = 1 << i; - - j = j % (*interval); - for(i=j; i< 32; i+=(*interval)) ohci->ohci_int_load[i] += load; +static int ep_int_ballance (ohci_t * ohci, int interval, int load) +{ + int i, branch = 0; + /* search for the least loaded interrupt endpoint branch of all 32 branches */ + for (i = 0; i < 32; i++) + if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i; - OHCI_DEBUG(printk("USB HC new int ed on pos %d of interval %d \n",j, *interval);) - - return j; + branch = branch % interval; + for (i = branch; i < 32; i += interval) ohci->ohci_int_load [i] += load; + + return branch; +} + +/*-------------------------------------------------------------------------*/ + +/* 2^int( ld (inter)) */ + +static int ep_2_n_interval (int inter) +{ + int i; + for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++); + return 1 << i; } +/*-------------------------------------------------------------------------*/ + /* the int tree is a binary tree * in order to process it sequentially the indexes of the branches have to be mapped - * the mapping reverses the bits of a word of num_bits length - * */ -static int rev(int num_bits, int word) { - int i; - int wout = 0; + * the mapping reverses the bits of a word of num_bits length */ + +static int ep_rev (int num_bits, int word) +{ + int i, wout = 0; - for(i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1)); + for (i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1)); return wout; } -/* get the ed from the endpoint / usb_device address */ - -struct usb_ohci_ed * ohci_find_ep(struct usb_device *usb_dev, struct usb_hcd_ed *hcd_ed) { - return &(usb_to_ohci(usb_dev)->ed[(hcd_ed->endpoint << 1) | ((hcd_ed->type == CTRL)? 0:hcd_ed->out)]); -} +/*-------------------------------------------------------------------------*/ /* link an ed into one of the HC chains */ -static int ohci_link_ed(struct ohci * ohci, struct usb_ohci_ed *ed) { +static int ep_link (ohci_t * ohci, ed_t * edi) +{ int int_branch; int i; int inter; int interval; int load; __u32 * ed_p; + volatile ed_t * ed = edi; - ED_setSTATE(ed, ED_OPER); + ed->state = ED_OPER; - switch(ED_TYPE(ed)) { + switch (ed->type) { case CTRL: ed->hwNextED = 0; - if(ohci->ed_controltail == NULL) { - writel(virt_to_bus(ed), &ohci->regs->ed_controlhead); - } - else { - ohci->ed_controltail->hwNextED = virt_to_bus(ed); + if (ohci->ed_controltail == NULL) { + writel (virt_to_bus (ed), &ohci->regs->ed_controlhead); + } else { + ohci->ed_controltail->hwNextED = cpu_to_le32 (virt_to_bus (ed)); } ed->ed_prev = ohci->ed_controltail; ohci->ed_controltail = ed; @@ -631,67 +554,71 @@ case BULK: ed->hwNextED = 0; - if(ohci->ed_bulktail == NULL) { - writel(virt_to_bus(ed), &ohci->regs->ed_bulkhead); - } - else { - ohci->ed_bulktail->hwNextED = virt_to_bus(ed); + if (ohci->ed_bulktail == NULL) { + writel (virt_to_bus (ed), &ohci->regs->ed_bulkhead); + } else { + ohci->ed_bulktail->hwNextED = cpu_to_le32 (virt_to_bus (ed)); } ed->ed_prev = ohci->ed_bulktail; ohci->ed_bulktail = ed; break; case INT: - interval = ed->int_period; load = ed->int_load; - int_branch = usb_ohci_int_ballance(ohci, &interval, load); + interval = ep_2_n_interval (ed->int_period); ed->int_interval = interval; + int_branch = ep_int_ballance (ohci, interval, load); ed->int_branch = int_branch; - for( i= 0; i < rev(6, interval); i += inter) { + for (i = 0; i < ep_rev (6, interval); i += inter) { inter = 1; - for(ed_p = &(ohci->hc_area->hcca.int_table[rev(5,i)+int_branch]); - (*ed_p != 0) && (((struct usb_ohci_ed *)bus_to_virt(*ed_p))->int_interval >= interval); - ed_p = &(((struct usb_ohci_ed *)bus_to_virt(*ed_p))->hwNextED)) - inter = rev(6,((struct usb_ohci_ed *)bus_to_virt(*ed_p))->int_interval); + for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) + int_branch]); + (*ed_p != 0) && (((ed_t *) bus_to_virt (le32_to_cpu (*ed_p)))->int_interval >= interval); + ed_p = &(((ed_t *) bus_to_virt (le32_to_cpu (*ed_p)))->hwNextED)) + inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpu (*ed_p)))->int_interval); ed->hwNextED = *ed_p; - *ed_p = virt_to_bus(ed); - OHCI_DEBUG(printk("int_link i: %2x, inter: %2x, ed: %4x\n", i, inter, ed->hwINFO);) + *ed_p = cpu_to_le32 (virt_to_bus (ed)); } +#ifdef DEBUG + ep_print_int_eds (ohci, "LINK_INT"); +#endif break; case ISO: - if(ohci->ed_isotail != NULL) { - ohci->ed_isotail->hwNextED = virt_to_bus(ed); + ed->hwNextED = 0; + ed->int_interval = 1; + if (ohci->ed_isotail != NULL) { + ohci->ed_isotail->hwNextED = cpu_to_le32 (virt_to_bus (ed)); ed->ed_prev = ohci->ed_isotail; - } - else { - for( i= 0; i < 32; i += inter) { + } else { + for ( i = 0; i < 32; i += inter) { inter = 1; - for(ed_p = &(ohci->hc_area->hcca.int_table[rev(5,i)]); + for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]); *ed_p != 0; - ed_p = &(((struct usb_ohci_ed *)bus_to_virt(*ed_p))->hwNextED)) - inter = rev(6,((struct usb_ohci_ed *)bus_to_virt(*ed_p))->int_interval); - *ed_p = virt_to_bus(ed); + ed_p = &(((ed_t *) bus_to_virt (le32_to_cpu (*ed_p)))->hwNextED)) + inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpu (*ed_p)))->int_interval); + *ed_p = cpu_to_le32 (virt_to_bus (ed)); } ed->ed_prev = NULL; } - ed->hwNextED = 0; ohci->ed_isotail = ed; +#ifdef DEBUG + ep_print_int_eds (ohci, "LINK_ISO"); +#endif break; - } - - - return 1; + } + return 0; } +/*-------------------------------------------------------------------------*/ + /* unlink an ed from one of the HC chains. * just the link to the ed is unlinked. * the link from the ed still points to another operational ed or 0 - * so the HC can eventually finish the processing of the unlinked ed - * */ -static int ohci_unlink_ed(struct ohci * ohci, struct usb_ohci_ed *ed) { + * so the HC can eventually finish the processing of the unlinked ed */ +static int ep_unlink (ohci_t * ohci, ed_t * ed) +{ int int_branch; int i; int inter; @@ -699,125 +626,129 @@ __u32 * ed_p; - switch(ED_TYPE(ed)) { - case CTRL: - if(ed->ed_prev == NULL) { - writel(ed->hwNextED, &ohci->regs->ed_controlhead); - } - else { - ed->ed_prev->hwNextED = ed->hwNextED; - } - if(ohci->ed_controltail == ed) { - ohci->ed_controltail = ed->ed_prev; - } - else { - ((struct usb_ohci_ed *)bus_to_virt(ed->hwNextED))->ed_prev = ed->ed_prev; - } - break; + switch (ed->type) { + case CTRL: + if (ed->ed_prev == NULL) { + writel (le32_to_cpu (ed->hwNextED), &ohci->regs->ed_controlhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if(ohci->ed_controltail == ed) { + ohci->ed_controltail = ed->ed_prev; + } else { + ((ed_t *) bus_to_virt (le32_to_cpu (ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; - case BULK: - if(ed->ed_prev == NULL) { - writel(ed->hwNextED, &ohci->regs->ed_bulkhead); - } - else { - ed->ed_prev->hwNextED = ed->hwNextED; - } - if(ohci->ed_bulktail == ed) { - ohci->ed_bulktail = ed->ed_prev; - } - else { - ((struct usb_ohci_ed *)bus_to_virt(ed->hwNextED))->ed_prev = ed->ed_prev; - } - break; + case BULK: + if (ed->ed_prev == NULL) { + writel (le32_to_cpu (ed->hwNextED), &ohci->regs->ed_bulkhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_bulktail == ed) { + ohci->ed_bulktail = ed->ed_prev; + } else { + ((ed_t *) bus_to_virt (le32_to_cpu (ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; case INT: int_branch = ed->int_branch; interval = ed->int_interval; - for( i= 0; i < rev(6,interval); i += inter) { - for(ed_p = &(ohci->hc_area->hcca.int_table[rev(5,i)+int_branch]), inter = 1; + for (i = 0; i < ep_rev (6, interval); i += inter) { + for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) + int_branch]), inter = 1; (*ed_p != 0) && (*ed_p != ed->hwNextED); - ed_p = &(((struct usb_ohci_ed *)bus_to_virt(*ed_p))->hwNextED), - inter = rev(6, ((struct usb_ohci_ed *)bus_to_virt(*ed_p))->int_interval)) { - if(((struct usb_ohci_ed *)bus_to_virt(*ed_p)) == ed) { + ed_p = &(((ed_t *) bus_to_virt (le32_to_cpu (*ed_p)))->hwNextED), + inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpu (*ed_p)))->int_interval)) { + if(((ed_t *) bus_to_virt (le32_to_cpu (*ed_p))) == ed) { *ed_p = ed->hwNextED; break; } } - OHCI_DEBUG(printk("int_link i: %2x, inter: %2x, ed: %4x\n", i, inter, ed->hwINFO);) } - for(i=int_branch; i< 32; i+=interval) ohci->ohci_int_load[i] -= ed->int_load; - break; + for (i = int_branch; i < 32; i += interval) ohci->ohci_int_load[i] -= ed->int_load; +#ifdef DEBUG + ep_print_int_eds (ohci, "UNLINK_INT"); +#endif break; case ISO: - if(ohci->ed_isotail == ed) + if (ohci->ed_isotail == ed) ohci->ed_isotail = ed->ed_prev; - if(ed->hwNextED != 0) - ((struct usb_ohci_ed *)bus_to_virt(ed->hwNextED))->ed_prev = ed->ed_prev; + if (ed->hwNextED != 0) + ((ed_t *) bus_to_virt (le32_to_cpu (ed->hwNextED)))->ed_prev = ed->ed_prev; - if(ed->ed_prev != NULL) { + if (ed->ed_prev != NULL) { ed->ed_prev->hwNextED = ed->hwNextED; - } - else { - for( i= 0; i < 32; i += inter) { + } else { + for (i = 0; i < 32; i += inter) { inter = 1; - for(ed_p = &(ohci->hc_area->hcca.int_table[rev(5,i)]); + for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]); *ed_p != 0; - ed_p = &(((struct usb_ohci_ed *)bus_to_virt(*ed_p))->hwNextED)) { - inter = rev(6,((struct usb_ohci_ed *)bus_to_virt(*ed_p))->int_interval); - if(((struct usb_ohci_ed *)bus_to_virt(*ed_p)) == ed) { + ed_p = &(((ed_t *) bus_to_virt (le32_to_cpu (*ed_p)))->hwNextED)) { + inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpu (*ed_p)))->int_interval); + if(((ed_t *) bus_to_virt (le32_to_cpu (*ed_p))) == ed) { *ed_p = ed->hwNextED; break; } } } } +#ifdef DEBUG + ep_print_int_eds (ohci, "UNLINK_ISO"); +#endif break; } - ED_setSTATE(ed, ED_UNLINK); - return 1; + ed->state = ED_UNLINK; + return 0; } -spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED; + +/*-------------------------------------------------------------------------*/ /* add/reinit an endpoint; this should be done once at the usb_set_configuration command, * but the USB stack is a little bit stateless so we do it at every transaction * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK * in all other cases the state is left unchanged - * the ed info fields are setted anyway even though they should not change - * */ -struct usb_ohci_ed *usb_ohci_add_ep(struct usb_device * usb_dev, struct usb_hcd_ed * hcd_ed, int interval, int load) { - - // struct ohci * ohci = usb_dev->bus->hcpriv; - struct usb_ohci_td * td; - struct usb_ohci_ed * ed; - - int ed_state; + * the ed info fields are setted anyway even though most of them should not change */ - spin_lock(&usb_ed_lock); +static ed_t * ep_add_ed (struct usb_device * usb_dev, unsigned int pipe, int interval, int load) +{ + ohci_t * ohci = usb_dev->bus->hcpriv; + td_t * td; + volatile ed_t * ed; + + + spin_lock (&usb_ed_lock); - ed = ohci_find_ep(usb_dev, hcd_ed); + ed = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | + (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]); - ed_state = ED_STATE(ed); /* store state of ed */ - OHCI_DEBUG(printk("USB HC add ed %x: %x :state: %x\n", ed->hwINFO, (unsigned int ) ed, ed_state); ) - if (ed_state == ED_NEW) { - OHCI_ALLOC(td, sizeof(*td)); /* dummy td; end of td list for ed */ - ed->hwTailP = virt_to_bus(td); + if((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) + return NULL; /* pending delete request */ + + if (ed->state == ED_NEW) { + ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ + OHCI_ALLOC (td, sizeof (*td)); /* dummy td; end of td list for ed */ + if(!td) return NULL; /* out of memory */ + ed->hwTailP = cpu_to_le32 (virt_to_bus (td)); ed->hwHeadP = ed->hwTailP; - ed_state = ED_UNLINK; + ed->state = ED_UNLINK; + ed->type = usb_pipetype (pipe); + usb_to_ohci (usb_dev)->ed_cnt++; } - - ed->hwINFO = hcd_ed->function - | hcd_ed->endpoint << 7 - | (hcd_ed->type == ISO? 0x8000 : 0) - | (hcd_ed->type == CTRL? 0:(hcd_ed->out == 1? 0x800 : 0x1000 )) - | hcd_ed->slow << 13 - | hcd_ed->maxpack << 16 - | hcd_ed->type << 27 - // | 1 << 14 - | ed_state << 29 ; - - if (ED_TYPE(ed) == INT && ed_state == ED_UNLINK) { + + ohci->dev[usb_pipedevice (pipe)] = usb_dev; + + ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe) + | usb_pipeendpoint (pipe) << 7 + | (usb_pipeisoc (pipe)? 0x8000: 0) + | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) + | usb_pipeslow (pipe) << 13 + | usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16); + + if (ed->type == INT && ed->state == ED_UNLINK) { ed->int_period = interval; ed->int_load = load; } @@ -826,569 +757,839 @@ return ed; } - +/*-------------------------------------------------------------------------*/ -/***** - * Request the removal of an endpoint - * +/* request the removal of an endpoint * put the ep on the rm_list and request a stop of the bulk or ctrl list - * real removal is done at the next start of frame (SOF) hardware interrupt - * the dummy td carries the essential information (handler, proc queue, ...) - * if(send & TD_RM) then just the TD witch have (TD->type & DEL) set will be removed - * otherwise all TDs including the dummy TD of the ED will be removed - */ -int usb_ohci_rm_ep(struct usb_device * usb_dev, struct usb_ohci_ed *ed, f_handler handler, __OHCI_BAG lw0, __OHCI_BAG lw1, int send) + * real removal is done at the next start of frame (SOF) hardware interrupt */ + +static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed) { - unsigned int flags; - - struct ohci * ohci = usb_dev->bus->hcpriv; - struct usb_ohci_td *td; - - OHCI_DEBUG(printk("USB HC remove ed %x: %x :\n", ed->hwINFO, (unsigned int ) ed); ) + unsigned int frame; + ohci_t * ohci = usb_dev->bus->hcpriv; - spin_lock_irqsave(&usb_ed_lock, flags); - ed->hwINFO |= OHCI_ED_SKIP; - writel( OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */ - - - if(send & TD_RM) { /* delete selected TDs */ - ED_setSTATE(ed, ED_TD_DEL); - } - else { /* delete all TDS */ - if(ED_STATE(ed) == ED_OPER) ohci_unlink_ed(ohci, ed); - td = (struct usb_ohci_td *) bus_to_virt(ed->hwTailP); - td->lw0 = lw0; - td->lw1 = lw1; - td->ed = ed; - td->hwINFO = TD_CC; - td->handler = handler; - td->type = send; - ED_setSTATE(ed, ED_DEL); - } - ed->ed_prev = ohci->ed_rm_list; - ohci->ed_rm_list = ed; - - switch(ED_TYPE(ed)) { - case CTRL: - writel_mask(~(0x01<<4), &ohci->regs->control); /* stop CTRL list */ + if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) return; + + ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP); + + writel (OHCI_INTR_SF, &ohci->regs->intrstatus); + writel (OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */ + + frame = ohci->hcca.frame_no & 0x1; + ed->ed_rm_list = ohci->ed_rm_list[frame]; + ohci->ed_rm_list[frame] = ed; + + switch (ed->type) { + case CTRL: /* stop CTRL list */ + writel (ohci->hc_control &= ~(0x01 << 4), &ohci->regs->control); break; - case BULK: - writel_mask(~(0x01<<5), &ohci->regs->control); /* stop BULK list */ + case BULK: /* stop BULK list */ + writel (ohci->hc_control &= ~(0x01 << 5), &ohci->regs->control); break; } - - spin_unlock_irqrestore(&usb_ed_lock, flags); - - return 1; } - -/* remove all endpoints of a function (device) - * just the last ed sends a reply - * the last ed is ed0 as there always should be an ep0 - * */ -int usb_ohci_rm_function(struct usb_device * usb_dev, f_handler handler,__OHCI_BAG tw0, __OHCI_BAG tw1) -{ - struct usb_ohci_ed *ed; - int i; - int cnt = 0; - - - for(i = NUM_EDS - 1 ; i >= 0; i--) { - ed = &(usb_to_ohci(usb_dev)->ed[i]); - if(ED_STATE(ed) != ED_NEW) { - OHCI_DEBUG(printk("USB RM FUNCTION ed: %4x;\n", ed->hwINFO);) - usb_ohci_rm_ep(usb_dev, ed, handler, tw0, tw1, i==0?SEND:0); - cnt++; - } - } - OHCI_DEBUG(printk("USB RM FUNCTION %d eds removed;\n", cnt);) - return cnt; -} +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ +/* prepare a TD */ +static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int type, int index) +{ + volatile td_t * td, * td_pt; + urb_priv_t * urb_priv = urb->hcpriv; -/****** - *** TD handling functions - ************************************/ - - -#define FILL_TD(INFO, DATA, LEN, LW0, LW1, TYPE, HANDLER) \ - OHCI_ALLOC(td_pt, sizeof(*td_pt)); \ - td_ret = (struct usb_ohci_td *) bus_to_virt(usb_ed->hwTailP & 0xfffffff0); \ - td_pt1 = td_ret; \ - td_pt1->ed = ed; \ - td_pt1->buffer_start = (DATA); \ - td_pt1->type = (TYPE); \ - td_pt1->handler = (HANDLER); \ - td_pt1->lw0 = (LW0); \ - td_pt1->lw1 = (LW1); \ - td_pt1->hwINFO = (INFO); \ - td_pt1->hwCBP = (((DATA)==NULL)||((LEN)==0))?0:virt_to_bus(DATA); \ - td_pt1->hwBE = (((DATA)==NULL)||((LEN)==0))?0:virt_to_bus((DATA) + (LEN) - 1); \ - td_pt1->hwNextTD = virt_to_bus(td_pt); \ - td_pt->hwNextTD = 0; \ - usb_ed->hwTailP = td_pt1->hwNextTD - -#define FILL_ISO_TD(INFO, DATA, LEN, LW0, LW1, TYPE, HANDLER) \ - OHCI_ALLOC(td_pt, sizeof(*td_pt)); \ - td_ret = (struct usb_ohci_td *) bus_to_virt(usb_ed->hwTailP & 0xfffffff0); \ - td_pt1 = td_ret; \ - td_pt1->ed = ed; \ - td_pt1->buffer_start = (DATA); \ - td_pt1->type = (TYPE); \ - td_pt1->handler = (HANDLER); \ - td_pt1->lw0 = (LW0); \ - td_pt1->lw1 = (LW1); \ - td_pt1->hwINFO = (INFO); \ - td_pt1->hwCBP = (((DATA)==NULL)||((LEN)==0))?0:(virt_to_bus(DATA) & 0xfffff000); \ - td_pt1->hwBE = (((DATA)==NULL)||((LEN)==0))?0:virt_to_bus((DATA) + (LEN) - 1); \ - td_pt1->hwNextTD = virt_to_bus(td_pt); \ - td_pt1->hwPSW[0] = (virt_to_bus(DATA) & 0xfff) | 0xe000; \ - td_pt->hwNextTD = 0; \ - usb_ed->hwTailP = td_pt1->hwNextTD - - -spinlock_t usb_req_lock = SPIN_LOCK_UNLOCKED; - -struct usb_ohci_td * ohci_trans_req(struct ohci * ohci, struct usb_ohci_ed * ed, int ctrl_len, - void *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1, unsigned int ed_type, f_handler handler) { - + if (index >= urb_priv->length) { + printk (KERN_ERR MODSTR "internal OHCI error: TD index > length\n"); + return; + } + + td_pt = urb_priv->td [index]; + /* fill the old dummy TD */ + td = (td_t *) bus_to_virt (le32_to_cpu (urb_priv->ed->hwTailP) & 0xfffffff0); + td->ed = urb_priv->ed; + td->index = index; + td->urb = urb; + td->hwINFO = cpu_to_le32 (info); + td->type = type; + if ((td->ed->type & 3) == PIPE_ISOCHRONOUS) { + td->hwCBP = cpu_to_le32 (((!data || !len)? + 0 : virt_to_bus (data)) & 0xFFFFF000); + td->ed->last_iso = info & 0xffff; + } else { + td->hwCBP = cpu_to_le32 (((!data || !len)? 0 : virt_to_bus (data))); + } + td->hwBE = cpu_to_le32 ((!data || !len )? 0: virt_to_bus (data + len - 1)); + td->hwNextTD = cpu_to_le32 (virt_to_bus (td_pt)); + td->hwPSW [0] = cpu_to_le16 ((virt_to_bus (data) & 0x0FFF) | 0xE000); + td_pt->hwNextTD = 0; + td->ed->hwTailP = td->hwNextTD; + urb_priv->td [index] = td; - unsigned int flags; - volatile struct usb_ohci_ed *usb_ed = ed; - volatile struct usb_ohci_td *td_pt; - volatile struct usb_ohci_td *td_pt1 = NULL; + td->next_dl_td = NULL; //td_pt; +} - struct usb_ohci_td *td_ret = NULL; - - int cnt = 0; +/*-------------------------------------------------------------------------*/ - - if(usb_ed == NULL ) return NULL; /* not known ep */ - - - spin_lock_irqsave(&usb_req_lock, flags); - - switch(ed_type) { - case BULK_IN: - while(data_len > 4096) - { - FILL_TD( TD_CC | TD_DP_IN | TD_T_TOGGLE, data, 4096, NULL, NULL, BULK_IN | ADD_LEN|(cnt++?0:ST_ADDR), NULL); - data += 4096; data_len -= 4096; - } - FILL_TD( TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE, data, data_len, lw0, lw1, BULK_IN |ADD_LEN|SEND|(cnt++?0:ST_ADDR), handler); - writel( OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ - break; - - case BULK_OUT: - while(data_len > 4096) - { - FILL_TD( TD_CC | TD_DP_OUT | TD_T_TOGGLE, data, 4096, NULL, NULL, BULK_OUT | ADD_LEN|(cnt++?0:ST_ADDR), NULL); - data += 4096; data_len -= 4096; - } - FILL_TD( TD_CC | TD_DP_OUT | TD_T_TOGGLE, data, data_len, lw0, lw1, BULK_OUT |ADD_LEN|SEND|(cnt++?0:ST_ADDR), handler); - writel( OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ - break; - - case INT_IN: - FILL_TD( TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE, data, data_len, lw0, lw1, INT_IN | ST_ADDR|ADD_LEN|SEND, handler); - break; - - case INT_OUT: - FILL_TD( TD_CC | TD_DP_OUT | TD_T_TOGGLE, data, data_len, lw0, lw1, INT_OUT | ST_ADDR|ADD_LEN|SEND, handler); - break; - - case CTRL_IN: - FILL_TD( TD_CC | TD_DP_SETUP | TD_T_DATA0, ctrl, ctrl_len, 0, 0, CTRL_SETUP |ST_ADDR, NULL); - if(data_len > 0) { - FILL_TD( TD_CC | TD_R | TD_DP_IN | TD_T_DATA1, data, data_len, 0, 0, CTRL_DATA_IN | ST_ADDR|ADD_LEN, NULL); - } - FILL_TD( TD_CC | TD_DP_OUT | TD_T_DATA1, NULL, 0, lw0, lw1, CTRL_STATUS_OUT |SEND, handler); - writel( OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ - break; - - case CTRL_OUT: - FILL_TD( TD_CC | TD_DP_SETUP | TD_T_DATA0, ctrl, ctrl_len, 0, 0, CTRL_SETUP |ST_ADDR, NULL); - if(data_len > 0) { - FILL_TD( TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1, data, data_len, 0, 0, CTRL_DATA_OUT | ST_ADDR|ADD_LEN, NULL); - } - FILL_TD( TD_CC | TD_DP_IN | TD_T_DATA1, NULL, 0, lw0, lw1, CTRL_STATUS_IN |SEND, handler); - writel( OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ - break; - - case ISO_IN: - FILL_ISO_TD( TD_CC|TD_ISO|(ctrl_len & 0xffff), data, data_len, lw0, lw1, ISO_IN | ST_ADDR|ADD_LEN|SEND, handler); - break; - - case ISO_OUT: - FILL_ISO_TD( TD_CC|TD_ISO|(ctrl_len & 0xffff), data, data_len, lw0, lw1, ISO_OUT | ST_ADDR|ADD_LEN|SEND, handler); - break; - } +/* prepare all TDs of a transfer */ + +static void td_submit_urb (urb_t * urb) +{ + urb_priv_t * urb_priv = urb->hcpriv; + ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv; + void * ctrl = urb->setup_packet; + void * data = urb->transfer_buffer; + int data_len = urb->transfer_buffer_length; + int cnt = 0; + __u32 info = 0; - spin_unlock_irqrestore(&usb_req_lock, flags); - return td_ret; -} + urb_priv->td_cnt = 0; + + switch (usb_pipetype (urb->pipe)) { + case PIPE_BULK: + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_DP_IN | TD_T_TOGGLE; + while(data_len > 4096) { + td_fill (info, data, 4096, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE; + td_fill (info, data, data_len, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); + cnt++; + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + break; + + case PIPE_INTERRUPT: + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE; + td_fill (info, data, data_len, urb, ST_ADDR | ADD_LEN, cnt++); + break; + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill (info, ctrl, 8, urb, ST_ADDR, cnt++); + if (data_len > 0) { + info = usb_pipeout (urb->pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + td_fill (info, data, data_len, urb, ADD_LEN, cnt++); + } + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill (info, NULL, 0, urb, 0, cnt++); + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + break; -/****** - *** Done List handling functions - ************************************/ -/* the HC halts an ED if an error occurs; - * on error move all pending tds of a transaction (from ed) onto the done list - * * */ -static struct usb_ohci_td * ohci_append_error_tds(struct usb_ohci_td * td_list, struct usb_ohci_td * td_rev) { - - struct usb_ohci_td * tdl; - struct usb_ohci_td * tdx; - struct usb_ohci_td * tdt; - int cc; - - tdl = (struct usb_ohci_td *) bus_to_virt( td_list->ed->hwHeadP & 0xfffffff0); - tdt = (struct usb_ohci_td *) bus_to_virt( td_list->ed->hwTailP); - cc = TD_CC_GET(td_list->hwINFO); - - for ( tdx = tdl; tdx != tdt; tdx = tdx->next_dl_td) { - if(tdx->type & SEND) break; - tdx->next_dl_td = bus_to_virt(tdx->hwNextTD & 0xfffffff0); - } - tdx->next_dl_td = td_rev; - td_list->ed->hwHeadP = (tdx->hwNextTD & 0xfffffff0) | (td_list->ed->hwHeadP & 0x2); - TD_CC_SET(tdx->hwINFO, cc); - return tdl; + case PIPE_ISOCHRONOUS: + for (cnt = 0; cnt < urb->number_of_packets; cnt++) { + td_fill (TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff), + (__u8 *) data + urb->iso_frame_desc[cnt].offset, + urb->iso_frame_desc[cnt].length, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); + } + break; + } +#ifdef DEBUG + if (urb_priv->length != cnt) + dbg (KERN_ERR MODSTR " ********* TD LENGTH %d != CNT %d\n", urb_priv->length, cnt); +#endif } +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + /* replies to the request have to be on a FIFO basis so * we reverse the reversed done-list */ -static struct usb_ohci_td * ohci_reverse_done_list(struct ohci * ohci) { - +static td_t * dl_reverse_done_list (ohci_t * ohci) +{ __u32 td_list_hc; - struct usb_ohci_td * td_rev = NULL; - struct usb_ohci_td * td_list = NULL; + td_t * td_rev = NULL; + td_t * td_list = NULL; + urb_priv_t * urb_priv = NULL; + unsigned int flags; - td_list_hc = ohci->hc_area->hcca.done_head & 0xfffffff0; - ohci->hc_area->hcca.done_head = 0; + spin_lock_irqsave (&usb_ed_lock, flags); + + td_list_hc = le32_to_cpu (ohci->hcca.done_head) & 0xfffffff0; + ohci->hcca.done_head = 0; + + while (td_list_hc) { + td_list = (td_t *) bus_to_virt (td_list_hc); - - while(td_list_hc) { - - td_list = (struct usb_ohci_td *) bus_to_virt(td_list_hc); - if(TD_CC_GET(td_list->hwINFO) && !(td_list->type & SEND)) - td_list->next_dl_td = ohci_append_error_tds(td_list, td_rev); - else - td_list->next_dl_td = td_rev; - + if (TD_CC_GET (le32_to_cpu (td_list->hwINFO))) { + urb_priv = (urb_priv_t *) td_list->urb->hcpriv; + dbg (KERN_DEBUG MODSTR "**** USB-error/status: %x : %p \n", + TD_CC_GET (le32_to_cpu (td_list->hwINFO)), td_list); + if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) { + if (urb_priv && ((td_list->index + 1) < urb_priv->length)) { + td_list->ed->hwHeadP = + (urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) | + (td_list->ed->hwHeadP & cpu_to_le32 (0x2)); + urb_priv->td_cnt += urb_priv->length - td_list->index - 1; + } else + td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2); + } + } + + td_list->next_dl_td = td_rev; td_rev = td_list; - td_list_hc = td_list->hwNextTD & 0xfffffff0; - - + td_list_hc = le32_to_cpu (td_list->hwNextTD) & 0xfffffff0; } - return td_list; + spin_unlock_irqrestore (&usb_ed_lock, flags); + return td_list; } -/* there are some pending requests to remove some of the eds - * we either process every td including the dummy td of these eds - * or just those marked with TD->type&DEL - * and link them to a list - * */ -static struct usb_ohci_td * usb_ohci_del_list(struct ohci * ohci) { - struct usb_ohci_ed * ed; - struct usb_ohci_td * td; - struct usb_ohci_td * td_tmp = NULL; - struct usb_ohci_td * td_list = NULL; - __u32 * td_hw; - - for(ed = ohci->ed_rm_list; ed != NULL; ed = ed->ed_prev) { - OHCI_DEBUG(printk("USB HC ed_rm_list: %4x :next : %p\n", ed->hwINFO, ed->ed_prev);) - - - for( td_hw = &(ed->hwHeadP); (*td_hw & 0xfffffff0) != ed->hwTailP; td_hw = &(td->hwNextTD)) { - td = (struct usb_ohci_td *)bus_to_virt(*td_hw & 0xfffffff0); - if((ED_STATE(ed) == ED_DEL) || (td->type & DEL)) { - *td_hw = td->hwNextTD; - if(td_list == NULL) - td_list = td; - else - td_tmp->next_dl_td = td; - td_tmp = td; - OHCI_DEBUG(printk("USB HC ed_rm_list: td: %4x\n", (unsigned int) td);) - td->next_dl_td = NULL; - } - } - if(ED_STATE(ed) == ED_DEL) { /* send dummy td */ - td = (struct usb_ohci_td *)bus_to_virt(*td_hw & 0xfffffff0); - td->ed = ed; - if(td_list == NULL) - td_list = td; - else - td_tmp->next_dl_td = td; - td_tmp = td; - td->type |= DEL_ED; - OHCI_DEBUG(printk("USB HC ed_rm_list: dummy (ED_DEL) td: %4x\n", (unsigned int) td);) - ED_setSTATE(td->ed, ED_DEL| ED_NEW); - td->next_dl_td = NULL; - } - if(ED_TYPE(ed) == CTRL) writel(0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */ - if(ED_TYPE(ed) == BULK) writel(0, &ohci->regs->ed_bulkcurrent); /* reset BULK list */ - - } - - writel_set((0x03<<4), &ohci->regs->control); /* start CTRL u. BULK list */ +/*-------------------------------------------------------------------------*/ - ohci->ed_rm_list = NULL; - +/* there are some pending requests to remove + * - some of the eds (if ed->state & ED_DEL (set by sohci_free_dev) + * - some URBs/TDs if urb_priv->state == URB_DEL */ - return td_list; -} +static void dl_del_list (ohci_t * ohci, unsigned int frame) +{ + unsigned int flags; + ed_t * ed; + __u32 edINFO; + td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP; + __u32 * td_p; + int ctrl = 0, bulk = 0; + + spin_lock_irqsave (&usb_ed_lock, flags); + for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) { + + tdTailP = bus_to_virt (le32_to_cpu (ed->hwTailP) & 0xfffffff0); + tdHeadP = bus_to_virt (le32_to_cpu (ed->hwHeadP) & 0xfffffff0); + edINFO = le32_to_cpu (ed->hwINFO); + td_p = &ed->hwHeadP; + + for (td = tdHeadP; td != tdTailP; td = td_next) { + urb_t * urb = td->urb; + urb_priv_t * urb_priv = td->urb->hcpriv; + + td_next = bus_to_virt (le32_to_cpu (td->hwNextTD) & 0xfffffff0); + if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) { + *td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3)); + if(++ (urb_priv->td_cnt) == urb_priv->length) + urb_rm_priv (urb); + } else { + td_p = &td->hwNextTD; + } + + } + if (ed->state & ED_DEL) { /* set by sohci_free_dev */ + struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]); + OHCI_FREE (tdTailP); /* free dummy td */ + ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); + ed->state = ED_NEW; + /* if all eds are removed wake up sohci_free_dev */ + if ((! --dev->ed_cnt) && dev->wait) { + add_wait_queue (&op_wakeup, dev->wait); + wake_up (&op_wakeup); + } + } + else { + ed->state &= ~ED_URB_DEL; + ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP); + } + + if ((ed->type & 3) == CTRL) ctrl |= 1; + if ((ed->type & 3) == BULK) bulk |= 1; + } + + if (ctrl) writel (0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */ + if (bulk) writel (0, &ohci->regs->ed_bulkcurrent); /* reset BULK list */ + if (!ohci->ed_rm_list[!frame]) /* start CTRL u. BULK list */ + writel (ohci->hc_control |= (0x03<<4), &ohci->regs->control); + ohci->ed_rm_list[frame] = NULL; + spin_unlock_irqrestore (&usb_ed_lock, flags); +} -/* all tds ever alive go through this loop - * requests are replied here - * the handler is the - * interface handler (blocking/non blocking) the real reply-handler - * is called in the non blocking interface routine - * */ -static int usb_ohci_done_list(struct ohci * ohci, struct usb_ohci_td * td_list) { +/*-------------------------------------------------------------------------*/ - struct usb_ohci_td * td_list_next = NULL; +/* td done list */ - int cc; - int i; +static void dl_done_list (ohci_t * ohci, td_t * td_list) +{ + td_t * td_list_next = NULL; + ed_t * ed; int dlen = 0; - - while(td_list) { + int cc = 0; + urb_t * urb; + urb_priv_t * urb_priv; + __u32 tdINFO, tdBE, tdCBP, edHeadP, edTailP; + __u16 tdPSW; + unsigned int flags; + + while (td_list) { td_list_next = td_list->next_dl_td; - - if(td_list->type & ST_ADDR) { /* remember start address of data buffer */ - td_list->ed->buffer_start = td_list->buffer_start; - td_list->ed->len = 0; - } - - if(td_list->type & ADD_LEN) { /* accumulate length of multi td transfers */ - if(td_list->hwINFO & TD_ISO) { - for(i= 0; i <= ((td_list->hwINFO >> 24) & 0x7); i++) - if((td_list->hwPSW[i] >> 12) < 0xE) td_list->ed->len += (td_list->hwPSW[i] & 0x3ff); - } - else { - if(td_list->hwBE != 0) { - dlen = (bus_to_virt(td_list->hwBE) - td_list->buffer_start + 1); - if(td_list->hwCBP == 0) - td_list->ed->len += dlen; + + urb = td_list->urb; + urb_priv = urb->hcpriv; + tdINFO = le32_to_cpu (td_list->hwINFO); + tdBE = le32_to_cpu (td_list->hwBE); + tdCBP = le32_to_cpu (td_list->hwCBP); + + ed = td_list->ed; + + if (td_list->type & ST_ADDR) + urb->actual_length = 0; + + if (td_list->type & ADD_LEN) { /* accumulate length of multi td transfers */ + if (tdINFO & TD_ISO) { + tdPSW = le16_to_cpu (td_list->hwPSW[0]); + cc = (tdPSW >> 12) & 0xF; + if (cc < 0xE) { + if (usb_pipeout(urb->pipe)) { + dlen = urb->iso_frame_desc[td_list->index].length; + } else { + dlen = tdPSW & 0x3ff; + } + urb->actual_length += dlen; + urb->iso_frame_desc[td_list->index].actual_length = dlen; + if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN)) + cc = TD_CC_NOERROR; + + urb->iso_frame_desc[td_list->index].status = cc_to_error[cc]; + } + } else { + if (tdBE != 0) { + dlen = (bus_to_virt (tdBE) - urb->transfer_buffer + 1); + if (td_list->hwCBP == 0) + urb->actual_length += dlen; else - td_list->ed->len += (bus_to_virt(td_list->hwCBP) - td_list->buffer_start); + urb->actual_length += (bus_to_virt(tdCBP) - urb->transfer_buffer); } } } /* error code of transfer */ - cc = (ED_STATE(td_list->ed) == ED_DEL || (td_list->type & DEL))? USB_ST_REMOVED : (USB_ST_CRC*TD_CC_GET(td_list->hwINFO)); - - if(td_list->type & DEL_ED) ED_setSTATE(td_list->ed, ED_NEW); /* remove ed */ - - if((td_list->type & SEND) && (ED_STATE(td_list->ed) != ED_STOP) && (td_list->handler)) { /* send the reply */ - td_list->handler((void *) ohci, - td_list, - td_list->ed->buffer_start, - td_list->ed->len, - dlen, - cc, - td_list->lw0, - td_list->lw1); - OHCI_DEBUG(if(cc != TD_CC_NOERROR) printk("******* USB BUS error %x @ep %x\n", TD_CC_GET(td_list->hwINFO), td_list->ed->hwINFO);) - - } - if(ED_STATE(td_list->ed) == ED_TD_DEL) td_list->ed->hwINFO &= ~OHCI_ED_SKIP; - - if(((td_list->ed->hwHeadP & 0xfffffff0) == td_list->ed->hwTailP) && (ED_STATE(td_list->ed) > ED_UNLINK)) - ohci_unlink_ed(ohci, td_list->ed); /* unlink eds if they are not busy */ - - OHCI_FREE(td_list); + cc = TD_CC_GET (tdINFO); + if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN)) + cc = TD_CC_NOERROR; + if (++(urb_priv->td_cnt) == urb_priv->length) { + if (urb_priv->state != URB_DEL && !(ed->state & ED_DEL) && ed->state != ED_NEW) { + urb->status = cc_to_error[cc]; + sohci_return_urb (urb); + } else { + urb_rm_priv (urb); + } + } + + spin_lock_irqsave (&usb_ed_lock, flags); + if (ed->state != ED_NEW) { + edHeadP = le32_to_cpu (ed->hwHeadP) & 0xfffffff0; + edTailP = le32_to_cpu (ed->hwTailP); + + if((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); /* unlink eds if they are not busy */ + + } + spin_unlock_irqrestore (&usb_ed_lock, flags); + td_list = td_list_next; } - return 0; } -/****** - *** HC functions - ************************************/ + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, /* __u16 bcdUSB; v1.0 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x00, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +/* +For OHCI we need just the 2nd Byte, so we +don't need this constant byte-array + +static __u8 root_hub_hub_des[] = +{ + 0x00, * __u8 bLength; * + 0x29, * __u8 bDescriptorType; Hub-descriptor * + 0x02, * __u8 bNbrPorts; * + 0x00, * __u16 wHubCharacteristics; * + 0x00, + 0x01, * __u8 bPwrOn2pwrGood; 2ms * + 0x00, * __u8 bHubContrCurrent; 0 mA * + 0x00, * __u8 DeviceRemovable; *** 8 Ports max *** * + 0xff * __u8 PortPwrCtrlMask; *** 8 ports max *** * +}; +*/ + +/*-------------------------------------------------------------------------*/ + +/* prepare Interrupt pipe data; HUB INTERRUPT ENDPOINT */ + +static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len) +{ + int num_ports; + int i; + int ret; + int len; + + __u8 data[8]; + + num_ports = readl (&ohci->regs->roothub.a) & 0xff; + *(__u8 *) data = (readl (&ohci->regs->roothub.status) & 0x00030000) > 0? 1: 0; + ret = *(__u8 *) data; + + for ( i = 0; i < num_ports; i++) { + *(__u8 *) (data + i / 8) |= + ((readl (&ohci->regs->roothub.portstatus[i]) & 0x001f0000) > 0? 1: 0) << ((i + 1) % 8); + ret += *(__u8 *) (data + i / 8); + } + len = i/8 + 1; + + if (ret > 0) { + memcpy (rh_data, data, min (len, min (rh_len, sizeof(data)))); + return len; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */ + +static void rh_int_timer_do (unsigned long ptr) +{ + int len; + + urb_t * urb = (urb_t *) ptr; + ohci_t * ohci = urb->dev->bus->hcpriv; + + if(ohci->rh.send) { + len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length); + if (len > 0) { + urb->actual_length = len; +#ifdef DEBUG + urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe)); +#endif + if (urb->complete) urb->complete (urb); + } + } + rh_init_int_timer (urb); +} + +/*-------------------------------------------------------------------------*/ + +/* Root Hub INTs are polled by this timer */ + +static int rh_init_int_timer (urb_t * urb) +{ + ohci_t * ohci = urb->dev->bus->hcpriv; + + ohci->rh.interval = urb->interval; + init_timer (&ohci->rh.rh_int_timer); + ohci->rh.rh_int_timer.function = rh_int_timer_do; + ohci->rh.rh_int_timer.data = (unsigned long) urb; + ohci->rh.rh_int_timer.expires = + jiffies + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000; + add_timer (&ohci->rh.rh_int_timer); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#define OK(x) len = (x); break +#define WR_RH_STAT(x) writel((x), &ohci->regs->roothub.status) +#define WR_RH_PORTSTAT(x) writel((x), &ohci->regs->roothub.portstatus[wIndex-1]) +#define RD_RH_STAT readl(&ohci->regs->roothub.status) +#define RD_RH_PORTSTAT readl(&ohci->regs->roothub.portstatus[wIndex-1]) + +/* request to virtual root hub */ + +static int rh_submit_urb (urb_t * urb) +{ + struct usb_device * usb_dev = urb->dev; + ohci_t * ohci = usb_dev->bus->hcpriv; + unsigned int pipe = urb->pipe; + devrequest * cmd = (devrequest *) urb->setup_packet; + void * data = urb->transfer_buffer; + int leni = urb->transfer_buffer_length; + int len = 0; + int status = TD_CC_NOERROR; + + __u8 datab[16]; + __u8 * data_buf = datab; + + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + if (usb_pipeint(pipe)) { + + ohci->rh.urb = urb; + ohci->rh.send = 1; + ohci->rh.interval = urb->interval; + rh_init_int_timer(urb); + urb->status = cc_to_error [TD_CC_NOERROR]; + + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = le16_to_cpu (cmd->value); + wIndex = le16_to_cpu (cmd->index); + wLength = le16_to_cpu (cmd->length); + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *) data_buf = cpu_to_le16 (1); OK (2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data_buf = cpu_to_le16 (0); OK (2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data_buf = cpu_to_le16 (0); OK (2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data_buf = cpu_to_le32 (RD_RH_STAT & 0x7fff7fff); OK (4); + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case (RH_C_HUB_OVER_CURRENT): + WR_RH_STAT(RH_PS_OCIC); OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_POCI); OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); + case (RH_C_PORT_CONNECTION): + WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); + case (RH_C_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_PESC); OK (0); + case (RH_C_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); + case (RH_C_PORT_OVER_CURRENT): + WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); + case (RH_C_PORT_RESET): + WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); + case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ + if((RD_RH_PORTSTAT &1) != 0) WR_RH_PORTSTAT (RH_PS_PRS ); OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); + case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ + if((RD_RH_PORTSTAT &1) != 0) WR_RH_PORTSTAT (RH_PS_PES ); OK (0); + } + break; + + case RH_SET_ADDRESS: ohci->rh.devnum = wValue; OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min (leni, min (sizeof (root_hub_dev_des), wLength)); + data_buf = root_hub_dev_des; OK(len); + case (0x02): /* configuration descriptor */ + len = min (leni, min (sizeof (root_hub_config_des), wLength)); + data_buf = root_hub_config_des; OK(len); + case (0x03): /* string descriptors */ + default: + status = TD_CC_STALL; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + *(__u8 *) (data_buf+1) = 0x29; + *(__u32 *) (data_buf+2) = cpu_to_le32 (readl (&ohci->regs->roothub.a)); + *(__u8 *) data_buf = (*(__u8 *) (data_buf + 2) / 8) * 2 + 9; /* length of descriptor */ + + len = min (leni, min(*(__u8 *) data_buf, wLength)); + *(__u8 *) (data_buf+6) = 0; /* Root Hub needs no current from bus */ + if (*(__u8 *) (data_buf+2) < 8) { /* less than 8 Ports */ + *(__u8 *) (data_buf+7) = readl (&ohci->regs->roothub.b) & 0xff; + *(__u8 *) (data_buf+8) = (readl (&ohci->regs->roothub.b) & 0xff0000) >> 16; + } else { + *(__u32 *) (data_buf+7) = cpu_to_le32 (readl(&ohci->regs->roothub.b)); + } + OK (len); + + case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); + + case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); + + default: + status = TD_CC_STALL; + } + + dbg (KERN_DEBUG MODSTR "USB HC roothubstat1: %x \n", + readl ( &(ohci->regs->roothub.portstatus[0]) )); + dbg (KERN_DEBUG MODSTR "USB HC roothubstat2: %x \n", + readl ( &(ohci->regs->roothub.portstatus[1]) )); + + len = min(len, leni); + memcpy (data, data_buf, len); + urb->actual_length = len; + urb->status = cc_to_error [status]; + +#ifdef DEBUG + urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe)); +#endif + + if (urb->complete) urb->complete (urb); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int rh_unlink_urb (urb_t * urb) +{ + ohci_t * ohci = urb->dev->bus->hcpriv; + + ohci->rh.send = 0; + del_timer (&ohci->rh.rh_int_timer); + return 0; +} +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ /* reset the HC not the BUS */ -void reset_hc(struct ohci *ohci) { +static void hc_reset (ohci_t * ohci) +{ int timeout = 30; int smm_timeout = 50; /* 0,5 sec */ - if(readl(&ohci->regs->control) & 0x100) { /* SMM owns the HC */ - writel(0x08, &ohci->regs->cmdstatus); /* request ownership */ - printk("USB HC TakeOver from SMM\n"); - while(readl(&ohci->regs->control) & 0x100) { - wait_ms(10); + if (readl (&ohci->regs->control) & 0x100) { /* SMM owns the HC */ + writel (0x08, &ohci->regs->cmdstatus); /* request ownership */ + printk (KERN_DEBUG MODSTR "USB HC TakeOver from SMM\n"); + while (readl (&ohci->regs->control) & 0x100) { + wait_ms (10); if (--smm_timeout == 0) { - printk("USB HC TakeOver failed!\n"); + printk (KERN_ERR MODSTR "USB HC TakeOver failed!\n"); break; } } } - writel((1<<31), &ohci->regs->intrdisable); /* Disable HC interrupts */ - OHCI_DEBUG(printk("USB HC reset_hc: %x ; \n", readl(&ohci->regs->control));) - - writel(1, &ohci->regs->cmdstatus); /* HC Reset */ - while ((readl(&ohci->regs->cmdstatus) & 0x01) != 0) { /* 10us Reset */ + writel ((1 << 31), &ohci->regs->intrdisable); /* Disable HC interrupts */ + dbg (KERN_DEBUG MODSTR "USB HC reset_hc: %x ; \n", readl (&ohci->regs->control)); + /* this seems to be needed for the lucent controller on powerbooks.. */ + writel (0, &ohci->regs->control); /* Move USB to reset state */ + + writel (1, &ohci->regs->cmdstatus); /* HC Reset */ + while ((readl (&ohci->regs->cmdstatus) & 0x01) != 0) { /* 10us Reset */ if (--timeout == 0) { - printk("USB HC reset timed out!\n"); + printk (KERN_ERR MODSTR "USB HC reset timed out!\n"); return; } - udelay(1); + udelay (1); } } +/*-------------------------------------------------------------------------*/ -/* - * Start an OHCI controller, set the BUS operational - * set and enable interrupts - * connect the virtual root hub - * * */ +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ -int start_hc(struct ohci *ohci) +static int hc_start (ohci_t * ohci) { - unsigned int mask; - int fminterval; - - /* - * Tell the controller where the control and bulk lists are - * The lists are empty now. - */ - writel(0, &ohci->regs->ed_controlhead); - writel(0, &ohci->regs->ed_bulkhead); + unsigned int mask; + unsigned int fminterval; + struct usb_device * usb_dev; + struct ohci_device * dev; + + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + + writel (0, &ohci->regs->ed_controlhead); + writel (0, &ohci->regs->ed_bulkhead); - writel(virt_to_bus(&ohci->hc_area->hcca), &ohci->regs->hcca); /* a reset clears this */ + writel (virt_to_bus(&ohci->hcca), &ohci->regs->hcca); /* a reset clears this */ - writel((0xBF), &ohci->regs->control); /* USB Operational */ - fminterval = 0x2edf; - writel(((fminterval)*9)/10, &ohci->regs->periodicstart); - fminterval |= ((((fminterval -210) * 6)/7)<<16); - writel(fminterval, &ohci->regs->fminterval); - writel(0x628, &ohci->regs->lsthresh); - OHCI_DEBUG(printk("USB HC fminterval: %x \n", readl( &(ohci->regs->fminterval) )); ) - OHCI_DEBUG(printk("USB HC periodicstart: %x \n", readl( &(ohci->regs->periodicstart) )); ) - OHCI_DEBUG(printk("USB HC lsthresh: %x \n", readl( &(ohci->regs->lsthresh) )); ) - OHCI_DEBUG(printk("USB HC control: %x\n", readl(&ohci->regs->control)); ) - OHCI_DEBUG(printk("USB HC roothubstata: %x \n", readl( &(ohci->regs->roothub.a) )); ) - OHCI_DEBUG(printk("USB HC roothubstatb: %x \n", readl( &(ohci->regs->roothub.b) )); ) - OHCI_DEBUG(printk("USB HC roothubstatu: %x \n", readl( &(ohci->regs->roothub.status) )); ) - OHCI_DEBUG(printk("USB HC roothubstat1: %x \n", readl( &(ohci->regs->roothub.portstatus[0]) )); ) - OHCI_DEBUG(printk("USB HC roothubstat2: %x \n", readl( &(ohci->regs->roothub.portstatus[1]) )); ) + writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel (fminterval, &ohci->regs->fminterval); + writel (0x628, &ohci->regs->lsthresh); + /* Choose the interrupts we care about now, others later on demand */ mask = OHCI_INTR_MIE | OHCI_INTR_WDH | OHCI_INTR_SO; - - writel(mask, &ohci->regs->intrenable); - writel(mask, &ohci->regs->intrstatus); + + writel (ohci->hc_control = 0xBF, &ohci->regs->control); /* USB Operational */ + writel (mask, &ohci->regs->intrenable); + writel (mask, &ohci->regs->intrstatus); -#ifdef VROOTHUB - { /* connect the virtual root hub */ - struct usb_device * usb_dev; - struct ohci_device *dev; - - - usb_dev = usb_alloc_dev(NULL, ohci->bus); + + usb_dev = usb_alloc_dev (NULL, ohci->bus); if (!usb_dev) return -1; - dev = usb_to_ohci(usb_dev); - // usb_dev->bus = ohci->bus; + dev = usb_to_ohci (usb_dev); ohci->bus->root_hub = usb_dev; - dev->ohci = ohci; - usb_connect(usb_dev); - if(usb_new_device(usb_dev) != 0) { - usb_free_dev(usb_dev); + usb_connect (usb_dev); + if (usb_new_device (usb_dev) != 0) { + usb_free_dev (usb_dev); return -1; } - - } -#endif + return 0; } +/*-------------------------------------------------------------------------*/ + /* an interrupt happens */ -static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r) + +static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r) { - struct ohci *ohci = __ohci; - struct ohci_regs *regs = ohci->regs; + ohci_t * ohci = __ohci; + struct ohci_regs * regs = ohci->regs; int ints; - if((ohci->hc_area->hcca.done_head != 0) && !(ohci->hc_area->hcca.done_head & 0x01)) { + if ((ohci->hcca.done_head != 0) && !(ohci->hcca.done_head & 0x01)) { ints = OHCI_INTR_WDH; - } - else { - if((ints = (readl(®s->intrstatus) & readl(®s->intrenable))) == 0) - return; + } else { + if ((ints = (readl (®s->intrstatus) & readl (®s->intrenable))) == 0) + return; } - - OHCI_DEBUG(printk("USB HC interrupt: %x (%x) \n", ints, readl(®s->intrstatus));) - - if(ints & OHCI_INTR_WDH) { - writel(OHCI_INTR_WDH, ®s->intrdisable); - usb_ohci_done_list(ohci, ohci_reverse_done_list(ohci)); - writel(OHCI_INTR_WDH, ®s->intrenable); + dbg (KERN_DEBUG MODSTR "Interrupt: %x frame: %x \n", ints, ohci->hcca.frame_no); + + if (ints & OHCI_INTR_WDH) { + writel (OHCI_INTR_WDH, ®s->intrdisable); + dl_done_list (ohci, dl_reverse_done_list (ohci)); + writel (OHCI_INTR_WDH, ®s->intrenable); } - if(ints & OHCI_INTR_SO) { - printk("**** USB Schedule overrun "); - writel(OHCI_INTR_SO, ®s->intrenable); + if (ints & OHCI_INTR_SO) { + dbg (KERN_ERR MODSTR " USB Schedule overrun \n"); + writel (OHCI_INTR_SO, ®s->intrenable); } - if(ints & OHCI_INTR_SF) { - writel(OHCI_INTR_SF, ®s->intrdisable); - if(ohci->ed_rm_list != NULL) { - usb_ohci_done_list(ohci, usb_ohci_del_list(ohci)); + if (ints & OHCI_INTR_SF) { + unsigned int frame = (ohci->hcca.frame_no) & 1; + writel (OHCI_INTR_SF, ®s->intrdisable); + if (ohci->ed_rm_list[!frame] != NULL) { + dl_del_list (ohci, !frame); } + if (ohci->ed_rm_list[frame] != NULL) writel (OHCI_INTR_SF, ®s->intrenable); } - writel(ints, ®s->intrstatus); - writel(OHCI_INTR_MIE, ®s->intrenable); + writel (ints, ®s->intrstatus); + writel (OHCI_INTR_MIE, ®s->intrenable); } +/*-------------------------------------------------------------------------*/ + /* allocate OHCI */ -static struct ohci *alloc_ohci(void* mem_base) +static ohci_t * hc_alloc_ohci (void * mem_base) { int i; - struct ohci *ohci; - struct ohci_hc_area *hc_area; - struct usb_bus *bus; + ohci_t * ohci; + struct usb_bus * bus; - hc_area = (struct ohci_hc_area *) __get_free_pages(GFP_KERNEL, 1); - if (!hc_area) + ohci = (ohci_t *) __get_free_pages (GFP_KERNEL, 1); + if (!ohci) return NULL; - memset(hc_area, 0, sizeof(*hc_area)); - ohci = &hc_area->ohci; + memset (ohci, 0, sizeof (ohci_t)); + ohci->irq = -1; ohci->regs = mem_base; - ohci->hc_area = hc_area; - /* - * for load ballancing of the interrupt branches - */ + /* for load ballancing of the interrupt branches */ for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0; - for (i = 0; i < NUM_INTS; i++) hc_area->hcca.int_table[i] = 0; + for (i = 0; i < NUM_INTS; i++) ohci->hcca.int_table[i] = 0; - /* - * Store the end of control and bulk list eds. So, we know where we can add - * elements to these lists. - */ + /* end of control and bulk lists */ ohci->ed_isotail = NULL; ohci->ed_controltail = NULL; ohci->ed_bulktail = NULL; - bus = usb_alloc_bus(&sohci_device_operations); + bus = usb_alloc_bus (&sohci_device_operations); if (!bus) { - free_pages((unsigned int) ohci->hc_area, 1); + free_pages ((unsigned int) ohci, 1); return NULL; } @@ -1398,121 +1599,166 @@ return ohci; } +/*-------------------------------------------------------------------------*/ -/* - * De-allocate all resources.. - * */ -static void release_ohci(struct ohci *ohci) -{ - - OHCI_DEBUG(printk("USB HC release ohci \n");); - /* stop hc */ - - - - /* disconnect all devices */ - if(ohci->bus->root_hub) usb_disconnect(&ohci->bus->root_hub); +/* De-allocate all resources.. */ + +static void hc_release_ohci (ohci_t * ohci) +{ + dbg (KERN_DEBUG MODSTR "USB HC release ohci\n"); + + /* disconnect all devices */ + if (ohci->bus->root_hub) usb_disconnect (&ohci->bus->root_hub); - reset_hc(ohci); - writel(OHCI_USB_RESET, &ohci->regs->control); - wait_ms(10); + hc_reset (ohci); + writel (OHCI_USB_RESET, &ohci->regs->control); + wait_ms (10); if (ohci->irq >= 0) { - free_irq(ohci->irq, ohci); + free_irq (ohci->irq, ohci); ohci->irq = -1; } - usb_deregister_bus(ohci->bus); - usb_free_bus(ohci->bus); + usb_deregister_bus (ohci->bus); + usb_free_bus (ohci->bus); /* unmap the IO address space */ - iounmap(ohci->regs); + iounmap (ohci->regs); - free_pages((unsigned int) ohci->hc_area, 1); + free_pages ((unsigned int) ohci, 1); } +/*-------------------------------------------------------------------------*/ -/* - * Increment the module usage count, start the control thread and - * return success. - * */ -static int found_ohci(int irq, void* mem_base) +/* Increment the module usage count, start the control thread and + * return success. */ + +static int hc_found_ohci (int irq, void * mem_base) { - struct ohci *ohci; - OHCI_DEBUG(printk("USB HC found ohci: irq= %d membase= %x \n", irq, (int)mem_base);) + ohci_t * ohci; + dbg (KERN_DEBUG MODSTR "USB HC found: irq= %d membase= %x \n", irq, (int) mem_base); - ohci = alloc_ohci(mem_base); + ohci = hc_alloc_ohci (mem_base); if (!ohci) { return -ENOMEM; } - INIT_LIST_HEAD(&ohci->ohci_hcd_list); - list_add(&ohci->ohci_hcd_list, &ohci_hcd_list); + INIT_LIST_HEAD (&ohci->ohci_hcd_list); + list_add (&ohci->ohci_hcd_list, &ohci_hcd_list); - reset_hc(ohci); - writel(OHCI_USB_RESET, &ohci->regs->control); - wait_ms(10); - usb_register_bus(ohci->bus); + hc_reset (ohci); + writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control); + wait_ms (10); + usb_register_bus (ohci->bus); - if (request_irq(irq, ohci_interrupt, SA_SHIRQ, "ohci-usb", ohci) == 0) { + if (request_irq (irq, hc_interrupt, SA_SHIRQ, "ohci-usb", ohci) == 0) { ohci->irq = irq; - start_hc(ohci); + hc_start (ohci); return 0; } - printk(KERN_ERR "USB HC (ohci-hcd): request interrupt %d failed\n", irq); - release_ohci(ohci); + printk (KERN_ERR MODSTR "request interrupt %d failed\n", irq); + hc_release_ohci (ohci); return -EBUSY; } + +/*-------------------------------------------------------------------------*/ -static int start_ohci(struct pci_dev *dev) +static int hc_start_ohci (struct pci_dev * dev) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) unsigned int mem_base = dev->resource[0].start; +#else + unsigned int mem_base = dev->base_address[0]; + if (mem_base & PCI_BASE_ADDRESS_SPACE_IO) return -ENODEV; + mem_base &= PCI_BASE_ADDRESS_MEM_MASK; +#endif - pci_set_master(dev); - mem_base = (unsigned int) ioremap_nocache(mem_base, 4096); + pci_set_master (dev); + mem_base = (unsigned int) ioremap_nocache (mem_base, 4096); if (!mem_base) { - printk("Error mapping OHCI memory\n"); + printk (KERN_ERR MODSTR "Error mapping OHCI memory\n"); return -EFAULT; } - return found_ohci(dev->irq, (void *) mem_base); + return hc_found_ohci (dev->irq, (void *) mem_base); } +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PMAC_PBOOK + +/* On Powerbooks, put the controller into suspend mode when going + * to sleep, and do a resume when waking up. */ + +static int ohci_sleep_notify (struct pmu_sleep_notifier * self, int when) +{ + struct list_head * ohci_l; + ohci_t * ohci; + + for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) { + ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list); + + switch (when) { + case PBOOK_SLEEP_NOW: + disable_irq (ohci->irq); + writel (ohci->hc_control = OHCI_USB_SUSPEND, &ohci->regs->control); + wait_ms (10); + break; + case PBOOK_WAKE: + writel (ohci->hc_control = OHCI_USB_RESUME, &ohci->regs->control); + wait_ms (20); + writel (ohci->hc_control = 0xBF, &ohci->regs->control); + enable_irq (ohci->irq); + break; + } + } + return PBOOK_SLEEP_OK; +} + +static struct pmu_sleep_notifier ohci_sleep_notifier = { + ohci_sleep_notify, SLEEP_LEVEL_MISC, +}; +#endif /* CONFIG_PMAC_PBOOK */ +/*-------------------------------------------------------------------------*/ + #ifdef CONFIG_APM -static int handle_apm_event(apm_event_t event) +static int handle_apm_event (apm_event_t event) { static int down = 0; - struct ohci * ohci; - struct list_head *ohci_l; - + ohci_t * ohci; + struct list_head * ohci_l; + switch (event) { case APM_SYS_SUSPEND: case APM_USER_SUSPEND: if (down) { - printk(KERN_DEBUG "ohci: received extra suspend event\n"); + printk(KERN_DEBUG MODSTR "received extra suspend event\n"); break; } - for(ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) { - ohci = list_entry(ohci_l, struct ohci, ohci_hcd_list); - OHCI_DEBUG(printk("USB OHCI suspend: %p\n", ohci);) - writel(0xff, &ohci->regs->control); /* Suspend */ + for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) { + ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list); + dbg (KERN_DEBUG MODSTR "USB-Bus suspend: %p\n", ohci); + writel (ohci->hc_control = 0xFF, &ohci->regs->control); } - wait_ms(10); + wait_ms (10); down = 1; break; case APM_NORMAL_RESUME: case APM_CRITICAL_RESUME: if (!down) { - printk(KERN_DEBUG "ohci: received bogus resume event\n"); + printk (KERN_DEBUG MODSTR "received bogus resume event\n"); break; } - for(ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) { - ohci = list_entry(ohci_l, struct ohci, ohci_hcd_list); - OHCI_DEBUG(printk("USB OHCI resume: %p\n", ohci);) - writel(0x7f, &ohci->regs->control); /* Resume */ - wait_ms(20); - writel(0xBF, &ohci->regs->control); /* Operational */ + for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) { + ohci = list_entry(ohci_l, ohci_t, ohci_hcd_list); + dbg (KERN_DEBUG MODSTR "USB-Bus resume: %p\n", ohci); + writel (ohci->hc_control = 0x7F, &ohci->regs->control); + } + wait_ms (20); + for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) { + ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list); + writel (ohci->hc_control = 0xBF, &ohci->regs->control); } down = 0; break; @@ -1521,42 +1767,56 @@ } #endif +/*-------------------------------------------------------------------------*/ + #define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310 -int ohci_hcd_init(void) +int ohci_hcd_init (void) { int ret = -ENODEV; - struct pci_dev *dev = NULL; + struct pci_dev * dev = NULL; - while((dev = pci_find_class(PCI_CLASS_SERIAL_USB_OHCI, dev))) { - if (start_ohci(dev) >= 0) ret = 0; + while ((dev = pci_find_class (PCI_CLASS_SERIAL_USB_OHCI, dev))) { + if (hc_start_ohci(dev) >= 0) ret = 0; } #ifdef CONFIG_APM - apm_register_callback(&handle_apm_event); + apm_register_callback (&handle_apm_event); #endif - + +#ifdef CONFIG_PMAC_PBOOK + pmu_register_sleep_notifier (&ohci_sleep_notifier); +#endif return ret; } +/*-------------------------------------------------------------------------*/ + #ifdef MODULE -int init_module(void) +int init_module (void) { - return ohci_hcd_init(); + return ohci_hcd_init (); } -void cleanup_module(void) +/*-------------------------------------------------------------------------*/ + +void cleanup_module (void) { - struct ohci *ohci; + ohci_t * ohci; -# ifdef CONFIG_APM - apm_unregister_callback(&handle_apm_event); -# endif - while(!list_empty(&ohci_hcd_list)) { - ohci = list_entry(ohci_hcd_list.next, struct ohci, ohci_hcd_list); - list_del(&ohci->ohci_hcd_list); - INIT_LIST_HEAD(&ohci->ohci_hcd_list); - release_ohci(ohci); +#ifdef CONFIG_APM + apm_unregister_callback (&handle_apm_event); +#endif + +#ifdef CONFIG_PMAC_PBOOK + pmu_unregister_sleep_notifier (&ohci_sleep_notifier); +#endif + + while (!list_empty (&ohci_hcd_list)) { + ohci = list_entry (ohci_hcd_list.next, ohci_t, ohci_hcd_list); + list_del (&ohci->ohci_hcd_list); + INIT_LIST_HEAD (&ohci->ohci_hcd_list); + hc_release_ohci (ohci); } } #endif //MODULE diff -u --recursive --new-file v2.3.33/linux/drivers/usb/ohci-hcd.h linux/drivers/usb/ohci-hcd.h --- v2.3.33/linux/drivers/usb/ohci-hcd.h Wed Oct 27 16:34:12 1999 +++ linux/drivers/usb/ohci-hcd.h Fri Dec 17 16:49:45 1999 @@ -1,105 +1,91 @@ /* - * OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber - * - * 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) - * The layer on top of it, is for interfacing to the alternate-usb device-drivers. + * URB OHCI HCD (Host Controller Driver) for USB. * - * [ This is based on Linus' UHCI code and gregs OHCI fragments (0.03c source tree). ] - * [ Open Host Controller Interface driver for USB. ] - * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ] - * [ (C) Copyright 1999 Gregory P. Smith ] - * [ _Log: ohci-hcd.h,v _ - * [ Revision 1.1 1999/04/05 08:32:30 greg ] + *(C) Copyright 1999 Roman Weissgaerber * - * v4.0 1999/08/18 - * v2.1 1999/05/09 ep_addr correction, code clean up - * v2.0 1999/05/04 - * v1.0 1999/04/27 * ohci-hcd.h + * */ - -#define OHCI_DBG /* printk some debug information */ -#include +#define MODSTR "ohci: " -// #ifdef CONFIG_USB_OHCI_VROOTHUB -#define VROOTHUB -// #endif -/* enables virtual root hub - * (root hub will be managed by the hub controller - * hub.c of the alternate usb driver) - * must be on now - */ - - - -#ifdef OHCI_DBG -#define OHCI_DEBUG(X) X -#else -#define OHCI_DEBUG(X) -#endif - -/* for readl writel functions */ -#include -#include -struct usb_ohci_ed; -struct usb_ohci_td; -/* for ED and TD structures */ -typedef void * __OHCI_BAG; -typedef int (*f_handler )(void * ohci, struct usb_ohci_td *td, void *data, int data_len, int dlen, int status, __OHCI_BAG lw0, __OHCI_BAG lw1); +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ +#ifdef USB_ST_CRC /* status codes */ + /* No Error */ USB_ST_NOERROR, + /* CRC Error */ USB_ST_CRC, + /* Bit Stuff */ USB_ST_BITSTUFF, + /* Data Togg */ USB_ST_CRC, + /* Stall */ USB_ST_STALL, + /* DevNotResp */ USB_ST_NORESPONSE, + /* PIDCheck */ USB_ST_BITSTUFF, + /* UnExpPID */ USB_ST_BITSTUFF, + /* DataOver */ USB_ST_DATAOVERRUN, + /* DataUnder */ USB_ST_DATAUNDERRUN, + /* reservd */ USB_ST_NORESPONSE, + /* reservd */ USB_ST_NORESPONSE, + /* BufferOver */ USB_ST_BUFFEROVERRUN, + /* BuffUnder */ USB_ST_BUFFERUNDERRUN, + /* Not Access */ USB_ST_NORESPONSE, + /* Not Access */ USB_ST_NORESPONSE +}; + +#else /* error codes */ + /* No Error */ 0, + /* CRC Error */ -EILSEQ, + /* Bit Stuff */ -EPROTO, + /* Data Togg */ -EILSEQ, + /* Stall */ -EPIPE, + /* DevNotResp */ -ETIMEDOUT, + /* PIDCheck */ -EPROTO, + /* UnExpPID */ -EPROTO, + /* DataOver */ -EOVERFLOW, + /* DataUnder */ -EREMOTEIO, + /* reservd */ -ETIMEDOUT, + /* reservd */ -ETIMEDOUT, + /* BufferOver */ -ECOMM, + /* BuffUnder */ -ECOMM, + /* Not Access */ -ETIMEDOUT, + /* Not Access */ -ETIMEDOUT +}; +#define USB_ST_URB_PENDING -EINPROGRESS +#endif - + +struct ed; +struct td; +/* for ED and TD structures */ + /* ED States */ #define ED_NEW 0x00 #define ED_UNLINK 0x01 #define ED_OPER 0x02 -#define ED_STOP 0x03 #define ED_DEL 0x04 -#define ED_TD_DEL 0x05 -#define ED_RH 0x07 /* marker for RH ED */ - -#define ED_STATE(ed) (((ed)->hwINFO >> 29) & 0x7) -#define ED_setSTATE(ed,state) (ed)->hwINFO = ((ed)->hwINFO & ~(0x7 << 29)) | (((state)& 0x7) << 29) -#define ED_TYPE(ed) (((ed)->hwINFO >> 27) & 0x3) +#define ED_URB_DEL 0x08 -struct usb_ohci_ed { +/* usb_ohci_ed */ +typedef struct ed { __u32 hwINFO; __u32 hwTailP; __u32 hwHeadP; __u32 hwNextED; - - void * buffer_start; - unsigned int len; - struct usb_ohci_ed *ed_prev; + + struct ed * ed_prev; __u8 int_period; __u8 int_branch; __u8 int_load; __u8 int_interval; + __u8 state; + __u8 type; + __u16 last_iso; + struct ed * ed_rm_list; -} __attribute((aligned(32))); - -struct usb_hcd_ed { - int endpoint; - int function; - int out; - int type; - int slow; - int maxpack; -}; - -struct ohci_state { - int len; - int status; -}; - +} ed_t; /* TD info field */ @@ -138,23 +124,21 @@ #define TD_NOTACCESSED 0x0F -#define MAXPSW 2 +#define MAXPSW 1 -struct usb_ohci_td { +typedef struct td { __u32 hwINFO; __u32 hwCBP; /* Current Buffer Pointer */ __u32 hwNextTD; /* Next TD Pointer */ __u32 hwBE; /* Memory Buffer End Pointer */ __u16 hwPSW[MAXPSW]; - __u32 type; - void * buffer_start; - f_handler handler; - struct usb_ohci_ed *ed; - struct usb_ohci_td *next_dl_td; - __OHCI_BAG lw0; - __OHCI_BAG lw1; -} __attribute((aligned(32))); + __u8 type; + __u8 index; + struct ed * ed; + struct td * next_dl_td; + urb_t * urb; +} td_t; /* TD types */ @@ -162,39 +146,21 @@ #define INT 0x01 #define CTRL 0x02 #define ISO 0x00 -/* TD types with direction */ -#define BULK_IN 0x07 -#define BULK_OUT 0x03 -#define INT_IN 0x05 -#define INT_OUT 0x01 -#define CTRL_IN 0x06 -#define CTRL_OUT 0x02 -#define ISO_IN 0x04 -#define ISO_OUT 0x00 - -#define CTRL_SETUP 0x102 -#define CTRL_DATA_IN 0x206 -#define CTRL_DATA_OUT 0x202 -#define CTRL_STATUS_IN 0x306 -#define CTRL_STATUS_OUT 0x302 - -#define SEND 0x00001000 -#define ST_ADDR 0x00002000 -#define ADD_LEN 0x00004000 -#define DEL 0x00008000 -#define DEL_ED 0x00040000 -#define TD_RM 0x00080000 +#define SEND 0x01 +#define ST_ADDR 0x02 +#define ADD_LEN 0x04 +#define DEL 0x08 + #define OHCI_ED_SKIP (1 << 14) - - /* * The HCCA (Host Controller Communications Area) is a 256 byte * structure defined in the OHCI spec. that the host controller is * told the base address of. It must be 256-byte aligned. */ + #define NUM_INTS 32 /* part of the OHCI standard */ struct ohci_hcca { __u32 int_table[NUM_INTS]; /* Interrupt ED table */ @@ -206,14 +172,7 @@ /* - * This is the maximum number of root hub ports. I don't think we'll - * ever see more than two as that's the space available on an ATX - * motherboard's case, but it could happen. The OHCI spec allows for - * up to 15... (which is insane!) - * - * Although I suppose several "ports" could be connected directly to - * internal laptop devices such as a keyboard, mouse, camera and - * serial/parallel ports. hmm... That'd be neat. + * Maximum number of root hub ports. */ #define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ @@ -253,17 +212,6 @@ } roothub; } __attribute((aligned(32))); - -/* - * Read a MMIO register and re-write it after ANDing with (m) - */ -#define writel_mask(m, a) writel( (readl((__u32)(a))) & (__u32)(m), (__u32)(a) ) - -/* - * Read a MMIO register and re-write it after ORing with (b) - */ -#define writel_set(b, a) writel( (readl((__u32)(a))) | (__u32)(b), (__u32)(a) ) - /* * cmdstatus register */ #define OHCI_CLF 0x02 @@ -286,18 +234,103 @@ * Control register masks */ #define OHCI_USB_RESET 0 +#define OHCI_USB_RESUME (1 << 6) #define OHCI_USB_OPER (2 << 6) #define OHCI_USB_SUSPEND (3 << 6) + +/* Virtual Root HUB */ struct virt_root_hub { int devnum; /* Address of Root Hub endpoint */ - usb_device_irq handler; - void * dev_id; + void * urb; void * int_addr; int send; int interval; struct timer_list rh_int_timer; }; + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + +/* Root-Hub Register info */ + +#define RH_PS_CCS 0x00000001 +#define RH_PS_PES 0x00000002 +#define RH_PS_PSS 0x00000004 +#define RH_PS_POCI 0x00000008 +#define RH_PS_PRS 0x00000010 +#define RH_PS_PPS 0x00000100 +#define RH_PS_LSDA 0x00000200 +#define RH_PS_CSC 0x00010000 +#define RH_PS_PESC 0x00020000 +#define RH_PS_PSSC 0x00040000 +#define RH_PS_OCIC 0x00080000 +#define RH_PS_PRSC 0x00100000 + + +#define min(a,b) (((a)<(b))?(a):(b)) + + +/* urb */ +typedef struct +{ + ed_t * ed; + __u16 length; // number of tds associated with this request + __u16 td_cnt; // number of tds already serviced + int state; + void * wait; + td_t * td[0]; // list pointer to all corresponding TDs associated with this request + +} urb_priv_t; +#define URB_DEL 1 + /* * This is the full ohci controller description * @@ -306,86 +339,62 @@ */ -struct ohci { - int irq; - struct ohci_regs *regs; /* OHCI controller's memory */ - struct ohci_hc_area *hc_area; /* hcca, int ed-tree, ohci itself .. */ +typedef struct ohci { + struct ohci_hcca hcca; /* hcca */ + int irq; + struct ohci_regs * regs; /* OHCI controller's memory */ struct list_head ohci_hcd_list; /* list of all ohci_hcd */ + struct ohci * next; // chain of uhci device contexts + struct list_head urb_list; // list of all pending urbs + spinlock_t urb_list_lock; // lock to keep consistency + int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load ballancing)*/ - struct usb_ohci_ed * ed_rm_list; /* list of all endpoints to be removed */ - struct usb_ohci_ed * ed_bulktail; /* last endpoint of bulk list */ - struct usb_ohci_ed * ed_controltail; /* last endpoint of control list */ - struct usb_ohci_ed * ed_isotail; /* last endpoint of iso list */ + ed_t * ed_rm_list[2]; /* lists of all endpoints to be removed */ + ed_t * ed_bulktail; /* last endpoint of bulk list */ + ed_t * ed_controltail; /* last endpoint of control list */ + ed_t * ed_isotail; /* last endpoint of iso list */ int intrstatus; - struct ohci_rep_td *repl_queue; /* for internal requests */ - int rh_int_interval; - int rh_int_timer; - struct usb_bus *bus; + __u32 hc_control; /* copy of the hc control reg */ + struct usb_bus * bus; + struct usb_device * dev[128]; struct virt_root_hub rh; -}; +} ohci_t; #define NUM_TDS 0 /* num of preallocated transfer descriptors */ #define NUM_EDS 32 /* num of preallocated endpoint descriptors */ -struct ohci_hc_area { - struct ohci_hcca hcca; /* OHCI mem. mapped IO area 256 Bytes*/ - struct ohci ohci; - -}; struct ohci_device { - struct usb_device *usb; - atomic_t refcnt; - struct ohci *ohci; - struct usb_ohci_ed ed[NUM_EDS]; - unsigned long data[16]; + ed_t ed[NUM_EDS]; + int ed_cnt; + void * wait; }; -#define ohci_to_usb(ohci) ((ohci)->usb) +// #define ohci_to_usb(ohci) ((ohci)->usb) #define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv) /* hcd */ -struct usb_ohci_td * ohci_trans_req(struct ohci * ohci, struct usb_ohci_ed * ed, int cmd_len, void *cmd, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1, unsigned int type, f_handler handler); -struct usb_ohci_ed *usb_ohci_add_ep(struct usb_device * usb_dev, struct usb_hcd_ed * hcd_ed, int interval, int load); -int usb_ohci_rm_function(struct usb_device * usb_dev, f_handler handler, __OHCI_BAG lw0, __OHCI_BAG lw1); -int usb_ohci_rm_ep(struct usb_device * usb_dev, struct usb_ohci_ed *ed, f_handler handler, __OHCI_BAG lw0, __OHCI_BAG lw1, int send); -struct usb_ohci_ed * ohci_find_ep(struct usb_device * usb_dev, struct usb_hcd_ed *hcd_ed); - -/* roothub */ - -int root_hub_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, void *data, int len); -int root_hub_release_irq(struct usb_device *usb_dev, void * ed); -int root_hub_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id, void **handle); - -/* Root-Hub Register info */ - -#define RH_PS_CCS 0x00000001 -#define RH_PS_PES 0x00000002 -#define RH_PS_PSS 0x00000004 -#define RH_PS_POCI 0x00000008 -#define RH_PS_PRS 0x00000010 -#define RH_PS_PPS 0x00000100 -#define RH_PS_LSDA 0x00000200 -#define RH_PS_CSC 0x00010000 -#define RH_PS_PESC 0x00020000 -#define RH_PS_PSSC 0x00040000 -#define RH_PS_OCIC 0x00080000 -#define RH_PS_PRSC 0x00100000 - +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load); +static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed); +/* td */ +static void td_fill(unsigned int info, void * data, int len, urb_t * urb, int type, int index); +static void td_submit_urb(urb_t * urb); +/* root hub */ +static int rh_submit_urb(urb_t * urb); +static int rh_unlink_urb(urb_t * urb); +static int rh_init_int_timer(urb_t * urb); -#ifdef OHCI_DBG +#ifdef DEBUG #define OHCI_FREE(x) kfree(x); printk("OHCI FREE: %d: %4x\n", -- __ohci_free_cnt, (unsigned int) x) -#define OHCI_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL); printk("OHCI ALLO: %d: %4x\n", ++ __ohci_free_cnt,(unsigned int) x) -#define USB_FREE(x) kfree(x); printk("USB FREE: %d: %4x\n", -- __ohci_free1_cnt, (unsigned int) x) -#define USB_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL); printk("USB ALLO: %d: %4x\n", ++ __ohci_free1_cnt, (unsigned int) x) +#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); printk("OHCI ALLO: %d: %4x\n", ++ __ohci_free_cnt,(unsigned int) x) static int __ohci_free_cnt = 0; -static int __ohci_free1_cnt = 0; #else #define OHCI_FREE(x) kfree(x) -#define OHCI_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL) -#define USB_FREE(x) kfree(x) -#define USB_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL) +#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) #endif diff -u --recursive --new-file v2.3.33/linux/drivers/usb/ohci-root-hub.c linux/drivers/usb/ohci-root-hub.c --- v2.3.33/linux/drivers/usb/ohci-root-hub.c Tue Dec 7 09:32:46 1999 +++ linux/drivers/usb/ohci-root-hub.c Wed Dec 31 16:00:00 1969 @@ -1,341 +0,0 @@ -/* - * HCD (OHCI) Virtual Root Hub for USB. - * - * (C) Copyright 1999 Roman Weissgaerber (weissg@vienna.at) - * - * 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. - * - * - * The Root Hub is build into the HC (UHCI or OHCI) hardware. - * This piece of code lets it look like it resides on the usb - * like the other hubs. - * (for anyone who wants to do a control operation on the root hub) - * - * v4.0 1999/08/18 - * v2.1 1999/05/09 - * v2.0 1999/05/04 - * v1.0 1999/04/27 - * ohci-root-hub.c - * - */ - - -#include -#include -#include -#include -#include -#include - -#include "usb.h" -#include "ohci-hcd.h" - -#ifdef VROOTHUB - -#include "ohci-root-hub.h" - -static __u8 root_hub_dev_des[] = -{ - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x00, /* __u16 bcdUSB; v1.0 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x00, /* __u8 iProduct; */ - 0x00, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -/* -For OHCI we need just the 2nd Byte, so we -don't need this constant byte-array - -static __u8 root_hub_hub_des[] = -{ - 0x00, * __u8 bLength; * - 0x29, * __u8 bDescriptorType; Hub-descriptor * - 0x02, * __u8 bNbrPorts; * - 0x00, * __u16 wHubCharacteristics; * - 0x00, - 0x01, * __u8 bPwrOn2pwrGood; 2ms * - 0x00, * __u8 bHubContrCurrent; 0 mA * - 0x00, * __u8 DeviceRemovable; *** 8 Ports max *** * - 0xff * __u8 PortPwrCtrlMask; *** 8 ports max *** * -}; -*/ - -#define OK(x) len = (x); req_reply = 0; break -#define WR_RH_STAT(x) writel((x), &ohci->regs->roothub.status) -#define WR_RH_PORTSTAT(x) writel((x), &ohci->regs->roothub.portstatus[wIndex-1]) -#define RD_RH_STAT readl(&ohci->regs->roothub.status) -#define RD_RH_PORTSTAT readl(&ohci->regs->roothub.portstatus[wIndex-1]) - -int root_hub_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest * cmd, void *data, int leni) -{ - struct ohci * ohci = usb_dev->bus->hcpriv; - __u8 data_buf[16]; - int req_reply=4; - - int len =leni; - - __u16 bmRType_bReq = cmd->requesttype | (cmd->request << 8); - __u16 wValue = cpu_to_le16(cmd->value); - __u16 wIndex = cpu_to_le16(cmd->index); - __u16 wLength = cpu_to_le16(cmd->length); - - OHCI_DEBUG(printk("USB root hub: adr: %2x cmd(%1x): ", ohci->rh.devnum, 8);) - OHCI_DEBUG({ int i; for(i=0;i<8;i++) printk(" %02x", ((unsigned char *)cmd)[i]);}) - OHCI_DEBUG(printk(" ; \n");) - - switch (bmRType_bReq) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: *(__u16 *)data = cpu_to_le16(1); OK(2); - case RH_GET_STATUS | RH_INTERFACE: *(__u16 *)data = cpu_to_le16(0); OK(2); - case RH_GET_STATUS | RH_ENDPOINT: *(__u16 *)data = cpu_to_le16(0); OK(2); - case RH_GET_STATUS | RH_CLASS: *(__u32 *)data = cpu_to_le32(RD_RH_STAT & 0x7fff7fff); OK(4); - case RH_GET_STATUS | RH_OTHER | RH_CLASS: *(__u32 *)data = cpu_to_le32(RD_RH_PORTSTAT); OK(4); - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): OK(0); - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case (RH_C_HUB_OVER_CURRENT): WR_RH_STAT(RH_PS_OCIC); OK(0); - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_CCS ); OK(0); - case (RH_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_POCI); OK(0); - case (RH_PORT_POWER): WR_RH_PORTSTAT(RH_PS_LSDA); OK(0); - case (RH_C_PORT_CONNECTION): WR_RH_PORTSTAT(RH_PS_CSC ); OK(0); - case (RH_C_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_PESC); OK(0); - case (RH_C_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_PSSC); OK(0); - case (RH_C_PORT_OVER_CURRENT): WR_RH_PORTSTAT(RH_PS_OCIC); OK(0); - case (RH_C_PORT_RESET): WR_RH_PORTSTAT(RH_PS_PRSC); OK(0); - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_PSS ); OK(0); - case (RH_PORT_RESET): if((RD_RH_PORTSTAT &1) != 0) WR_RH_PORTSTAT(RH_PS_PRS ); /* BUG IN HUP CODE *********/ OK(0); - case (RH_PORT_POWER): WR_RH_PORTSTAT(RH_PS_PPS ); OK(0); - case (RH_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_PES ); OK(0); - } - break; - - case RH_SET_ADDRESS: ohci->rh.devnum = wValue; OK(0); - - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min(leni, min(sizeof(root_hub_dev_des), wLength)); - memcpy(data, root_hub_dev_des, len); OK(len); - case (0x02): /* configuration descriptor */ - len = min(leni, min(sizeof(root_hub_config_des), wLength)); - memcpy(data, root_hub_config_des, len); OK(len); - case (0x03): /* string descriptors */ - default: OK(-4); - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - *(__u8 *)(data_buf+1) = 0x29; - *(__u32 *)(data_buf+2) = cpu_to_le32(readl(&ohci->regs->roothub.a)); - *(__u8 *)data_buf = (*(__u8 *)(data_buf+2) / 8) * 2 + 9; /* length of descriptor */ - - len = min(leni, min(*(__u8 *)data_buf, wLength)); - *(__u8 *)(data_buf+6) = 0; /* Root Hub needs no current from bus */ - if(*(__u8 *)(data_buf+2) < 8) { /* less than 8 Ports */ - *(__u8 *) (data_buf+7) = readl(&ohci->regs->roothub.b) & 0xff; - *(__u8 *) (data_buf+8) = (readl(&ohci->regs->roothub.b) & 0xff0000) >> 16; - } - else { - *(__u32 *) (data_buf+7) = cpu_to_le32(readl(&ohci->regs->roothub.b)); - } - memcpy(data, data_buf, len); - OK(len); - - case RH_GET_CONFIGURATION: *(__u8 *)data = 0x01; OK(1); - - case RH_SET_CONFIGURATION: WR_RH_STAT( 0x10000); OK(0); - - default: OK(-4); - } - - OHCI_DEBUG(printk("USB HC roothubstat1: %x \n", readl( &(ohci->regs->roothub.portstatus[0]) ));) - OHCI_DEBUG(printk("USB HC roothubstat2: %x \n", readl( &(ohci->regs->roothub.portstatus[1]) ));) - - - return len; -} - -/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ -static int root_hub_send_irq(struct ohci * ohci, void * rh_data, int rh_len ) { - - int num_ports; - int i; - int ret; - int len; - - __u8 * data = rh_data; - - num_ports = readl(&ohci->regs->roothub.a) & 0xff; - *(__u8 *)data = (readl(&ohci->regs->roothub.status) & 0x00030000)>0?1:0; - ret = *(__u8 *)data; - - for(i=0; i < num_ports; i++) { - *(__u8 *)(data+i/8) |= ((readl(&ohci->regs->roothub.portstatus[i]) & 0x001f0000)>0?1:0) << ((i+1) % 8); - ret += *(__u8 *)(data+i/8); - } - len = i/8 + 1; - - if (ret > 0) return len; - - return RH_NACK; -} - - -static int ohci_init_rh_int_timer(struct usb_device * usb_dev, int interval); - -/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */ -static void rh_int_timer_do(unsigned long ptr) { - int len; - int interval; - int ret; - - struct usb_device * usb_dev = (struct usb_device *) ptr; - struct ohci * ohci = usb_dev->bus->hcpriv; - struct ohci_device * dev = usb_to_ohci(usb_dev); - - - if(ohci->rh.send) { - len = root_hub_send_irq(ohci, dev->data, 1 ); - - if(len > 0) { - - ret = ohci->rh.handler(0, dev->data, len, ohci->rh.dev_id); - if(ret <= 0)ohci->rh.send = 0; /* 0 .. do not requeue */ - } - } - interval = ohci->rh.interval; - ohci_init_rh_int_timer(usb_dev, interval); -} - -/* Root Hub INTs are polled by this timer */ -static int ohci_init_rh_int_timer(struct usb_device * usb_dev, int interval) { - struct ohci * ohci = usb_dev->bus->hcpriv; - - ohci->rh.interval = interval; - init_timer(& ohci->rh.rh_int_timer); - ohci->rh.rh_int_timer.function = rh_int_timer_do; - ohci->rh.rh_int_timer.data = (unsigned long) usb_dev; - ohci->rh.rh_int_timer.expires = jiffies + (HZ * (interval<30?30: interval)) /1000; - add_timer(&ohci->rh.rh_int_timer); - - return 0; -} - -static int ohci_del_rh_int_timer(struct ohci * ohci) { - del_timer(&ohci->rh.rh_int_timer); - return 0; -} - -int root_hub_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id, void **handle) -{ - struct ohci * ohci = usb_dev->bus->hcpriv; - - OHCI_DEBUG( printk("USB HC-RH IRQ>>>: RH every %d ms\n", period);) - ohci->rh.handler = handler; - ohci->rh.dev_id = dev_id; - ohci->rh.send = 1; - ohci->rh.interval = period; - ohci_init_rh_int_timer(usb_dev, period); - *handle = ohci->rh.int_addr = usb_to_ohci(usb_dev); - return 0; -} - -int root_hub_release_irq(struct usb_device *usb_dev, void * ed) -{ - // struct usb_device *usb_dev = ((struct ohci_device *) ((unsigned int)ed & 0xfffff000))->usb; - struct ohci * ohci = usb_dev->bus->hcpriv; - - OHCI_DEBUG( printk("USB HC-RH RM_IRQ>>>:\n");) - ohci->rh.send = 0; - ohci_del_rh_int_timer(ohci); - return 0; -} - - - -#endif diff -u --recursive --new-file v2.3.33/linux/drivers/usb/ohci-root-hub.h linux/drivers/usb/ohci-root-hub.h --- v2.3.33/linux/drivers/usb/ohci-root-hub.h Mon May 10 10:18:34 1999 +++ linux/drivers/usb/ohci-root-hub.h Wed Dec 31 16:00:00 1969 @@ -1,71 +0,0 @@ -/* - * HCD (OHCI) Virtual Root Hub Protocol for USB. - * - * (C) Copyright 1999 Roman Weissgaerber (weissg@vienna.at) - * - * The Root Hub is build into the HC (UHCI or OHCI) hardware. - * This piece of code lets it look like it resides on the bus - * like the other hubs. - * (for anyone who wants to do a control operation on the root hub) - * - * v1.0 1999/04/27 - * ohci-root-hub.h - * - */ - -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -/* Our Vendor Specific feature */ -#define RH_REMOVE_EP 0x00 - - -#define RH_ACK 0x01 -#define RH_REQ_ERR -1 -#define RH_NACK 0x00 - -#define min(a,b) (((a)<(b))?(a):(b)) - diff -u --recursive --new-file v2.3.33/linux/drivers/usb/ohci.c linux/drivers/usb/ohci.c --- v2.3.33/linux/drivers/usb/ohci.c Fri Oct 15 15:25:14 1999 +++ linux/drivers/usb/ohci.c Wed Dec 31 16:00:00 1969 @@ -1,2856 +0,0 @@ -/* - * Open Host Controller Interface driver for USB. - * - * (C) Copyright 1999 Gregory P. Smith - * Significant code from the following individuals has also been used: - * (C) Copyright 1999 Roman Weissgaerber [ohci-hcd.c] - * (C) Copyright 1999 Linus Torvalds [uhci.c] - * - * This is the "other" host controller interface for USB. You will - * find this on many non-Intel based motherboards, and of course the - * Mac. As Linus hacked his UHCI driver together first, I originally - * modeled this after his.. (it should be obvious) - * - * To get started in USB, I used the "Universal Serial Bus System - * Architecture" book by Mindshare, Inc. It was a reasonable introduction - * and overview of USB and the two dominant host controller interfaces - * however you're better off just reading the real specs available - * from www.usb.org as you'll need them to get enough details to - * actually implement a HCD. The book has many typos and omissions - * Beware, the specs are the victim of a committee. - * - * This code was written with Guinness on the brain, xsnow on the desktop - * and Orbital, Orb, Enya & Massive Attack on the CD player. What a life! ;) - * - * No filesystems were harmed in the development of this code. - * - * $Id: ohci.c,v 1.80 1999/09/30 06:32:17 greg Exp $ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "ohci.h" - -#ifdef CONFIG_APM -#include -static int handle_apm_event(apm_event_t event); -static int apm_resume = 0; -#endif - -static DECLARE_WAIT_QUEUE_HEAD(ohci_configure); - -#ifdef CONFIG_USB_OHCI_DEBUG -#define OHCI_DEBUG /* to make typing it easier.. */ -#endif - -int MegaDebug = 0; /* SIGUSR2 to the control thread toggles this */ - - -#ifdef OHCI_TIMER -static struct timer_list ohci_timer; /* timer for root hub polling */ -#endif - -static spinlock_t ohci_edtd_lock = SPIN_LOCK_UNLOCKED; - -#define FIELDS_OF_ED(e) le32_to_cpup(&e->status), le32_to_cpup(&e->tail_td), \ - le32_to_cpup(&e->_head_td), le32_to_cpup(&e->next_ed) -#define FIELDS_OF_TD(t) le32_to_cpup(&t->info), le32_to_cpup(&t->cur_buf), \ - le32_to_cpup(&t->next_td), le32_to_cpup(&t->buf_end) - -#ifdef OHCI_DEBUG -static const char *cc_names[16] = { - "no error", - "CRC error", - "bit stuff error", - "data toggle mismatch", - "stall", - "device not responding", - "PID check failed", - "unexpected PID", - "data overrun", - "data underrun", - "reserved (10)", - "reserved (11)", - "buffer overrun", - "buffer underrun", - "not accessed (14)", - "not accessed" -}; -#endif - -/* - * Add a chain of TDs to the end of the TD list on a given ED. - * - * This function uses the first TD of the chain as the new dummy TD - * for the ED, and uses the old dummy TD instead of the first TD - * of the chain. The reason for this is that this makes it possible - * to update the TD chain without needing any locking between the - * CPU and the OHCI controller. - * - * The return value is the pointer to the new first TD (the old - * dummy TD). - * - * Important! This function is not re-entrant w.r.t. each ED. - * Locking ohci_edtd_lock while using the function is a must - * if there is any possibility of another CPU or an interrupt routine - * calling this function with the same ED. - * - * This function can be called by the interrupt handler. - */ -static struct ohci_td *ohci_add_tds_to_ed(struct ohci_td *td, - struct ohci_ed *ed) -{ - struct ohci_td *t, *dummy_td; - u32 new_dummy; - - if (ed->tail_td == 0) { - printk(KERN_ERR "eek! an ED without a dummy_td\n"); - return td; - } - - /* Get a pointer to the current dummy TD. */ - dummy_td = bus_to_virt(ed_tail_td(ed)); - - for (t = td; ; t = bus_to_virt(le32_to_cpup(&t->next_td))) { - t->ed = ed; - if (t->next_td == 0) - break; - } - - /* Make the last TD point back to the first, since it - * will become the new dummy TD. */ - new_dummy = cpu_to_le32(virt_to_bus(td)); - t->next_td = new_dummy; - - /* Copy the contents of the first TD into the dummy */ - *dummy_td = *td; - - /* Turn the first TD into a dummy */ - make_dumb_td(td); - - /* Set the HC's tail pointer to the new dummy */ - ed->tail_td = new_dummy; - - return dummy_td; /* replacement head of chain */ -} /* ohci_add_tds_to_ed() */ - - -/* .......... */ - - -void ohci_start_control(struct ohci *ohci) -{ - /* tell the HC to start processing the control list */ - writel_set(OHCI_USB_CLE, &ohci->regs->control); - writel_set(OHCI_CMDSTAT_CLF, &ohci->regs->cmdstatus); -} - -void ohci_start_bulk(struct ohci *ohci) -{ - /* tell the HC to start processing the bulk list */ - writel_set(OHCI_USB_BLE, &ohci->regs->control); - writel_set(OHCI_CMDSTAT_BLF, &ohci->regs->cmdstatus); -} - -void ohci_start_periodic(struct ohci *ohci) -{ - /* enable processing periodic (intr) transfers starting next frame */ - writel_set(OHCI_USB_PLE, &ohci->regs->control); -} - -void ohci_start_isoc(struct ohci *ohci) -{ - /* enable processing isoc. transfers starting next frame */ - writel_set(OHCI_USB_IE, &ohci->regs->control); -} - -/* - * Add an ED to the hardware register ED list pointed to by hw_listhead_p - * This function only makes sense for Control and Bulk EDs. - */ -static void ohci_add_ed_to_hw(struct ohci_ed *ed, void* hw_listhead_p) -{ - __u32 listhead; - unsigned long flags; - - spin_lock_irqsave(&ohci_edtd_lock, flags); - - listhead = readl(hw_listhead_p); - - /* if the list is not empty, insert this ED at the front */ - /* XXX should they go on the end? */ - ed->next_ed = cpu_to_le32(listhead); - - /* update the hardware listhead pointer */ - writel(virt_to_bus(ed), hw_listhead_p); - - spin_unlock_irqrestore(&ohci_edtd_lock, flags); -} /* ohci_add_ed_to_hw() */ - -/* - * Put a control ED on the controller's list - */ -void ohci_add_control_ed(struct ohci *ohci, struct ohci_ed *ed) -{ - ohci_add_ed_to_hw(ed, &ohci->regs->ed_controlhead); - ohci_start_control(ohci); -} /* ohci_add_control_ed() */ - -/* - * Put a bulk ED on the controller's list - */ -void ohci_add_bulk_ed(struct ohci *ohci, struct ohci_ed *ed) -{ - ohci_add_ed_to_hw(ed, &ohci->regs->ed_bulkhead); - ohci_start_bulk(ohci); -} /* ohci_add_bulk_ed() */ - -/* - * Put a periodic ED on the appropriate list given the period. - */ -void ohci_add_periodic_ed(struct ohci *ohci, struct ohci_ed *ed, int period) -{ - struct ohci_ed *int_ed; - struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); - unsigned long flags; - - /* - * Pick a good frequency endpoint based on the requested period - */ - int_ed = &root_hub->ed[ms_to_ed_int(period)]; -#ifdef OHCI_DEBUG - if (MegaDebug) - printk(KERN_DEBUG "usb-ohci: Using INT ED queue %d for %dms period\n", - ms_to_ed_int(period), period); -#endif - - spin_lock_irqsave(&ohci_edtd_lock, flags); - /* - * Insert this ED at the front of the list. - */ - ed->next_ed = int_ed->next_ed; - int_ed->next_ed = cpu_to_le32(virt_to_bus(ed)); - - spin_unlock_irqrestore(&ohci_edtd_lock, flags); - - ohci_start_periodic(ohci); -} /* ohci_add_periodic_ed() */ - -/* - * Locate the periodic ED for a given interrupt endpoint. - */ -struct ohci_ed *ohci_get_periodic_ed(struct ohci_device *dev, int period, - unsigned int pipe, int isoc) -{ - struct ohci_device *root_hub = usb_to_ohci(dev->ohci->bus->root_hub); - unsigned long flags; - struct ohci_ed *int_ed; - unsigned int status, req_status; - - /* get the dummy ED before the EDs for this period */ - int_ed = &root_hub->ed[ms_to_ed_int(period)]; - - /* decide on what the status field should look like */ - req_status = ed_set_maxpacket(usb_maxpacket(ohci_to_usb(dev), pipe, usb_pipeout(pipe))) - | ed_set_speed(usb_pipeslow(pipe)) - | (usb_pipe_endpdev(pipe) & 0x7ff) - | ed_set_type_isoc(isoc); - - spin_lock_irqsave(&ohci_edtd_lock, flags); - for (;;) { - int_ed = bus_to_virt(le32_to_cpup(&int_ed->next_ed)); - /* stop if we get to the end or to another dummy ED. */ - if (!int_ed) - break; /* return NULL */ - status = le32_to_cpup(&int_ed->status); - if ((status & OHCI_ED_FA) == 0) { - int_ed = NULL; - break; /* return NULL */ - } - /* check whether all the appropriate fields match */ - if ((status & 0x7ffa7ff) == req_status) - break; /* return int_ed */ - } - spin_unlock_irqrestore(&ohci_edtd_lock, flags); - return int_ed; -} - - -/* - * This will be used for the interrupt to wake us up on the next SOF - */ -DECLARE_WAIT_QUEUE_HEAD(start_of_frame_wakeup); - -static void ohci_wait_sof(struct ohci_regs *regs) -{ - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(&start_of_frame_wakeup, &wait); - - /* clear the SOF interrupt status and enable it */ - writel(OHCI_INTR_SF, ®s->intrstatus); - writel(OHCI_INTR_SF, ®s->intrenable); - - schedule_timeout(HZ/10); - - remove_wait_queue(&start_of_frame_wakeup, &wait); -} - -/* - * Guarantee that an ED is safe to be modified by the HCD (us). - * - * This function can NOT be called from an interrupt. - * - * TODO: If we're waiting for the ED to be safe so that it can be - * destroyed, a similar "ohci_schedule_ed_free" function that just - * adds it to a list of EDs to destroy during the SOF interrupt - * processing would be useful (and be callable from an interrupt). - */ -void ohci_wait_for_ed_safe(struct ohci_regs *regs, struct ohci_ed *ed) -{ - __u32 *hw_listcurrent; - - /* tell the controller to skip this ED */ - ed->status |= cpu_to_le32(OHCI_ED_SKIP); - - switch (ohci_ed_hcdtype(ed)) { - case HCD_ED_CONTROL: - hw_listcurrent = ®s->ed_controlcurrent; - break; - case HCD_ED_BULK: - hw_listcurrent = ®s->ed_bulkcurrent; - break; - case HCD_ED_ISOC: - case HCD_ED_INT: - hw_listcurrent = ®s->ed_periodcurrent; - break; - default: - return; - } - - /* - * If the HC is processing this ED we need to wait until the - * at least the next frame. - */ - if (virt_to_bus(ed) == readl(hw_listcurrent)) { -#ifdef OHCI_DEBUG - printk(KERN_INFO "Waiting a frame for OHC to finish with ED %p [%x %x %x %x]\n", ed, FIELDS_OF_ED(ed)); -#endif - ohci_wait_sof(regs); - - } - - return; /* The ED is now safe */ -} /* ohci_wait_for_ed_safe() */ - - -/* - * Remove an ED from the HC's list. - * This function can ONLY be used for Control or Bulk EDs. - * - * Note that the SKIP bit is left on in the removed ED. - */ -void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed) -{ - unsigned long flags; - struct ohci_regs *regs = ohci->regs; - struct ohci_ed *cur; - __u32 bus_ed = virt_to_bus(ed); - __u32 bus_cur; - __u32 *hw_listhead_p; - __u32 ed_type = ohci_ed_hcdtype(ed); - - if (ed == NULL || !bus_ed) - return; - ed->status |= cpu_to_le32(OHCI_ED_SKIP); - - switch (ed_type) { - case HCD_ED_CONTROL: - hw_listhead_p = ®s->ed_controlhead; - break; - case HCD_ED_BULK: - hw_listhead_p = ®s->ed_bulkhead; - break; - default: -#ifdef OHCI_DEBUG - printk(KERN_ERR "Wrong HCD ED type: %d.\n", ed_type); -#endif - return; - } - - bus_cur = readl(hw_listhead_p); - - if (bus_cur == 0) - return; /* the list is already empty */ - - cur = bus_to_virt(bus_cur); - - spin_lock_irqsave(&ohci_edtd_lock, flags); - - /* if its the head ED, move the head */ - if (bus_cur == bus_ed) { - writel(le32_to_cpup(&cur->next_ed), hw_listhead_p); - } else if (cur->next_ed != 0) { - struct ohci_ed *prev; - - /* walk the list and unlink the ED if found */ - do { - prev = cur; - cur = bus_to_virt(le32_to_cpup(&cur->next_ed)); - - if (cur == ed) { - /* unlink from the list */ - prev->next_ed = cur->next_ed; - break; - } - } while (cur->next_ed != 0); - } - - /* - * Make sure this ED is not being accessed by the HC as we speak. - */ - ohci_wait_for_ed_safe(regs, ed); - - /* clear any links from the ED for safety */ - ed->next_ed = 0; - - spin_unlock_irqrestore(&ohci_edtd_lock, flags); -} /* ohci_remove_norm_ed_from_hw() */ - - -/* - * Remove a periodic ED from the host controller - */ -void ohci_remove_periodic_ed(struct ohci *ohci, struct ohci_ed *ed) -{ - struct ohci_device *root_hub = usb_to_ohci(ohci->bus->root_hub); - struct ohci_ed *cur_ed = NULL, *prev_ed; - unsigned long flags; - - /* FIXME: this will need to up fixed when add_periodic_ed() - * is updated to spread similar polling rate EDs out over - * multiple periodic queues. Currently this assumes that the - * 32ms (slowest) polling queue links to all others... */ - - /* search the periodic EDs, skipping the first one which is - * only a placeholder. */ - prev_ed = &root_hub->ed[ED_INT_32]; - if (prev_ed->next_ed) - cur_ed = bus_to_virt(le32_to_cpup(&prev_ed->next_ed)); - - while (cur_ed) { - if (ed == cur_ed) { /* remove the ED */ - /* set its SKIP bit and be sure its not in use */ - ohci_wait_for_ed_safe(ohci->regs, ed); - - /* unlink it */ - spin_lock_irqsave(&ohci_edtd_lock, flags); - prev_ed->next_ed = ed->next_ed; - spin_unlock_irqrestore(&ohci_edtd_lock, flags); - ed->next_ed = 0; - - break; - } - - spin_lock_irqsave(&ohci_edtd_lock, flags); - if (cur_ed->next_ed) { - prev_ed = cur_ed; - cur_ed = bus_to_virt(le32_to_cpup(&cur_ed->next_ed)); - spin_unlock_irqrestore(&ohci_edtd_lock, flags); - } else { - spin_unlock_irqrestore(&ohci_edtd_lock, flags); - - /* if multiple polling queues need to be checked, - * here is where you'd advance to the next one */ - - printk("usb-ohci: ed %p not found on periodic queue\n", ed); - break; - } - } -} /* ohci_remove_periodic_ed() */ - - -/* - * Remove an ED from the host controller, choosing - */ -void ohci_remove_ed(struct ohci *ohci, struct ohci_ed *ed) -{ - /* Remove the ED from the HC */ - if (ohci_ed_hcdtype(ed) & HCD_ED_NORMAL) { - ohci_remove_norm_ed_from_hw(ohci, ed); - } else { - ohci_remove_periodic_ed(ohci, ed); - } -} /* ohci_remove_ed() */ - - -/* - * Remove all the EDs which have a given device address from a list. - * Used when the device is unplugged. - * Returns 1 if anything was changed. - */ -static int ohci_remove_device_list(__u32 *headp, int devnum) -{ - struct ohci_ed *ed; - __u32 *prevp = headp; - int removed = 0; - - while (*prevp != 0) { - ed = bus_to_virt(le32_to_cpup(prevp)); - if ((le32_to_cpup(&ed->status) & OHCI_ED_FA) == devnum) { - /* set the controller to skip this one - and remove it from the list */ - ed->status |= cpu_to_le32(OHCI_ED_SKIP); - /* XXX should call td->completed for each td */ - *prevp = ed->next_ed; - removed = 1; - } else { - prevp = &ed->next_ed; - } - } - wmb(); - - return removed; -} - -/* - * Remove all the EDs for a given device from all lists. - */ -void ohci_remove_device(struct ohci *ohci, int devnum) -{ - unsigned long flags; - __u32 head; - struct ohci_regs *regs = ohci->regs; - struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); - - spin_lock_irqsave(&ohci_edtd_lock, flags); - - /* Control list */ - head = cpu_to_le32(readl(®s->ed_controlhead)); - if (ohci_remove_device_list(&head, devnum)) - writel(le32_to_cpup(&head), ®s->ed_controlhead); - - /* Bulk list */ - head = cpu_to_le32(readl(®s->ed_bulkhead)); - if (ohci_remove_device_list(&head, devnum)) - writel(le32_to_cpup(&head), ®s->ed_bulkhead); - - /* Interrupt/iso list */ - head = cpu_to_le32(virt_to_bus(&root_hub->ed[ED_INT_32])); - ohci_remove_device_list(&head, devnum); - - /* - * Wait until the start of the next frame to ensure - * that the HC has seen any changes. - */ - ohci_wait_sof(ohci->regs); - - spin_unlock_irqrestore(&ohci_edtd_lock, flags); -} - -/* - * Remove a TD from the given EDs TD list. The TD is freed as well. - * (so far this function hasn't been needed) - */ -void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed) -{ - unsigned long flags; - struct ohci_td *head_td; - - if ((td == NULL) || (ed == NULL)) - return; - - if (ed_head_td(ed) == 0) - return; - - spin_lock_irqsave(&ohci_edtd_lock, flags); - - /* set the "skip me bit" in this ED */ - ed->status |= cpu_to_le32(OHCI_ED_SKIP); - - /* XXX Assuming this list will never be circular */ - - head_td = bus_to_virt(ed_head_td(ed)); - if (virt_to_bus(td) == ed_head_td(ed)) { - /* It's the first TD, remove it. */ - set_ed_head_td(ed, head_td->next_td); - } else { - struct ohci_td *prev_td, *cur_td; - - /* FIXME: collapse this into a nice simple loop :) */ - if (head_td->next_td != 0) { - prev_td = head_td; - cur_td = bus_to_virt(le32_to_cpup(&head_td->next_td)); - for (;;) { - if (td == cur_td) { - /* remove it */ - prev_td->next_td = cur_td->next_td; - break; - } - if (cur_td->next_td == 0) - break; - prev_td = cur_td; - cur_td = bus_to_virt(le32_to_cpup(&cur_td->next_td)); - } - } - } - - td->next_td = 0; /* remove the TDs links */ - td->ed = NULL; - - /* return this TD to the pool of free TDs */ - ohci_free_td(td); - - /* unset the "skip me bit" in this ED */ - ed->status &= cpu_to_le32(~OHCI_ED_SKIP); - - spin_unlock_irqrestore(&ohci_edtd_lock, flags); -} /* ohci_remove_td_from_ed() */ - - -/* - * Get a pointer (virtual) to an available TD from the given device's - * pool. Return NULL if none are left. - */ -static struct ohci_td *ohci_get_free_td(struct ohci_device *dev) -{ - int idx; - -#if 0 - printk(KERN_DEBUG "in ohci_get_free_td()\n"); -#endif - - /* FIXME: this is horribly inefficient */ - for (idx=0; idx < NUM_TDS; idx++) { -#if 0 - show_ohci_td(&dev->td[idx]); -#endif - if (!td_allocated(dev->td[idx])) { - struct ohci_td *new_td = &dev->td[idx]; - /* zero out the TD */ - memset(new_td, 0, sizeof(*new_td)); - /* mark the new TDs as unaccessed */ - new_td->info = cpu_to_le32(OHCI_TD_CC_NEW); - /* mark it as allocated */ - allocate_td(new_td); - /* record the device that its on */ - new_td->usb_dev = ohci_to_usb(dev); - return new_td; - } - } - - printk(KERN_ERR "usb-ohci: unable to allocate a TD\n"); - return NULL; -} /* ohci_get_free_td() */ - - -/* - * Get a pointer (virtual) to an available TD from the given device's - * pool. Return NULL if none are left. - * - * NOTE: This function does not allocate and attach the dummy_td. - * That is done in ohci_fill_ed(). [should we really do that here?] - */ -static struct ohci_ed *ohci_get_free_ed(struct ohci_device *dev, __u32 ed_type) -{ - int idx; - - /* FIXME: this is horribly inefficient */ - for (idx=0; idx < NUM_EDS; idx++) { - if (!ed_allocated(dev->ed[idx])) { - struct ohci_ed *new_ed = &dev->ed[idx]; - /* zero out the ED */ - memset(new_ed, 0, sizeof(*new_ed)); - /* all new EDs start with the SKIP bit set */ - new_ed->status |= cpu_to_le32(OHCI_ED_SKIP); - /* mark it as allocated */ - allocate_ed(new_ed); - ohci_ed_set_hcdtype(new_ed, ed_type); - new_ed->ohci_dev = dev; - return new_ed; - } - } - - printk(KERN_ERR "usb-ohci: unable to allocate an ED\n"); - return NULL; -} /* ohci_get_free_ed() */ - - -/* - * Free an OHCI ED and all of the TDs on its list. It is assumed that - * this ED is not active. You should call ohci_wait_for_ed_safe() - * beforehand if you can't guarantee that. - */ -void ohci_free_ed(struct ohci_ed *ed) -{ - if (!ed) - return; - - if (ed_head_td(ed) != 0) { - struct ohci_td *td, *tail_td, *next_td; - - td = bus_to_virt(ed_head_td(ed)); - tail_td = bus_to_virt(ed_tail_td(ed)); - for (;;) { - next_td = bus_to_virt(le32_to_cpup(&td->next_td)); - ohci_free_td(td); - if (td == tail_td) - break; - td = next_td; - } - } - - ed->status &= cpu_to_le32(~(__u32)ED_ALLOCATED); -} /* ohci_free_ed() */ - - -/* - * Initialize a TD - * - * dir = OHCI_TD_D_IN, OHCI_TD_D_OUT, or OHCI_TD_D_SETUP - * toggle = TOGGLE_AUTO, TOGGLE_DATA0, TOGGLE_DATA1 - */ -struct ohci_td *ohci_fill_new_td(struct ohci_td *td, int dir, int toggle, __u32 flags, void *data, __u32 len, void *dev_id, usb_device_irq completed) -{ - /* hardware fields */ - td->info = cpu_to_le32(OHCI_TD_CC_NEW | - (dir & OHCI_TD_D) | - (toggle & OHCI_TD_DT) | - flags); - td->cur_buf = (data == NULL) ? 0 : cpu_to_le32(virt_to_bus(data)); - td->buf_end = (len == 0) ? 0 : - cpu_to_le32(virt_to_bus((char *)data + len - 1)); - - /* driver fields */ - td->data = data; - td->dev_id = dev_id; - td->completed = completed; - -#if 0 - printk(KERN_DEBUG "ohci_fill_new_td created:\n"); - show_ohci_td(td); -#endif - - return td; -} /* ohci_fill_new_td() */ - - -/* - * Initialize a new ED on device dev, including allocating and putting the - * dummy tail_td on its queue if it doesn't already have one. Any - * TDs on this ED other than the dummy will be lost (so there better - * not be any!). This assumes that the ED is Allocated and will - * force the Allocated bit on. - */ -struct ohci_ed *ohci_fill_ed(struct ohci_device *dev, struct ohci_ed *ed, - int maxpacketsize, int lowspeed, int endp_id) -{ - struct ohci_td *dummy_td; - - if (ed_head_td(ed) != ed_tail_td(ed)) - printk(KERN_ERR "Reusing a non-empty ED %p!\n", ed); - - if (!ed->tail_td) { - dummy_td = ohci_get_free_td(dev); - if (dummy_td == NULL) { - printk(KERN_ERR "Error allocating dummy TD for ED %p\n", ed); - return NULL; /* no dummy available! */ - } - make_dumb_td(dummy_td); /* flag it as a dummy */ - ed->tail_td = cpu_to_le32(virt_to_bus(dummy_td)); - } else { - dummy_td = bus_to_virt(ed_tail_td(ed)); - if (!td_dummy(*dummy_td)) - printk(KERN_ERR "ED %p's dummy %p is screwy\n", ed, dummy_td); - } - - /* set the head TD to the dummy and clear the Carry & Halted bits */ - ed->_head_td = ed->tail_td; - - ed->status &= cpu_to_le32(OHCI_ED_HCD_MASK); - ed->status |= cpu_to_le32( - ed_set_maxpacket(maxpacketsize) | - ed_set_speed(lowspeed) | - (endp_id & 0x7ff) | - ((ohci_ed_hcdtype(ed) != HCD_ED_ISOC) ? OHCI_ED_F_NORM : OHCI_ED_F_ISOC)); - allocate_ed(ed); - ed->next_ed = 0; - - return ed; -} /* ohci_fill_ed() */ - - -/* - * Create a chain of Normal TDs to be used for a large data transfer - * (bulk or control). - * - * The next_td parameter should be OHCI byte order bus address of the - * next TD to follow this chain or 0 if there is none. - * - * Returns the head TD in the chain. - */ -struct ohci_td *ohci_build_td_chain(struct ohci_device *dev, - void *data, unsigned int len, int dir, __u32 toggle, - int round, int auto_free, void* dev_id, - usb_device_irq handler, __u32 next_td) -{ - struct ohci_td *head, *cur_td; - unsigned max_len; - - if (!data || (len == 0)) - return NULL; - - /* Get the first TD */ - head = ohci_get_free_td(dev); - if (head == NULL) { - printk(KERN_ERR "usb-ohci: out of TDs\n"); - return NULL; - } - - cur_td = head; - - /* AFICT, that the OHCI controller takes care of the innards of - * bulk & control data transfers by sending zero length - * packets as necessary if the transfer falls on an even packet - * size boundary, we don't need a special TD for that. */ - - /* check the 4096 byte alignment of the start of the data */ - max_len = 0x2000 - ((unsigned long)data & 0xfff); - - /* check if the remaining data occupies more than two pages */ - while (len > max_len) { - struct ohci_td *new_td; - - /* TODO lookup effect of rounding bit on - * individual TDs vs. whole TD chain transfers; - * disable cur_td's rounding bit here if needed. */ - - ohci_fill_new_td(cur_td, - td_set_dir_out(dir), - toggle & OHCI_TD_DT, - (round ? OHCI_TD_ROUND : 0), - data, max_len - 1, - dev_id, handler); - if (!auto_free) - noauto_free_td(head); - - /* adjust the data pointer & remaining length */ - data += max_len; - len -= max_len; - - /* allocate another td */ - new_td = ohci_get_free_td(dev); - if (new_td == NULL) { - printk(KERN_ERR "usb-ohci: out of TDs\n"); - /* FIXME: free any allocated TDs */ - return NULL; - } - - /* Link the new TD to the chain & advance */ - cur_td->next_td = cpu_to_le32(virt_to_bus(new_td)); - cur_td = new_td; - - /* address is page-aligned now */ - max_len = 0x2000; - toggle = TOGGLE_AUTO; /* toggle Data0/1 via the ED */ - } - - ohci_fill_new_td(cur_td, - td_set_dir_out(dir), - toggle & OHCI_TD_DT, - (round ? OHCI_TD_ROUND : 0), - data, len, - dev_id, handler); - if (!auto_free) - noauto_free_td(head); - - /* link the given next_td to the end of this chain */ - cur_td->next_td = next_td; - if (next_td == 0) - set_td_endofchain(cur_td); - - return head; -} /* ohci_build_td_chain() */ - - -/* - * Compute the number of bytes that have been transferred on a given - * TD. Do not call this on TDs that are active on the host - * controller. - */ -static __u16 ohci_td_bytes_done(struct ohci_td *td) -{ - __u16 result; - __u32 bus_data_start, bus_data_end; - - bus_data_start = virt_to_bus(td->data); - if (!td->data || !bus_data_start) - return 0; - - /* if cur_buf is 0, all data has been transferred */ - if (!td->cur_buf) { - return le32_to_cpup(&td->buf_end) - bus_data_start + 1; - } - - bus_data_end = le32_to_cpup(&td->cur_buf); - - /* is it on the same page? */ - if ((bus_data_start & ~0xfff) == (bus_data_end & ~0xfff)) { - result = bus_data_end - bus_data_start; - } else { - /* compute the amount transferred on the first page */ - result = 0x1000 - (bus_data_start & 0xfff); - /* add the amount done in the second page */ - result += (bus_data_end & 0xfff); - } - - return result; -} /* ohci_td_bytes_done() */ - - -/********************************** - * OHCI interrupt list operations * - **********************************/ - -/* - * Request an interrupt handler for one "pipe" of a USB device. - * (this function is pretty minimal right now) - * - * At the moment this is only good for input interrupts. (ie: for a - * mouse or keyboard) - * - * Period is desired polling interval in ms. The closest, shorter - * match will be used. Powers of two from 1-32 are supported by OHCI. - * - * Returns: success (0) or failure (< 0). - * Also sets the "handle pointer" that release_irq can use to stop this - * interrupt. (It's really a pointer to the TD). - */ -static int ohci_request_irq(struct usb_device *usb, unsigned int pipe, - usb_device_irq handler, int period, void *dev_id, void **handle, long bustime) -{ - struct ohci_device *dev = usb_to_ohci(usb); - struct ohci_td *td; - struct ohci_ed *interrupt_ed; /* endpoint descriptor for this irq */ - int maxps = usb_maxpacket(usb, pipe, usb_pipeout(pipe)); - unsigned long flags; - - /* Get an ED and TD */ - interrupt_ed = ohci_get_periodic_ed(dev, period, pipe, 0); - if (interrupt_ed == 0) { - interrupt_ed = ohci_get_free_ed(dev, HCD_ED_INT); - if (!interrupt_ed) { - printk(KERN_ERR "Out of EDs on device %p in ohci_request_irq\n", dev); - return (-ENOMEM); - } - - /* - * Set the max packet size, device speed, endpoint number, usb - * device number (function address), and type of TD. - */ - ohci_fill_ed(dev, interrupt_ed, maxps, usb_pipeslow(pipe), - usb_pipe_endpdev(pipe) ); - interrupt_ed->status &= cpu_to_le32(~OHCI_ED_SKIP); - - /* Assimilate the new ED into the collective */ - ohci_add_periodic_ed(dev->ohci, interrupt_ed, period); - } -#ifdef OHCI_DEBUG - if (MegaDebug) { - printk(KERN_DEBUG "ohci_request irq: using ED %p [%x %x %x %x]\n", - interrupt_ed, FIELDS_OF_ED(interrupt_ed)); - printk(KERN_DEBUG " for dev %d pipe %x period %d\n", usb->devnum, - pipe, period); - } -#endif - - td = ohci_get_free_td(dev); - if (!td) { - printk(KERN_ERR "Out of TDs in ohci_request_irq\n"); - ohci_free_ed(interrupt_ed); - return (-ENOMEM); - } - - /* Fill in the TD */ - if (maxps > sizeof(dev->data)) - maxps = sizeof(dev->data); - ohci_fill_new_td(td, td_set_dir_out(usb_pipeout(pipe)), - TOGGLE_AUTO, - OHCI_TD_ROUND, - dev->data, maxps, - dev_id, handler); - set_td_endofchain(td); - - /* - * Put the TD onto our ED and make sure its ready to run - */ - td->next_td = 0; - spin_lock_irqsave(&ohci_edtd_lock, flags); - td = ohci_add_tds_to_ed(td, interrupt_ed); - spin_unlock_irqrestore(&ohci_edtd_lock, flags); - - *handle = (void*)td; - return 0; -} /* ohci_request_irq() */ - -/* - * Release an interrupt handler previously allocated using - * ohci_request_irq. This function does no validity checking, so make - * sure you're not releasing an already released handle as it may be - * in use by something else.. - * - * This function can NOT be called from an interrupt. - */ -int ohci_release_irq(struct usb_device *usb, void* handle) -{ - struct ohci_device *dev; - struct ohci_td *int_td; - struct ohci_ed *int_ed; - -#ifdef OHCI_DEBUG - if (handle) - printk("usb-ohci: Releasing irq handle %p\n", handle); -#endif - - int_td = (struct ohci_td*)handle; - if (int_td == NULL) - return USB_ST_INTERNALERROR; - - dev = usb_to_ohci(int_td->usb_dev); - int_ed = int_td->ed; - - ohci_remove_periodic_ed(dev->ohci, int_ed); - - /* Tell the driver that the IRQ has been killed. */ - /* Passing NULL in the "buffer" void* along with the - * USB_ST_REMOVED status is the signal. */ - if (int_td->completed != NULL) - int_td->completed(USB_ST_REMOVED, NULL, 0, int_td->dev_id); - - /* Free the ED (& TD) */ - ohci_free_ed(int_ed); - - return USB_ST_NOERROR; -} /* ohci_release_irq() */ - - -/********************************************************************** - * Generic bulk/control/isochronous transfer processing - **********************************************************************/ - - -/* - * Queue a generic OHCI data transfer, specifying the type in ed_type. - * - * - data_td_toggle specifies the data toggle value to start with. - * - round specifies if the transfer should be allowed to fall short. - * - autofree determines if the data TDs are automatically freed. - * - setup_td and status_td will be prepended and appended to the TD - * chain respectively. Control transfers need these.. - * - * - handler will be called upon completion of every data TD it - * needs to check if the transfer is really done or not. - * - * A handle to the transfer is returned. - */ -static void * ohci_generic_trans(struct usb_device *usb_dev, int pipe, - int data_td_toggle, int round, int autofree, - void *dev_id, usb_device_irq handler, void *data, int len, - __u32 ed_type, struct ohci_td *setup_td, struct ohci_td *status_td) -{ - struct ohci_device *dev = usb_to_ohci(usb_dev); - struct ohci_ed *trans_ed; - struct ohci_td *data_td, *head_td; - unsigned long flags; - -#ifdef OHCI_DEBUG - if (MegaDebug) - printk(KERN_DEBUG "ohci_request_trans()\n"); -#endif - - trans_ed = ohci_get_free_ed(dev, ed_type); - if (!trans_ed) { - printk("usb-ohci: couldn't get ED for dev %p\n", dev); - return NULL; - } - - /* If this transfer has a data phase, allocate TDs for it */ - if (len > 0) { - __u32 next_td = status_td ? cpu_to_le32(virt_to_bus(status_td)) : 0; - /* allocate & fill in the TDs for this request */ - data_td = ohci_build_td_chain( dev, data, len, - usb_pipeout(pipe), - data_td_toggle, - round, autofree, - dev_id, - handler, - next_td ); - if (!data_td) { - /* out of TDs */ - printk(KERN_ERR "usb-ohci: build_td_chain failed for dev %p, pipe 0x%x\n", dev, pipe); - goto gt_free_and_exit; - } - } else { - data_td = status_td; - } - - /* start with the setup TD if there is one */ - if (setup_td) { - if (data_td) { - setup_td->next_td = cpu_to_le32(virt_to_bus(data_td)); - head_td = setup_td; - } else { -#ifdef OHCI_DEBUG - printk(KERN_ERR "usb-ohci: lonely setup_td detected\n"); -#endif - goto gt_free_and_exit; - } - } else { - head_td = data_td; - } - - if (!head_td) { -#ifdef OHCI_DEBUG - printk(KERN_ERR "usb-ohci: no TDs in transfer\n"); -#endif - goto gt_free_and_exit; /* nothing to do */ - } - - /* Set the max packet size, device speed, endpoint number, usb - * device number (function address), and type of TD. */ - ohci_fill_ed(dev, trans_ed, - usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)), - usb_pipeslow(pipe), - usb_pipe_endpdev(pipe) ); - - /* initialize the toggle carry flag in the ED */ - if (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) - ohci_ed_set_carry(trans_ed); - - /* - * Add the TDs to the ED, remove the skip flag - */ - spin_lock_irqsave(&ohci_edtd_lock, flags); - head_td = ohci_add_tds_to_ed(head_td, trans_ed); - trans_ed->status &= cpu_to_le32(~OHCI_ED_SKIP); - /* ohci_unhalt_ed(trans_ed); */ - spin_unlock_irqrestore(&ohci_edtd_lock, flags); - -#ifdef OHCI_DEBUG - if (MegaDebug) { - /* complete transaction debugging output (before) */ - printk(KERN_DEBUG " Trans ED %lx:\n", virt_to_bus(trans_ed)); - show_ohci_ed(trans_ed); - printk(KERN_DEBUG " Trans TD chain:\n"); - show_ohci_td_chain(head_td); - } -#endif - - /* Give the ED to the HC on the appropriate list */ - switch (ed_type) { - case HCD_ED_ISOC: - /* Isochronous transfers have a 1ms period */ - ohci_add_periodic_ed(dev->ohci, trans_ed, 1); - break; - case HCD_ED_CONTROL: - ohci_add_control_ed(dev->ohci, trans_ed); - break; - case HCD_ED_BULK: - ohci_add_bulk_ed(dev->ohci, trans_ed); - break; - default: -#ifdef OHCI_DEBUG - printk(KERN_ERR "usb-ohci: bad ED type %d\n", ed_type); -#endif - goto gt_free_and_exit; - } - - /* ... */ - - return trans_ed; - -gt_free_and_exit: - ohci_free_ed(trans_ed); - return NULL; -} /* ohci_generic_trans() */ - - -/* - * Terminate a transfer initiated by ohci_generic_trans() - * - * This function is NOT safe to call from an interrupt. - */ -static int ohci_terminate_trans(struct usb_device *usb_dev, void *handle) -{ - struct ohci_ed *req_ed = (struct ohci_ed *) handle; - struct ohci_device *dev = usb_to_ohci(usb_dev); - struct ohci_regs *regs = dev->ohci->regs; - - if (!handle) - return 0; - - /* stop the transfer & collect the number of bytes */ - /* (this is the non-interrupt safe function call) */ - ohci_wait_for_ed_safe(regs, req_ed); - - /* Remove the ED from the HC */ - ohci_remove_ed(dev->ohci, req_ed); - - ohci_free_ed(req_ed); /* return it to the pool */ - - return 1; -} /* ohci_terminate_trans() */ - - -/********************************************************************** - * Control transfer processing - **********************************************************************/ - - -static DECLARE_WAIT_QUEUE_HEAD(control_wakeup); - -/* - * This is the handler that gets called when a control transaction - * completes. - * - * This function is called from the interrupt handler. - */ -static int ohci_control_completed(int stats, void *buffer, int len, void *dev_id) -{ - /* pass the TDs completion status back to control_msg */ - if (dev_id) { - int *completion_status = (int *)dev_id; - - switch (stats) { - case USB_ST_NOERROR: - case USB_ST_DATAOVERRUN: - *completion_status = len; - break; - default: - *completion_status = -stats; - } - } - - wake_up(&control_wakeup); - return 0; -} /* ohci_control_completed() */ - - -/* - * Send or receive a control message on a "pipe" - * - * The cmd parameter is a pointer to the 8 byte setup command to be - * sent. - * - * A control message contains: - * - The command itself - * - An optional data phase (if len > 0) - * - Status complete phase - * - * This function can NOT be called from an interrupt. - */ -static int ohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, - devrequest *cmd, void *data, int len, int timeout) -{ - struct ohci_device *dev = usb_to_ohci(usb_dev); - void *trans_handle; - struct ohci_td *setup_td, *status_td; - DECLARE_WAITQUEUE(wait, current); - int completion_status = USB_ST_TIMEOUT; - devrequest our_cmd; - - /* byte-swap fields of cmd if necessary */ - our_cmd = *cmd; - cpu_to_le16s(&our_cmd.value); - cpu_to_le16s(&our_cmd.index); - cpu_to_le16s(&our_cmd.length); - -#ifdef OHCI_DEBUG - if (MegaDebug) - printk(KERN_DEBUG "ohci_control_msg %p (ohci_dev: %p) pipe %x, cmd %p, data %p, len %d\n", usb_dev, dev, pipe, cmd, data, len); -#endif - - /* get a TD to send this control message with */ - setup_td = ohci_get_free_td(dev); - if (!setup_td) { - printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl setup]\n", dev); - return USB_ST_INTERNALERROR; - } - - /* - * Set the not accessed condition code, allow odd sized data, - * and set the data transfer type to SETUP. Setup DATA always - * uses a DATA0 packet. - * - * The setup packet contains a devrequest (usb.h) which - * will always be 8 bytes long. - */ - ohci_fill_new_td(setup_td, OHCI_TD_D_SETUP, TOGGLE_DATA0, - OHCI_TD_IOC_OFF, - &our_cmd, 8, /* cmd is always 8 bytes long */ - &completion_status, NULL); - - /* Allocate a TD for the control xfer status */ - status_td = ohci_get_free_td(dev); - if (!status_td) { - printk("usb-ohci: couldn't get TD for dev %p [cntl status]\n", dev); - ohci_free_td(setup_td); - return USB_ST_INTERNALERROR; - } - - /* The control status packet always uses a DATA1 - * Give "dev_id" the address of completion_status so that the - * TDs status can be passed back to us from the IRQ. */ - ohci_fill_new_td(status_td, - td_set_dir_in(usb_pipeout(pipe) | (len == 0)), - TOGGLE_DATA1, - 0 /* flags */, - NULL /* data */, 0 /* data len */, - &completion_status, ohci_control_completed); - set_td_endofchain(status_td); - status_td->next_td = 0; /* end of TDs */ - - /* - * Start the control transaction.. - * XXX should this come so soon? or... XXX - * XXX should it be put into generic_trans? XXX - */ - current->state = TASK_UNINTERRUPTIBLE; - add_wait_queue(&control_wakeup, &wait); - - trans_handle = ohci_generic_trans(usb_dev, pipe, - TOGGLE_DATA1, 1 /* round */, 1 /* autofree */, - &completion_status, NULL /* no data td handler */, - data, len, HCD_ED_CONTROL, - setup_td, status_td); - - /* did something go wrong? return an error */ - if (!trans_handle) { - remove_wait_queue(&control_wakeup, &wait); - ohci_free_td(setup_td); - ohci_free_td(status_td); - return USB_ST_INTERNALERROR; - } - - /* wait a user defined amount of time for it to complete */ - schedule_timeout(timeout); - - remove_wait_queue(&control_wakeup, &wait); - -#ifdef OHCI_DEBUG - /* - * NOTE! this debug code blatently assumes that the handle returned - * by ohci_generic_trans is the pointer to this transfers ED. - * (which it is at the moment) - * Also, since the TDs were autofreed, there is no guarantee that - * they haven't been reclaimed by another transfer by now... - */ - if (completion_status < 0) { - const char *what; - if (completion_status == USB_ST_TIMEOUT) - what = "timed out"; - else - what = cc_names[(-completion_status) & 0xf]; - printk(KERN_ERR "ohci_control_msg: %s on pipe %x cmd %x %x %x %x %x", - what, pipe, cmd->requesttype, cmd->request, - cmd->value, cmd->index, cmd->length); - if (usb_pipeout(pipe) && len > 0) { - int i; - printk(" data"); - for (i = 0; i < 16 && i < len; ++i) - printk(" %.2x", ((unsigned char *)data)[i]); - if (i < len) - printk(" ..."); - } - printk("\n"); - if (MegaDebug && completion_status < 0) { - struct ohci_ed *control_ed = (struct ohci_ed *) trans_handle; - printk(KERN_DEBUG "control_ed at %p:\n", control_ed); - show_ohci_ed(control_ed); - if (ed_head_td(control_ed) != ed_tail_td(control_ed)) - show_ohci_td_chain(bus_to_virt(ed_head_td(control_ed))); - printk(KERN_DEBUG "setup TD at %p:\n", setup_td); - show_ohci_td(setup_td); - } - } else if (!usb_pipeout(pipe)) { - unsigned char *q = data; - int i; - printk(KERN_DEBUG "ctrl msg %x %x %x %x %x on pipe %x returned:", - cmd->requesttype, cmd->request, cmd->value, cmd->index, - cmd->length, pipe); - for (i = 0; i < len; ++i) { - if (i % 16 == 0) - printk("\n" KERN_DEBUG); - printk(" %x", q[i]); - } - printk("\n"); - } - - if (MegaDebug) { - struct ohci_ed *control_ed = (struct ohci_ed *) trans_handle; - /* complete transaction debugging output (after) */ - printk(KERN_DEBUG " *after* Control ED %lx:\n", virt_to_bus(control_ed)); - show_ohci_ed(control_ed); - printk(KERN_DEBUG " *after* Control TD chain:\n"); - show_ohci_td_chain(setup_td); - printk(KERN_DEBUG " *after* OHCI Controller Status:\n"); - show_ohci_status(dev->ohci); - } -#endif - - /* no TD cleanup, the TDs were auto-freed as they finished */ - - /* remove the generic_trans ED from the HC and free it */ - ohci_terminate_trans(usb_dev, trans_handle); - - return completion_status; -} /* ohci_control_msg() */ - - -/********************************************************************** - * Bulk transfer processing - **********************************************************************/ - - -/* - * Request to send or receive bulk data. The handler() function - * will be called as each OHCI TD in the transfer completes or is - * aborted due to an error. - * - * IMPORTANT NOTE: This means the handler may be called multiple - * times for a large (more than one 4096 byte page) request until - * the transfer is finished. - * - * Returns: a pointer to the ED being used for this request as the - * bulk request handle. - */ -static void * ohci_request_bulk(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, void* data, int len, void* dev_id) -{ -#ifdef OHCI_DEBUG - struct ohci_device *dev = usb_to_ohci(usb_dev); - if (MegaDebug) - printk(KERN_DEBUG "ohci_request_bulk() ohci_dev %p, handler %p, pipe %x, data %p, len %d, dev_id %p\n", dev, handler, pipe, data, len, dev_id); -#endif - - return ohci_generic_trans(usb_dev, pipe, - TOGGLE_AUTO, - 0 /* round */, 1 /* autofree */, - dev_id, handler, data, len, - HCD_ED_BULK, - NULL /* no setup_td */, NULL /* no status_td */ ); -} /* ohci_request_bulk() */ - - -/* - * Terminate a bulk transfer requested using ohci_request_bulk. - * - * This function is NOT safe to call from an interrupt. - */ -static int ohci_terminate_bulk(struct usb_device *usb_dev, void * handle) -{ - return ohci_terminate_trans(usb_dev, handle); -} /* ohci_terminate_bulk() */ - - -/* - * Internal state for an ohci_bulk_request_msg - */ -struct ohci_bulk_msg_request_state { - struct usb_device *usb_dev; - unsigned int pipe; /* usb "pipe" */ - void *data; /* ptr to data */ - int length; /* length to transfer */ - int _bytes_done; /* bytes transferred so far */ - unsigned long *bytes_transferred_p; /* where to increment */ - void *dev_id; /* pass to the completion handler */ - usb_device_irq completion; /* completion handler */ -}; - -/* - * this handles the individual TDs of a (possibly) larger bulk - * request. It keeps track of the total bytes transferred, calls the - * final completion handler, etc. - */ -static int ohci_bulk_msg_td_handler(int stats, void *buffer, int len, void *dev_id) -{ - struct ohci_bulk_msg_request_state *req; - - req = (struct ohci_bulk_msg_request_state *) dev_id; - -#ifdef OHCI_DEBUG - if (MegaDebug) - printk(KERN_DEBUG "ohci_bulk_td_handler stats %x, buffer %p, len %d, req %p\n", stats, buffer, len, req); -#endif - - /* only count TDs that were completed successfully */ - if (stats == USB_ST_NOERROR || stats == USB_ST_DATAUNDERRUN) /*DEN*/ - req->_bytes_done += len; - -#ifdef OHCI_DEBUG - if (MegaDebug && req->_bytes_done) { - int i; - printk(KERN_DEBUG " %d bytes, bulk data:", req->_bytes_done); - for (i = 0; i < 16 && i < req->_bytes_done; ++i) - printk(" %.2x", ((unsigned char *)buffer)[i]); - if (i < req->_bytes_done) - printk(" ..."); - printk("\n"); - } -#endif - - /* call the real completion handler when done or on an error */ - if ((stats != USB_ST_NOERROR) || - (req->_bytes_done >= req->length && req->completion != NULL)) { - *req->bytes_transferred_p += req->_bytes_done; -#ifdef OHCI_DEBUG - if (MegaDebug) - printk(KERN_DEBUG "usb-ohci: bulk request %p ending\n", req); -#endif - req->completion(stats, buffer, req->_bytes_done, req->dev_id); - } - - return 0; /* do not re-queue the TD */ -} /* ohci_bulk_msg_td_handler() */ - - - - -static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup); - - -static int ohci_bulk_msg_completed(int stats, void *buffer, int len, void *dev_id) -{ -#ifdef OHCI_DEBUG - printk("ohci_bulk_msg_completed %x, %p, %d, %p\n", stats, buffer, len, dev_id); -#endif - if (dev_id != NULL) { - int *completion_status = (int *)dev_id; - *completion_status = stats; - } - - wake_up(&bulk_wakeup); - return 0; /* don't requeue the TD */ -} /* ohci_bulk_msg_completed() */ - - -static int ohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *bytes_transferred_p, int timeout) -{ - DECLARE_WAITQUEUE(wait, current); - int completion_status = USB_ST_INTERNALERROR; - struct ohci_bulk_msg_request_state req; - struct ohci_ed *req_ed; - -#ifdef OHCI_DEBUG - if (MegaDebug) - printk(KERN_DEBUG "ohci_bulk_msg %p pipe %x, data %p, len %d, bytes_transferred %p\n", usb_dev, pipe, data, len, bytes_transferred_p); -#endif - - /* initialize bytes transferred to nothing */ - *bytes_transferred_p = 0; - - /* Hopefully this is similar to the "URP" (USB Request Packet) code - * that michael gee is working on... */ - req.usb_dev = usb_dev; - req.pipe = pipe; - req.data = data; - req.length = len; - req.bytes_transferred_p = bytes_transferred_p; - req.dev_id = &completion_status; - req.completion = ohci_bulk_msg_completed; - req._bytes_done = 0; - if (bytes_transferred_p) - *bytes_transferred_p = 0; - - if (usb_endpoint_halted(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) - && usb_clear_halt(usb_dev, usb_pipeendpoint(pipe) | (pipe & 0x80))) - return USB_ST_STALL; - - /* - * Start the transaction.. - */ - current->state = TASK_UNINTERRUPTIBLE; - add_wait_queue(&bulk_wakeup, &wait); - - /* - * Request to send or receive bulk data for a blocking bulk_msg call. - * - * req.bytes_transferred_p is a pointer to an integer that - * will be set to the number of bytes that have been successfully - * transferred upon completion. The interrupt handler will update it - * after each internal TD completes successfully. - */ - req_ed = ohci_request_bulk(usb_dev, pipe, - ohci_bulk_msg_td_handler, - data, len, &req); - - /* wait for a caller specified time... */ - schedule_timeout(timeout); - - /* completion_status will only stay in this state of the - * request never finished */ - if (completion_status == USB_ST_INTERNALERROR) { - struct ohci_device *dev = usb_to_ohci(usb_dev); - struct ohci_regs *regs = dev->ohci->regs; - -#ifdef OHCI_DEBUG - printk(KERN_DEBUG "ohci_bulk_msg timing out\n"); -#endif - /* XXX This code should go into a function used to stop - * a previously requested bulk transfer. -greg */ - - /* stop the transfer & collect the number of bytes */ - ohci_wait_for_ed_safe(regs, req_ed); - - /* Get the number of bytes transferred out of the head TD - * on the ED if it didn't finish while we were waiting. */ - if ( ed_head_td(req_ed) && - (ed_head_td(req_ed) != ed_tail_td(req_ed)) ) { - struct ohci_td *partial_td; - partial_td = bus_to_virt(ed_head_td(req_ed)); - -#ifdef OHCI_DEBUG - if (MegaDebug) { - show_ohci_td(partial_td); - } -#endif - /* Record the bytes as transferred */ - *bytes_transferred_p += ohci_td_bytes_done(partial_td); - - /* If there was an unreported error, return it. - * Otherwise return a timeout */ - completion_status = OHCI_TD_CC_GET(partial_td->info); - if (completion_status == USB_ST_NOERROR) { - completion_status = USB_ST_TIMEOUT; - } - } - - } - - remove_wait_queue(&bulk_wakeup, &wait); - - /* remove the ED from the HC */ - ohci_remove_norm_ed_from_hw(usb_to_ohci(usb_dev)->ohci, req_ed); - - /* save the toggle value back into the usb_dev */ - usb_settoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), - ohci_ed_carry(req_ed)); - - ohci_free_ed(req_ed); /* return it to the pool */ - -#ifdef OHCI_DEBUG - if (completion_status != 0 || MegaDebug) - printk(KERN_DEBUG "ohci_bulk_msg done, status %x (bytes_transferred = %ld).\n", completion_status, *bytes_transferred_p); -#endif - - return completion_status; -} /* ohci_bulk_msg() */ - - -/* .......... */ - - - -/* - * Allocate a new USB device attached to this OHCI controller - */ -static int ohci_dev_allocate(struct usb_device *usb_dev) -{ - struct ohci_device *dev; - int idx; - - /* - * Allocate an OHCI device (EDs and TDs for this device) - */ - dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -1; - - memset(dev, 0, sizeof(*dev)); - - /* Initialize all EDs in a new device with the skip flag so that - * they are ignored by the controller until set otherwise. */ - for (idx = 0; idx < NUM_EDS; ++idx) { - dev->ed[idx].status = cpu_to_le32(OHCI_ED_SKIP); - } - - /* - * Link them together - */ - usb_dev->hcpriv = dev; - dev->usb = usb_dev; - - if (usb_dev->parent) - dev->ohci = usb_to_ohci(usb_dev->parent)->ohci; - - return 0; -} /* ohci_dev_allocate() */ - - -/* - * Free a usb device on this ohci controller. - */ -static int ohci_dev_deallocate(struct usb_device *usb_dev) -{ - struct ohci_device *dev = usb_to_ohci(usb_dev); - - ohci_remove_device(dev->ohci, usb_dev->devnum); - - kfree(usb_to_ohci(usb_dev)); - return 0; -} - -static int ohci_get_current_frame_number(struct usb_device *usb_dev) -{ - return USB_ST_NOTSUPPORTED; -} - -static int ohci_alloc_isochronous (struct usb_device *usb_dev, -unsigned int pipe, int frame_count, void *context, -struct usb_isoc_desc **isocdesc) -{ - return USB_ST_NOTSUPPORTED; -} - -static void ohci_delete_isochronous (struct usb_isoc_desc *isocdesc) -{ - return; -} - -static int ohci_sched_isochronous (struct usb_isoc_desc *isocdesc, -struct usb_isoc_desc *pr_isocdesc) -{ - return USB_ST_NOTSUPPORTED; -} - -static int ohci_unsched_isochronous (struct usb_isoc_desc *isocdesc) -{ - return USB_ST_NOTSUPPORTED; -} - -/* - * functions for the generic USB driver - */ -struct usb_operations ohci_device_operations = { - ohci_dev_allocate, - ohci_dev_deallocate, - ohci_control_msg, - ohci_bulk_msg, - ohci_request_irq, - ohci_release_irq, - ohci_request_bulk, - ohci_terminate_bulk, - ohci_get_current_frame_number, - ohci_alloc_isochronous, - ohci_delete_isochronous, - ohci_sched_isochronous, - ohci_unsched_isochronous -}; - - -/* - * Reset an OHCI controller. Returns >= 0 on success. - * - * Afterwards the HC will be in the "suspend" state which prevents you - * from writing to some registers. Bring it to the operational state - * ASAP. - */ -static int reset_hc(struct ohci *ohci) -{ - int timeout = 10000; /* prevent an infinite loop */ - -#if 0 - printk(KERN_INFO "usb-ohci: resetting HC %p\n", ohci); -#endif - - writel(~0x0, &ohci->regs->intrdisable); /* Disable HC interrupts */ - writel(1, &ohci->regs->cmdstatus); /* HC Reset */ - writel_mask(0x3f, &ohci->regs->control); /* move to UsbReset state */ - - while ((readl(&ohci->regs->cmdstatus) & OHCI_CMDSTAT_HCR) != 0) { - if (!--timeout) { - printk(KERN_ERR "usb-ohci: USB HC reset timed out!\n"); - return -1; - } - udelay(1); - } - - printk(KERN_DEBUG "usb-ohci: HC %p reset.\n", ohci); - - return 0; -} /* reset_hc() */ - - -/* - * Reset and start an OHCI controller. Returns >= 0 on success. - */ -static int start_hc(struct ohci *ohci) -{ - int ret = 0; - int fminterval; - __u32 what_to_enable; - - struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); - -#if 0 - printk(KERN_DEBUG "entering start_hc %p\n", ohci); -#endif - - if (reset_hc(ohci) < 0) - return -1; - - /* restore registers cleared by the reset */ - writel(virt_to_bus(root_hub->hcca), &ohci->regs->hcca); - - /* - * fminterval has to be 11999 (it can be adjusted +/- 1 - * to sync with other things if necessary). - */ - fminterval = 11999; - - /* Start periodic transfers at 90% of fminterval (fmremaining - * counts down; this will put them in the first 10% of the - * frame). */ - writel((fminterval * 9) / 10, &ohci->regs->periodicstart); - - /* Set largest data packet counter and frame interval. */ - fminterval |= ((fminterval - 210) * 6 / 7) << 16; - writel(fminterval, &ohci->regs->fminterval); - - /* Set low-speed threshold (value from MacOS) */ - writel(1576, &ohci->regs->lsthresh); - - /* - * FNO (frame number overflow) could be enabled... they - * occur every 32768 frames (every 32-33 seconds). This is - * useful for debugging and as a bus heartbeat. -greg - */ - /* Choose the interrupts we care about */ - what_to_enable = OHCI_INTR_MIE | -#ifdef OHCI_RHSC_INT - OHCI_INTR_RHSC | -#endif - /* | OHCI_INTR_FNO */ - OHCI_INTR_WDH; - writel( what_to_enable, &ohci->regs->intrenable); - - /* Enter the USB Operational state & start the frames a flowing.. */ - writel_set(OHCI_USB_OPER, &ohci->regs->control); - - /* Enable control lists, claim the host controller */ - writel_set( - OHCI_USB_IE | OHCI_USB_CLE | OHCI_USB_BLE, - &ohci->regs->control); - - /* Force global power enable -gal@cs.uni-magdeburg.de */ - /* - * This turns on global power switching for all the ports - * and tells the HC that all of the ports should be powered on - * all of the time. - * - * TODO: This could be battery draining for laptops.. We - * should implement power switching. - */ - writel_set( OHCI_ROOT_A_NPS, &ohci->regs->roothub.a ); - writel_mask( ~((__u32)OHCI_ROOT_A_PSM), &ohci->regs->roothub.a ); - - /* Turn on power to the root hub ports (thanks Roman!) */ - writel( OHCI_ROOT_LPSC, &ohci->regs->roothub.status ); - - printk(KERN_INFO "usb-ohci: host controller operational\n"); - - return ret; -} /* start_hc() */ - - -/* - * Reset a root hub port - */ -static void ohci_reset_port(struct ohci *ohci, unsigned int port) -{ - int status; - - /* Don't allow overflows. */ - if (port >= MAX_ROOT_PORTS) { - printk(KERN_ERR "usb-ohci: bad port #%d in ohci_reset_port\n", port); - port = MAX_ROOT_PORTS-1; - } - - writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]); /* Reset */ - - /* - * Wait for the reset to complete. - */ - wait_ms(20); - - /* check port status to see that the reset completed */ - status = readl(&ohci->regs->roothub.portstatus[port]); - if (status & PORT_PRS) { - /* reset failed, try harder? */ - printk(KERN_ERR "usb-ohci: port %d reset failed, retrying\n", port); - writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]); - wait_ms(50); - } - - /* TODO we might need to re-enable the port here or is that - * done elsewhere? */ - -} /* ohci_reset_port */ - - -/* - * This gets called if the connect status on the root hub changes. - */ -static void ohci_connect_change(struct ohci * ohci, int port) -{ - struct usb_device *usb_dev; - struct ohci_device *dev; - struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); - /* memory I/O address of the port status register */ - __u32 *portaddr = &ohci->regs->roothub.portstatus[port]; - int portstatus; - -#ifdef OHCI_DEBUG - printk(KERN_DEBUG "ohci_connect_change on port %d\n", port); -#endif - - /* - * Because of the status change we have to forget - * everything we think we know about the device - * on this root hub port. It may have changed. - */ - usb_disconnect(root_hub->usb->children + port); - - portstatus = readl(portaddr); - - /* disable the port if nothing is connected */ - if (!(portstatus & PORT_CCS)) { - writel(PORT_CCS, portaddr); - /* We need to reset the CSC bit -after- disabling the - * port because it causes the CSC bit to come on - * again... */ - wait_ms(20); - writel(PORT_CSC, portaddr); -#ifdef OHCI_DEBUG - printk(KERN_DEBUG "ohci port %d disabled, nothing connected.\n", port); -#endif - return; - } - - /* - * Allocate a device for the new thingy that's been attached - */ - usb_dev = usb_alloc_dev(root_hub->usb, root_hub->usb->bus); - if (!usb_dev) - return; - - dev = usb_dev->hcpriv; - - usb_connect(dev->usb); - - /* link it into the bus's device tree */ - root_hub->usb->children[port] = usb_dev; - - wait_ms(200); /* wait for powerup; XXX is this needed? */ - ohci_reset_port(ohci, port); - - /* Get information on speed by using LSD */ - usb_dev->slow = readl(portaddr) & PORT_LSDA ? 1 : 0; - - /* - * Do generic USB device tree processing on the new device. - */ - usb_new_device(usb_dev); - -} /* ohci_connect_change() */ - - -/* - * This gets called when the root hub configuration - * has changed. Just go through each port, seeing if - * there is something interesting happening. - */ -static void ohci_check_configuration(struct ohci *ohci) -{ - struct ohci_regs *regs = ohci->regs; - int num = 0; - int maxport = readl(&ohci->regs->roothub) & 0xff; - __u32 rh_change_flags = PORT_CSC | PORT_PESC; /* root hub status changes */ - -#ifdef OHCI_DEBUG - printk(KERN_DEBUG "entering ohci_check_configuration %p\n", ohci); -#endif - - do { - __u32 *portstatus_p = ®s->roothub.portstatus[num]; - if (readl(portstatus_p) & rh_change_flags) { - /* acknowledge the root hub status changes */ - writel_set(rh_change_flags, portstatus_p); - /* disable the port if nothing is on it */ - /* check the port for a nifty device */ - ohci_connect_change(ohci, num); - } - } while (++num < maxport); - -#if 0 - printk(KERN_DEBUG "leaving ohci_check_configuration %p\n", ohci); -#endif -} /* ohci_check_configuration() */ - - - -/* - * Check root hub port status and wake the control thread up if - * anything has changed. - * - * This function is called from the interrupt handler. - */ -static void ohci_root_hub_events(struct ohci *ohci) -{ - int num = 0; - struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); - int maxport = root_hub->usb->maxchild; - - if (!waitqueue_active(&ohci_configure)) - return; - do { - __u32 *portstatus_p = &ohci->regs->roothub.portstatus[num]; - if (readl(portstatus_p) & PORT_CSC) { - if (waitqueue_active(&ohci_configure)) - wake_up(&ohci_configure); - return; - } - } while (++num < maxport); - -} /* ohci_root_hub_events() */ - - -/* - * The done list is in reverse order; we need to process TDs in the - * order they were finished (FIFO). This function builds the FIFO - * list using the next_dl_td pointer. - * - * This function originally by Roman Weissgaerber (weissg@vienna.at) - * - * This function is called from the interrupt handler. - */ -static struct ohci_td * ohci_reverse_donelist(struct ohci * ohci) -{ - __u32 td_list_hc; - struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); - struct ohci_hcca *hcca = root_hub->hcca; - struct ohci_td *td_list = NULL; - struct ohci_td *td_rev = NULL; - - td_list_hc = le32_to_cpup(&hcca->donehead) & 0xfffffff0; - hcca->donehead = 0; - - while(td_list_hc) { - td_list = (struct ohci_td *) bus_to_virt(td_list_hc); - td_list->next_dl_td = td_rev; - td_rev = td_list; - td_list_hc = le32_to_cpup(&td_list->next_td) & 0xfffffff0; - } - - return td_list; -} /* ohci_reverse_donelist() */ - -/* - * Look at the ed (and td if necessary) - * and return its direction as 0 = IN, 1 = OUT. - */ -int ed_get_dir (struct ohci_ed *ed, struct ohci_td *td) -{ - __u32 status; - - status = le32_to_cpu(ed->status) & OHCI_ED_D; /* keep only the Direction bits */ - if (status == OHCI_ED_D_IN) return 0; - if (status == OHCI_ED_D_OUT) return 1; - - /* but if status == 0 or 3, look at the td for the Direction */ - status = le32_to_cpu(td->info) & OHCI_TD_D; /* keep only the Direction bits */ - if (status == OHCI_TD_D_IN) return 0; - return 1; -} - -/* - * Collect this interrupt's goodies off of the list of finished TDs - * that the OHCI controller is kind enough to setup for us. - * - * This function is called from the interrupt handler. - */ -static void ohci_reap_donelist(struct ohci *ohci) -{ - struct ohci_td *td; /* used for walking the list */ - - /* um... isn't this dangerous to do in an interrupt handler? -greg */ - /* nope. -paulus */ - spin_lock(&ohci_edtd_lock); - - /* create the FIFO ordered donelist */ - td = ohci_reverse_donelist(ohci); - - while (td != NULL) { - struct ohci_td *next_td = td->next_dl_td; - int cc = OHCI_TD_CC_GET(le32_to_cpup(&td->info)); - struct ohci_ed *ed = td->ed; - - if (td_dummy(*td)) - printk(KERN_ERR "usb-ohci: yikes! reaping dummy TD\n"); - -#ifdef OHCI_DEBUG - if (cc != 0 && MegaDebug) { - printk("cc=%s on td %p (ed %p)\n", cc_names[cc], td, ed); - show_ohci_td(td); - show_ohci_ed(ed); - if (ed_head_td(ed) != ed_tail_td(ed)) - show_ohci_td_chain(bus_to_virt(ed_head_td(ed))); - } -#endif - - if (cc == USB_ST_STALL) { - /* mark endpoint as halted */ - usb_endpoint_halt(ed->ohci_dev->usb, ed_get_en(ed), ed_get_dir(ed, td)); - } - - if (cc != 0 && ohci_ed_halted(ed) && !td_endofchain(*td)) { - /* - * There was an error on this TD and the ED - * is halted, and this was not the last TD - * of the transaction, so there will be TDs - * to clean off the ED. - */ - struct ohci_td *tail_td = bus_to_virt(ed_tail_td(ed)); - struct ohci_td *ntd; - - ohci_free_td(td); - td = ntd = bus_to_virt(ed_head_td(ed)); - while (td != tail_td) { - ntd = bus_to_virt(le32_to_cpup(&td->next_td)); - if (td_endofchain(*td)) - break; - - printk(KERN_DEBUG "usb-ohci: skipping TD %p\n", td); - ohci_free_td(td); - - td = ntd; - } - /* Set the ED head past the ones we cleaned - off, and clear the halted flag */ - printk(KERN_DEBUG "usb-ohci: restarting ED %p at TD %p\n", ed, ntd); - set_ed_head_td(ed, virt_to_bus(ntd)); - ohci_unhalt_ed(ed); - /* If we didn't find an endofchain TD, give up */ - if (td == tail_td) { - td = next_td; - continue; - } - } - - /* Check if TD should be re-queued */ - if ((td->completed != NULL) && - (td->completed(cc, td->data, ohci_td_bytes_done(td), td->dev_id))) { - /* Mark the TD as active again: - * Set the not accessed condition code - * Reset the Error count - */ - td->info |= cpu_to_le32(OHCI_TD_CC_NEW); - clear_td_errorcount(td); - /* reset the toggle field to TOGGLE_AUTO (0) */ - td->info &= cpu_to_le32(~OHCI_TD_DT); - - /* point it back to the start of the data buffer */ - td->cur_buf = cpu_to_le32(virt_to_bus(td->data)); - - /* insert it back on its ED */ - td->next_td = 0; - ohci_add_tds_to_ed(td, ed); - /* ohci_unhalt_ed(td->ed); */ - } else { - /* return it to the pool of free TDs */ - if (can_auto_free(*td)) - ohci_free_td(td); - } - - td = next_td; - } - - spin_unlock(&ohci_edtd_lock); -} /* ohci_reap_donelist() */ - - -/* - * Get annoyed at the controller for bothering us. - * This pretty much follows the OHCI v1.0a spec, section 5.3. - */ -static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r) -{ - struct ohci *ohci = __ohci; - struct ohci_regs *regs = ohci->regs; - struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); - struct ohci_hcca *hcca = root_hub->hcca; - __u32 status, context; - - /* Save the status of the interrupts that are enabled */ - status = readl(®s->intrstatus); - status &= readl(®s->intrenable); - - /* make context = the interrupt status bits that we care about */ - if (hcca->donehead != 0) { - context = OHCI_INTR_WDH; /* hcca donehead needs processing */ - if (hcca->donehead & cpu_to_le32(1)) { - context |= status; /* other status change to check */ - } - } else { - context = status; - if (!context) { - /* TODO increment a useless interrupt counter here */ - return; - } - } - - /* Disable HC interrupts */ /* why? - paulus */ - writel(OHCI_INTR_MIE, ®s->intrdisable); - -#if 0 - /* Only do this for SERIOUS debugging, be sure kern.debug logs - * are not going to the console as this can cause your - * machine to lock up if so... -greg */ - show_ohci_status(ohci); -#endif - - /* Process the done list */ - if (context & OHCI_INTR_WDH) { - /* See which TD's completed.. */ - ohci_reap_donelist(ohci); - - /* reset the done queue and tell the controller */ - hcca->donehead = 0; /* XXX already done in ohci_reverse_donelist */ - writel(OHCI_INTR_WDH, ®s->intrstatus); - - context &= ~OHCI_INTR_WDH; /* mark this as checked */ - } - -#ifdef OHCI_RHSC_INT - /* NOTE: this is very funky on some USB controllers (ie: it - * doesn't work right). Using the ohci_timer instead to poll - * the root hub is a much better choice. */ - /* Process any root hub status changes */ - if (context & OHCI_INTR_RHSC) { - /* Wake the thread to process root hub events */ - if (waitqueue_active(&ohci_configure)) - wake_up(&ohci_configure); - - writel(OHCI_INTR_RHSC, ®s->intrstatus); - /* - * Don't unset RHSC in context; it should be disabled. - * The control thread will re-enable it after it has - * checked the root hub status. - */ - } -#endif - - /* Start of Frame interrupts, used during safe ED removal */ - if (context & (OHCI_INTR_SF)) { - writel(OHCI_INTR_SF, ®s->intrstatus); - if (waitqueue_active(&start_of_frame_wakeup)) - wake_up(&start_of_frame_wakeup); - /* Do NOT mark the frame start interrupt as checked - * as we don't want to receive any more of them until - * asked. */ - } - - /* Check those "other" pesky bits */ - if (context & (OHCI_INTR_FNO)) { - writel(OHCI_INTR_FNO, ®s->intrstatus); - context &= ~OHCI_INTR_FNO; /* mark this as checked */ - } - if (context & OHCI_INTR_SO) { - writel(OHCI_INTR_SO, ®s->intrstatus); - context &= ~OHCI_INTR_SO; /* mark this as checked */ - } - if (context & OHCI_INTR_RD) { - writel(OHCI_INTR_RD, ®s->intrstatus); - context &= ~OHCI_INTR_RD; /* mark this as checked */ - } - if (context & OHCI_INTR_UE) { - /* TODO: need to have the control thread reset the - * controller now and keep a count of unrecoverable - * errors. If there are too many, it should just shut - * the broken controller down entirely. */ - writel(OHCI_INTR_UE, ®s->intrstatus); - context &= ~OHCI_INTR_UE; /* mark this as checked */ - } - if (context & OHCI_INTR_OC) { - writel(OHCI_INTR_OC, ®s->intrstatus); - context &= ~OHCI_INTR_OC; /* mark this as checked */ - } - - /* Mask out any remaining unprocessed or unmasked interrupts - * so that we don't get any more of them. */ - if (context & ~OHCI_INTR_MIE) { - writel(context, ®s->intrdisable); - } - - /* Re-enable HC interrupts */ - writel(OHCI_INTR_MIE, ®s->intrenable); - -} /* ohci_interrupt() */ - - -/* - * Allocate the resources required for running an OHCI controller. - * Host controller interrupts must not be running while calling this - * function or the penguins will get angry. - * - * The mem_base parameter must be the usable -virtual- address of the - * host controller's memory mapped I/O registers. - */ -static struct ohci *alloc_ohci(void* mem_base) -{ - int i; - struct ohci *ohci; - struct usb_bus *bus; - struct ohci_device *dev; - struct usb_device *usb; - struct ohci_hcca *hcca; - u32 ctrl; - -#if 0 - printk(KERN_DEBUG "entering alloc_ohci %p\n", mem_base); -#endif - - ohci = kmalloc(sizeof(*ohci), GFP_KERNEL); - if (!ohci) - return NULL; - memset(ohci, 0, sizeof(*ohci)); - - ohci->irq = -1; - ohci->regs = mem_base; - INIT_LIST_HEAD(&ohci->interrupt_list); - - /* - * Allocate the Host Controller Communications Area on a 256 - * byte boundary. XXX take the easy way out and just grab a - * page as that's guaranteed to have a nice boundary. - */ - hcca = (struct ohci_hcca *) __get_free_page(GFP_KERNEL); - if (hcca == NULL) - goto au_free_ohci; - memset(hcca, 0, sizeof(struct ohci_hcca)); - - bus = usb_alloc_bus(&ohci_device_operations); - if (!bus) - goto au_free_hcca; - - ohci->bus = bus; - bus->hcpriv = ohci; - - /* - * Allocate the USB device structure and root hub. - * - * Here we allocate our own root hub and TDs as well as the - * OHCI host controller communications area. The HCCA is just - * a nice pool of memory with pointers to endpoint descriptors - * for the different interrupts. - */ - usb = usb_alloc_dev(NULL, bus); - if (!usb) - goto au_free_bus; - - usb->bus = bus; - - dev = usb_to_ohci(usb); - dev->ohci = ohci; - dev->hcca = hcca; - - ohci->bus->root_hub = ohci_to_usb(dev); - - /* Initialize the root hub */ - dev->ohci = ohci; /* link back to the controller */ - - /* Tell the controller where the HCCA is */ - writel(virt_to_bus(dev->hcca), &ohci->regs->hcca); - -#if 0 - printk(KERN_DEBUG "usb-ohci: HCCA allocated at %p (bus %p)\n", dev->hcca, (void*)virt_to_bus(dev->hcca)); -#endif - - /* Get the number of ports on the root hub */ - usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff; - if (usb->maxchild > MAX_ROOT_PORTS) { - printk(KERN_INFO "usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS); - usb->maxchild = MAX_ROOT_PORTS; - } - if (usb->maxchild < 1) { - printk(KERN_ERR "usb-ohci: Less than one root hub port? Impossible!\n"); - usb->maxchild = 1; - } - printk(KERN_DEBUG "usb-ohci: %d root hub ports found\n", usb->maxchild); - - - /* - * Fix up legacy mode - */ - - ctrl = readl(&ohci->regs->control); - if(ctrl&OHCI_USB_IR) - { - int ct = 0; - /* Ask SMM for the controls */ - writel(8, &ohci->regs->cmdstatus); - printk(KERN_INFO "usb-ohci: switching interface from legacy mode.\n"); - while((readl(&ohci->regs->control)&OHCI_USB_IR) && ct < 250) - { - udelay(100); - ct++; - } - } - - /* - * Initialize the ED polling "tree" (for simplicity's sake in - * this driver many nodes in the tree will be identical) - */ - dev->ed[ED_INT_32].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_16])); - dev->ed[ED_INT_16].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_8])); - dev->ed[ED_INT_8].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_4])); - dev->ed[ED_INT_4].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_2])); - dev->ed[ED_INT_2].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_1])); - - /* - * Initialize the polling table to call interrupts at the - * intended intervals. Note that these EDs are just - * placeholders. They have their SKIP bit set and are used as - * list heads to insert real EDs onto. - */ - dev->hcca->int_table[0] = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_1])); - for (i = 1; i < NUM_INTS; i++) { - if (i & 16) - dev->hcca->int_table[i] = - cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_32])); - if (i & 8) - dev->hcca->int_table[i] = - cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_16])); - if (i & 4) - dev->hcca->int_table[i] = - cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_8])); - if (i & 2) - dev->hcca->int_table[i] = - cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_4])); - if (i & 1) - dev->hcca->int_table[i] = - cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_2])); - } - - /* - * Tell the controller where the control and bulk lists are. - * The lists start out empty. - */ - writel(0, &ohci->regs->ed_controlhead); - writel(0, &ohci->regs->ed_bulkhead); - -#ifdef OHCI_DEBUG - if (MegaDebug) { - printk(KERN_DEBUG "alloc_ohci(): controller\n"); - show_ohci_status(ohci); - } -#endif - -#if 0 - printk(KERN_DEBUG "leaving alloc_ohci %p\n", ohci); -#endif - - return ohci; - -au_free_bus: - usb_free_bus(bus); -au_free_hcca: - free_page((unsigned long)hcca); -au_free_ohci: - kfree(ohci); - return NULL; - -} /* alloc_ohci() */ - - -/* - * De-allocate all resoueces.. - */ -static void release_ohci(struct ohci *ohci) -{ - printk(KERN_INFO "Releasing OHCI controller 0x%p\n", ohci); - -#ifdef OHCI_TIMER - /* stop our timer */ - del_timer(&ohci_timer); -#endif - if (ohci->irq >= 0) { - free_irq(ohci->irq, ohci); - ohci->irq = -1; - } - - /* stop all OHCI interrupts */ - writel(~0x0, &ohci->regs->intrdisable); - - if (ohci->bus->root_hub) { - struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); - /* ensure that HC is stopped before releasing the HCCA */ - writel(OHCI_USB_SUSPEND, &ohci->regs->control); - free_page((unsigned long) root_hub->hcca); - kfree(ohci->bus->root_hub); - ohci->bus->root_hub = NULL; - } - - /* unmap the IO address space */ - iounmap(ohci->regs); - - kfree(ohci); - - MOD_DEC_USE_COUNT; - - /* If the ohci itself were dynamic we'd free it here */ - - printk(KERN_DEBUG "usb-ohci: HC resources released.\n"); -} /* release_ohci() */ - - -/* - * USB OHCI control thread - */ -static int ohci_control_thread(void * __ohci) -{ - struct ohci *ohci = (struct ohci *)__ohci; - - /* - * I'm unfamiliar with the SMP kernel locking.. where should - * this be released and what does it do? -greg - */ - lock_kernel(); - - /* - * This thread doesn't need any user-level access, - * so get rid of all of our resources.. - */ - printk(KERN_DEBUG "ohci-control thread code for 0x%p code at 0x%p\n", __ohci, &ohci_control_thread); - exit_mm(current); - exit_files(current); - /*exit_fs(current);*/ /* can't do kernel_thread if we do this */ - - strcpy(current->comm, "ohci-control"); - - usb_register_bus(ohci->bus); - - /* - * Damn the torpedoes, full speed ahead - */ - if (start_hc(ohci) < 0) { - printk(KERN_ERR "usb-ohci: failed to start the controller\n"); - release_ohci(ohci); - usb_deregister_bus(ohci->bus); - printk(KERN_INFO "leaving ohci_control_thread %p\n", __ohci); - return 0; - } - - for(;;) { - siginfo_t info; - int unsigned long signr; - - wait_ms(200); - - /* check the root hub configuration for changes. */ - ohci_check_configuration(ohci); - - /* re-enable root hub status change interrupts. */ -#ifdef OHCI_RHSC_INT - writel(OHCI_INTR_RHSC, &ohci->regs->intrenable); -#endif - - printk(KERN_DEBUG "ohci-control thread sleeping\n"); - interruptible_sleep_on(&ohci_configure); -#ifdef CONFIG_APM - if (apm_resume) { - apm_resume = 0; - if (start_hc(ohci) < 0) - break; - continue; - } -#endif - - /* - * If we were woken up by a signal, see if its useful, - * otherwise exit. - */ - if (signal_pending(current)) { - /* sending SIGUSR1 makes us print out some info */ - spin_lock_irq(¤t->sigmask_lock); - signr = dequeue_signal(¤t->blocked, &info); - spin_unlock_irq(¤t->sigmask_lock); - - if(signr == SIGUSR1) { - printk(KERN_DEBUG "OHCI status dump:\n"); - show_ohci_status(ohci); - } else if (signr == SIGUSR2) { - /* toggle mega TD/ED debugging output */ -#ifdef OHCI_DEBUG - MegaDebug = !MegaDebug; - printk(KERN_DEBUG "usb-ohci: Mega debugging %sabled.\n", - MegaDebug ? "en" : "dis"); -#endif - } else { - /* unknown signal, exit the thread */ - printk(KERN_DEBUG "usb-ohci: control thread for %p exiting on signal %ld\n", __ohci, signr); - break; - } - } - } /* for (;;) */ - - reset_hc(ohci); - release_ohci(ohci); - usb_deregister_bus(ohci->bus); - - return 0; -} /* ohci_control_thread() */ - - -#ifdef CONFIG_APM -static int handle_apm_event(apm_event_t event) -{ - static int down = 0; - - switch (event) { - case APM_SYS_SUSPEND: - case APM_USER_SUSPEND: - if (down) { - printk(KERN_DEBUG "usb-ohci: received extra suspend event\n"); - break; - } - down = 1; - break; - case APM_NORMAL_RESUME: - case APM_CRITICAL_RESUME: - if (!down) { - printk(KERN_DEBUG "usb-ohci: received bogus resume event\n"); - break; - } - down = 0; - if (waitqueue_active(&ohci_configure)) { - apm_resume = 1; - wake_up(&ohci_configure); - } - break; - } - return 0; -} /* handle_apm_event() */ -#endif - - -#ifdef OHCI_TIMER -/* - * Inspired by Iñaky's driver. This function is a timer routine that - * is called every OHCI_TIMER_FREQ ms. It polls the root hub for - * status changes as on my system the RHSC interrupt just doesn't - * play well with others.. (so RHSC is turned off by default in this - * driver) - * [my controller is a "SiS 7001 USB (rev 16)"] - * -greg - */ -static void ohci_timer_func (unsigned long ohci_ptr) -{ - struct ohci *ohci = (struct ohci*)ohci_ptr; - - ohci_root_hub_events(ohci); - - /* set the next timer */ - mod_timer(&ohci_timer, jiffies + ((OHCI_TIMER_FREQ*HZ)/1000)); - -} /* ohci_timer_func() */ -#endif - - -/* - * Increment the module usage count, start the control thread and - * return success if the controller is good. - */ -static int found_ohci(int irq, void* mem_base) -{ - int retval; - struct ohci *ohci; - -#if 0 - printk(KERN_DEBUG "entering found_ohci %d %p\n", irq, mem_base); -#endif - - /* Allocate the running OHCI structures */ - ohci = alloc_ohci(mem_base); - if (!ohci) { - return -ENOMEM; - } - -#ifdef OHCI_TIMER - init_timer(&ohci_timer); - ohci_timer.expires = jiffies + ((OHCI_TIMER_FREQ*HZ)/1000); - ohci_timer.data = (unsigned long)ohci; - ohci_timer.function = ohci_timer_func; - add_timer(&ohci_timer); -#endif - - retval = -EBUSY; - if (request_irq(irq, ohci_interrupt, SA_SHIRQ, "usb-ohci", ohci) == 0) { - int pid; - - ohci->irq = irq; - -#ifdef OHCI_DEBUG - printk(KERN_DEBUG "usb-ohci: forking ohci-control thread for 0x%p\n", ohci); -#endif - - /* fork off the handler */ - pid = kernel_thread(ohci_control_thread, ohci, - CLONE_FS | CLONE_FILES | CLONE_SIGHAND); - if (pid >= 0) { - return 0; - } - - retval = pid; - } else { - printk(KERN_ERR "usb-ohci: Couldn't allocate interrupt %d\n", irq); - } - release_ohci(ohci); - -#ifdef OHCI_DEBUG - printk(KERN_DEBUG "leaving found_ohci %d %p\n", irq, mem_base); -#endif - - return retval; -} /* found_ohci() */ - - -/* - * If this controller is for real, map the IO memory and proceed - */ -static int init_ohci(struct pci_dev *dev) -{ - unsigned long mem_base = dev->resource[0].flags; - - /* If its OHCI, its memory */ - if (mem_base & PCI_BASE_ADDRESS_SPACE_IO) - return -ENODEV; - - pci_set_master(dev); - - /* Get the memory address and map it for IO */ - mem_base = dev->resource[0].start; - - /* no interrupt won't work... */ - if (dev->irq == 0) { - printk(KERN_ERR "usb-ohci: no irq assigned? check your BIOS settings.\n"); - return -ENODEV; - } - - /* - * FIXME ioremap_nocache isn't implemented on all CPUs (such - * as the Alpha) [?] What should I use instead... - * - * The iounmap() is done on in release_ohci. - */ - mem_base = (unsigned long) ioremap_nocache(mem_base, 4096); - - if (!mem_base) { - printk(KERN_ERR "Error mapping OHCI memory\n"); - return -EFAULT; - } - MOD_INC_USE_COUNT; - -#ifdef OHCI_DEBUG - printk(KERN_INFO "usb-ohci: Warning! Gobs of debugging output has been enabled.\n"); - printk(KERN_INFO " Check your kern.debug logs for the bulk of it.\n"); -#endif - - if (found_ohci(dev->irq, (void *) mem_base) < 0) { - MOD_DEC_USE_COUNT; - return -1; - } - - return 0; -} /* init_ohci() */ - -/* TODO this should be named following Linux convention and go in pci.h */ -#define PCI_CLASS_SERIAL_USB_OHCI ((PCI_CLASS_SERIAL_USB << 8) | 0x0010) - -/* - * Search the PCI bus for an OHCI USB controller and set it up - * - * If anyone wants multiple controllers this will need to be - * updated.. Right now, it just picks the first one it finds. - */ -int ohci_init(void) -{ - int retval; - struct pci_dev *dev = NULL; - /*u8 type;*/ - - if (sizeof(struct ohci_device) > 4096) { - printk(KERN_ERR "usb-ohci: struct ohci_device to large\n"); - return -ENODEV; - } - - printk(KERN_INFO "OHCI USB Driver loading\n"); - - retval = -ENODEV; - for (;;) { - /* Find an OHCI USB controller */ - dev = pci_find_class(PCI_CLASS_SERIAL_USB_OHCI, dev); - if (!dev) - break; - - /* Verify that its OpenHCI by checking for MMIO */ - /* pci_read_config_byte(dev, PCI_CLASS_PROG, &type); - if (!type) - continue; */ - - /* Ok, set it up */ - retval = init_ohci(dev); - if (retval < 0) - continue; - -#ifdef CONFIG_APM - apm_register_callback(&handle_apm_event); -#endif - - return 0; /* no error */ - } - return retval; -} /* ohci_init */ - - -#ifdef MODULE -/* - * Clean up when unloading the module - */ -void cleanup_module(void){ -# ifdef CONFIG_APM - apm_unregister_callback(&handle_apm_event); -# endif - printk(KERN_ERR "usb-ohci: module unloaded\n"); -} - -int init_module(void){ - return ohci_init(); -} -#endif //MODULE - -/* vim:sw=8 - */ diff -u --recursive --new-file v2.3.33/linux/drivers/usb/ohci.h linux/drivers/usb/ohci.h --- v2.3.33/linux/drivers/usb/ohci.h Sat Oct 9 11:47:50 1999 +++ linux/drivers/usb/ohci.h Wed Dec 31 16:00:00 1969 @@ -1,437 +0,0 @@ -#ifndef __LINUX_OHCI_H -#define __LINUX_OHCI_H - -/* - * Open Host Controller Interface data structures and defines. - * - * (C) Copyright 1999 Gregory P. Smith - * - * $Id: ohci.h,v 1.40 1999/09/05 07:26:46 greg Exp $ - */ - -#include -#include - -#include "usb.h" - -struct ohci_ed; - -/* - * Each TD must be aligned on a 16-byte boundary. From the OHCI v1.0 spec - * it does not state that TDs must be contiguious in memory (due to the - * use of the next_td field). This gives us extra room at the end of a - * TD for our own driver specific data. - * - * This structure's size must be a multiple of 16 bytes. ?? no way, I - * don't see why. Alignment should be all that matters. - */ -struct ohci_td { - /* OHCI Hardware fields */ - __u32 info; /* TD status & type flags */ - __u32 cur_buf; /* Current Buffer Pointer (bus address) */ - __u32 next_td; /* Next TD Pointer (bus address) */ - __u32 buf_end; /* Memory Buffer End Pointer (bus address) */ - - /* Driver specific fields */ - struct ohci_ed *ed; /* address of the ED this TD is on */ - struct ohci_td *next_dl_td; /* used during donelist processing */ - void *data; /* virt. address of the the buffer */ - usb_device_irq completed; /* Completion handler routine */ - int hcd_flags; /* Flags for the HCD: */ - /* bit0: Is this TD allocated? */ - /* bit1: Is this a dummy (end of list) TD? */ - /* bit2: do NOT automatically free this TD on completion */ - /* bit3: this is the last TD in a contiguious TD chain */ - - struct usb_device *usb_dev; /* the owning device */ - - void *dev_id; /* user defined pointer passed to irq handler */ -} __attribute((aligned(32))); - -#define OHCI_TD_ROUND (1 << 18) /* buffer rounding bit */ -#define OHCI_TD_D (3 << 19) /* direction of xfer: */ -#define OHCI_TD_D_IN (2 << 19) -#define OHCI_TD_D_OUT (1 << 19) -#define OHCI_TD_D_SETUP (0 << 19) -#define td_set_dir_in(d) ((d) ? OHCI_TD_D_IN : OHCI_TD_D_OUT ) -#define td_set_dir_out(d) ((d) ? OHCI_TD_D_OUT : OHCI_TD_D_IN ) -#define OHCI_TD_IOC_DELAY (7 << 21) /* frame delay allowed before int. */ -#define OHCI_TD_IOC_OFF (OHCI_TD_IOC_DELAY) /* no interrupt on complete */ -#define td_set_ioc_delay(frames) (((frames) & 7) << 21) -#define OHCI_TD_DT (3 << 24) /* data toggle bits */ -#define TOGGLE_AUTO (0 << 24) /* automatic (from the ED) */ -#define TOGGLE_DATA0 (2 << 24) /* force Data0 */ -#define TOGGLE_DATA1 (3 << 24) /* force Data1 */ -#define td_force_toggle(b) (((b) | 2) << 24) -#define OHCI_TD_ERRCNT (3 << 26) /* error count */ -#define td_errorcount(td) ((le32_to_cpup(&(td).info) >> 26) & 3) -#define clear_td_errorcount(td) ((td)->info &= cpu_to_le32(~(__u32)OHCI_TD_ERRCNT)) -#define OHCI_TD_CC (0xf << 28) /* condition code */ -#define OHCI_TD_CC_GET(td_i) (((td_i) >> 28) & 0xf) -#define OHCI_TD_CC_NEW (OHCI_TD_CC) /* set this on all unaccessed TDs! */ -#define td_cc_notaccessed(td) ((le32_to_cpup(&(td).info) >> 29) == 7) -#define td_cc_accessed(td) ((le32_to_cpup(&(td).info) >> 29) != 7) -#define td_cc_noerror(td) (((le32_to_cpup(&(td).info)) & OHCI_TD_CC) == 0) -#define td_done(td) (td_cc_noerror((td)) || (td_errorcount((td)) == 3)) - -/* - * Macros to use the td->hcd_flags field. - */ -#define td_allocated(td) ((td).hcd_flags & 1) -#define allocate_td(td) ((td)->hcd_flags |= 1) -#define ohci_free_td(td) ((td)->hcd_flags &= ~(__u32)1) - -#define td_dummy(td) ((td).hcd_flags & 2) -#define make_dumb_td(td) ((td)->hcd_flags |= 2) -#define clear_dumb_td(td) ((td)->hcd_flags &= ~(__u32)2) - -#define td_endofchain(td) ((td).hcd_flags & (1 << 3)) -#define clear_td_endofchain(td) ((td)->hcd_flags &= ~(1 << 3)) -#define set_td_endofchain(td) ((td)->hcd_flags |= (1 << 3)) - -/* - * These control if the IRQ will call ohci_free_td after taking the TDs - * off of the donelist (assuming the completion function does not ask - * for the TD to be requeued). - */ -#define can_auto_free(td) (!((td).hcd_flags & 4)) -#define noauto_free_td(td) ((td)->hcd_flags |= 4) -#define auto_free_td(td) ((td)->hcd_flags &= ~(__u32)4) - - -/* - * The endpoint descriptors also requires 16-byte alignment - */ -struct ohci_ed { - /* OHCI hardware fields */ - __u32 status; - __u32 tail_td; /* TD Queue tail pointer */ - __u32 _head_td; /* TD Queue head pointer, toggle carry & halted bits */ - __u32 next_ed; /* Next ED */ - - /* driver fields */ - struct ohci_device *ohci_dev; - struct ohci_ed *ed_chain; -} __attribute((aligned(16))); - -/* get the head_td */ -#define ed_head_td(ed) (le32_to_cpup(&(ed)->_head_td) & 0xfffffff0) -#define ed_tail_td(ed) (le32_to_cpup(&(ed)->tail_td)) - -/* save the carry & halted flag while setting the head_td */ -#define set_ed_head_td(ed, td) ((ed)->_head_td = cpu_to_le32((td)) \ - | ((ed)->_head_td & cpu_to_le32(3))) - -/* Control the ED's halted and carry flags */ -#define ohci_halt_ed(ed) ((ed)->_head_td |= cpu_to_le32(1)) -#define ohci_unhalt_ed(ed) ((ed)->_head_td &= cpu_to_le32(~(__u32)1)) -#define ohci_ed_halted(ed) ((ed)->_head_td & cpu_to_le32(1)) -#define ohci_ed_set_carry(ed) ((ed)->_head_td |= cpu_to_le32(2)) -#define ohci_ed_clr_carry(ed) ((ed)->_head_td &= ~cpu_to_le32(2)) -#define ohci_ed_carry(ed) ((le32_to_cpup(&(ed)->_head_td) >> 1) & 1) - -#define OHCI_ED_SKIP (1 << 14) -#define OHCI_ED_MPS (0x7ff << 16) -/* FIXME: should cap at the USB max packet size [0x4ff] */ -#define ed_set_maxpacket(s) (((s) << 16) & OHCI_ED_MPS) -#define OHCI_ED_F_NORM (0) -#define OHCI_ED_F_ISOC (1 << 15) -#define ed_set_type_isoc(i) ((i) ? OHCI_ED_F_ISOC : OHCI_ED_F_NORM) -#define OHCI_ED_S_LOW (1 << 13) -#define OHCI_ED_S_HIGH (0) -#define ed_set_speed(s) ((s) ? OHCI_ED_S_LOW : OHCI_ED_S_HIGH) -#define OHCI_ED_D (3 << 11) -#define OHCI_ED_D_IN (2 << 11) -#define OHCI_ED_D_OUT (1 << 11) -#define ed_set_dir_in(d) ((d) ? OHCI_ED_D_IN : OHCI_ED_D_OUT) -#define ed_set_dir_out(d) ((d) ? OHCI_ED_D_OUT : OHCI_ED_D_IN) -#define OHCI_ED_EN (0xf << 7) -#define OHCI_ED_FA (0x7f) - - -#define ed_get_en(ed) ((le32_to_cpup(&(ed)->status) & OHCI_ED_EN) >> 7) -#define ed_get_fa(ed) (le32_to_cpup(&(ed)->status) & OHCI_ED_FA) - -/* NOTE: bits 27-31 of the status dword are reserved for the HCD */ -#define OHCI_ED_HCD_MASK (0x1f << 27) -/* - * We'll use this status flag for to mark if an ED is in use by the - * driver or not. If the bit is set, it is being used. (bit 31) - */ -#define ED_ALLOCATED (1 << 31) -#define ed_allocated(ed) (le32_to_cpup(&(ed).status) & ED_ALLOCATED) -#define allocate_ed(ed) ((ed)->status |= cpu_to_le32(ED_ALLOCATED)) - -/* - * These store the endpoint transfer type for this ED in the status - * field. (bits 27 and 28) - * Bit 28: - * 0 = Periodic ED - * Bit 27: - * 0 = Isochronous - * 1 = Interrupt - * 1 = Normal ED - * Bit 27: - * 0 = Control - * 1 = Bulk - */ -#define HCD_ED_NORMAL (1 << 28) /* (2 << 27) */ -#define HCD_ED_ISOC (0) -#define HCD_ED_INT (1 << 27) -#define HCD_ED_CONTROL (2 << 27) -#define HCD_ED_BULK (3 << 27) -#define HCD_ED_MASK (3 << 27) - -#define ohci_ed_hcdtype(ed) (le32_to_cpup(&(ed)->status) & HCD_ED_MASK) -#define ohci_ed_set_hcdtype(ed, t) ( (ed)->status = ((ed)->status & ~cpu_to_le32(HCD_ED_MASK)) | cpu_to_le32((t) & HCD_ED_MASK) ) - - -/* - * The HCCA (Host Controller Communications Area) is a 256 byte - * structure defined in the OHCI spec. that the host controller is - * told the base address of. It must be 256-byte aligned. - */ -#define NUM_INTS 32 /* part of the OHCI standard */ -struct ohci_hcca { - __u32 int_table[NUM_INTS]; /* Interrupt ED table */ - __u16 frame_no; /* current frame number */ - __u16 pad1; /* set to 0 on each frame_no change */ - __u32 donehead; /* info returned for an interrupt */ - u8 reserved_for_hc[116]; -} __attribute((aligned(256))); - -/* - * The TD entries here are pre-allocated as Linus did with his simple - * UHCI implementation. With the current state of this driver that - * shouldn't be a problem. However if someone ever connects 127 - * supported devices to this driver and tries to use them all at once: - * a) They're insane! - * b) They should code in dynamic allocation - */ -struct ohci; - -/* - * Warning: These constants must not be so large as to cause the - * ohci_device structure to exceed one 4096 byte page. Or "weird - * things will happen" as the alloc_ohci() function assumes that - * its less than one page. (FIXME) - */ -#define NUM_TDS 32 /* num of preallocated transfer descriptors */ -#define DATA_BUF_LEN 16 /* num of unsigned long's for the data buf */ - -/* - * For this "simple" driver we only support a single ED for each - * polling rate. - * - * Later on this driver should be extended to use a full tree of EDs - * so that there can be 32 different 32ms polling frames, etc. - * Individual devices shouldn't need as many as the root hub in that - * case; complicating how things are done at the moment. - * - * Bulk and Control transfers hang off of their own ED lists. - */ -#define NUM_EDS 16 /* num of preallocated endpoint descriptors */ - -#define ohci_to_usb(ohci) ((ohci)->usb) -#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv) - -/* The usb_device must be first! */ -struct ohci_device { - struct usb_device *usb; - - struct ohci *ohci; - struct ohci_hcca *hcca; /* OHCI mem. mapped IO area */ - - struct ohci_ed ed[NUM_EDS]; /* Endpoint Descriptors */ - struct ohci_td td[NUM_TDS]; /* Transfer Descriptors */ - - unsigned long data[DATA_BUF_LEN]; -} __attribute((aligned(32))); - -/* .... */ - -/* - * These are the index of the placeholder EDs for the root hub to - * build the interrupt transfer ED tree out of. - */ -#define ED_INT_1 0 -#define ED_INT_2 1 -#define ED_INT_4 2 -#define ED_INT_8 3 -#define ED_INT_16 4 -#define ED_INT_32 5 -#define ED_ISO ED_INT_1 /* same as 1ms interrupt queue */ - -/* - * Given a period p in ms, convert it to the closest endpoint - * interrupt frequency; rounding down. This is a gross macro. - * Feel free to toss it for actual code. (gasp!) - */ -#define ms_to_ed_int(p) \ - ((p >= 32) ? ED_INT_32 : \ - ((p & 16) ? ED_INT_16 : \ - ((p & 8) ? ED_INT_8 : \ - ((p & 4) ? ED_INT_4 : \ - ((p & 2) ? ED_INT_2 : \ - ED_INT_1))))) /* hmm.. scheme or lisp anyone? */ - -/* - * This is the maximum number of root hub ports. I don't think we'll - * ever see more than two as that's the space available on an ATX - * motherboard's case, but it could happen. The OHCI spec allows for - * up to 15... (which is insane given that they each need to supply up - * to 500ma; that would be 7.5 amps!). I have seen a PCI card with 4 - * downstream ports on it. - * - * Although I suppose several "ports" could be connected directly to - * internal laptop devices such as a keyboard, mouse, camera and - * serial/parallel ports. hmm... That'd be neat. - */ -#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ - -/* - * This is the structure of the OHCI controller's memory mapped I/O - * region. This is Memory Mapped I/O. You must use the readl() and - * writel() macros defined in asm/io.h to access these!! - */ -struct ohci_regs { - /* control and status registers */ - __u32 revision; - __u32 control; - __u32 cmdstatus; - __u32 intrstatus; - __u32 intrenable; - __u32 intrdisable; - /* memory pointers */ - __u32 hcca; - __u32 ed_periodcurrent; - __u32 ed_controlhead; - __u32 ed_controlcurrent; - __u32 ed_bulkhead; - __u32 ed_bulkcurrent; - __u32 current_donehead; /* The driver should get this from the HCCA */ - /* frame counters */ - __u32 fminterval; - __u32 fmremaining; - __u32 fmnumber; - __u32 periodicstart; - __u32 lsthresh; - /* Root hub ports */ - struct ohci_roothub_regs { - __u32 a; - __u32 b; - __u32 status; - __u32 portstatus[MAX_ROOT_PORTS]; - } roothub; -} __attribute((aligned(32))); - -/* - * Read a MMIO register and re-write it after ANDing with (m) - */ -#define writel_mask(m, a) writel( (readl((unsigned long)(a))) & (__u32)(m), (unsigned long)(a) ) - -/* - * Read a MMIO register and re-write it after ORing with (b) - */ -#define writel_set(b, a) writel( (readl((unsigned long)(a))) | (__u32)(b), (unsigned long)(a) ) - - -#define PORT_CCS (1) /* port current connect status */ -#define PORT_PES (1 << 1) /* port enable status */ -#define PORT_PSS (1 << 2) /* port suspend status */ -#define PORT_POCI (1 << 3) /* port overcurrent indicator */ -#define PORT_PRS (1 << 4) /* port reset status */ -#define PORT_PPS (1 << 8) /* port power status */ -#define PORT_LSDA (1 << 9) /* port low speed dev. attached */ -#define PORT_CSC (1 << 16) /* port connect status change */ -#define PORT_PESC (1 << 17) /* port enable status change */ -#define PORT_PSSC (1 << 18) /* port suspend status change */ -#define PORT_OCIC (1 << 19) /* port over current indicator chg */ -#define PORT_PRSC (1 << 20) /* port reset status change */ - -/* - * Root Hub status register masks - */ -#define OHCI_ROOT_LPS (1) /* turn off root hub ports power */ -#define OHCI_ROOT_OCI (1 << 1) /* Overcurrent Indicator */ -#define OHCI_ROOT_DRWE (1 << 15) /* Device remote wakeup enable */ -#define OHCI_ROOT_LPSC (1 << 16) /* turn on root hub ports power */ -#define OHCI_ROOT_OCIC (1 << 17) /* Overcurrent indicator change */ -#define OHCI_ROOT_CRWE (1 << 31) /* Clear RemoteWakeupEnable */ - -/* - * Root hub A register masks - */ -#define OHCI_ROOT_A_NPS (1 << 9) -#define OHCI_ROOT_A_PSM (1 << 8) - -/* - * Root hub B register masks - */ - -/* - * Interrupt register masks - */ -#define OHCI_INTR_SO (1) -#define OHCI_INTR_WDH (1 << 1) -#define OHCI_INTR_SF (1 << 2) -#define OHCI_INTR_RD (1 << 3) -#define OHCI_INTR_UE (1 << 4) -#define OHCI_INTR_FNO (1 << 5) -#define OHCI_INTR_RHSC (1 << 6) -#define OHCI_INTR_OC (1 << 30) -#define OHCI_INTR_MIE (1 << 31) - -/* - * Control register masks - */ -#define OHCI_USB_RESUME (1 << 6) -#define OHCI_USB_OPER (2 << 6) -#define OHCI_USB_SUSPEND (3 << 6) -#define OHCI_USB_PLE (1 << 2) /* Periodic (interrupt) list enable */ -#define OHCI_USB_IE (1 << 3) /* Isochronous list enable */ -#define OHCI_USB_CLE (1 << 4) /* Control list enable */ -#define OHCI_USB_BLE (1 << 5) /* Bulk list enable */ -#define OHCI_USB_IR (1 << 8) /* Int. Routing, HCD ownership */ -#define OHCI_USB_RWC (1 << 9) /* Remote Wakeup Connected */ -#define OHCI_USB_RWE (1 << 10) /* Remote Wakeup Enable */ - -/* - * Command status register masks - */ -#define OHCI_CMDSTAT_HCR (1) -#define OHCI_CMDSTAT_CLF (1 << 1) -#define OHCI_CMDSTAT_BLF (1 << 2) -#define OHCI_CMDSTAT_OCR (1 << 3) -#define OHCI_CMDSTAT_SOC (3 << 16) - -/* - * This is the full ohci controller description - * - * Note how the "proper" USB information is just - * a subset of what the full implementation needs. (Linus) - */ -struct ohci { - int irq; - struct ohci_regs *regs; /* OHCI controller's memory */ - struct usb_bus *bus; - struct ohci_device *root_hub; /* Root hub & controller */ - struct list_head interrupt_list; /* List of interrupt active TDs for this OHCI */ -}; - -#define OHCI_TIMER /* enable the OHCI timer */ -#define OHCI_TIMER_FREQ (234) /* ms between each root hub status check */ - -#undef OHCI_RHSC_INT /* Don't use root hub status interrupts! */ - -/* Debugging code [ohci-debug.c] */ -void show_ohci_ed(struct ohci_ed *ed); -void show_ohci_td(struct ohci_td *td); -void show_ohci_td_chain(struct ohci_td *td); -void show_ohci_status(struct ohci *ohci); -void show_ohci_device(struct ohci_device *dev); -void show_ohci_hcca(struct ohci_hcca *hcca); - -#endif -/* vim:sw=8 - */ diff -u --recursive --new-file v2.3.33/linux/drivers/usb/proc_usb.c linux/drivers/usb/proc_usb.c --- v2.3.33/linux/drivers/usb/proc_usb.c Tue Dec 7 09:32:46 1999 +++ linux/drivers/usb/proc_usb.c Mon Dec 20 14:10:06 1999 @@ -37,14 +37,21 @@ * 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 + * + * $Id: proc_usb.c,v 1.14 1999/12/17 10:51:41 fliegl Exp $ */ -#define __KERNEL__ 1 - +#define __NO_VERSION__ +#include #include -#include #include -/* #include */ #include #include #include @@ -53,18 +60,34 @@ #include #include #include +#include +#include #include "usb.h" -#define DUMP_LIMIT (PAGE_SIZE - 100) - /* limit to only one memory page of output */ #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: Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd */ - "T: Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s MxCh=%2d\n"; +/* 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=%s\n"; + +static char *format_string_product = +/* S: Product=xxxx */ + "S: Product=%s\n"; + +#ifdef ALLOW_SERIAL_NUMBER +static char *format_string_serialnumber = +/* S: SerialNumber=xxxx */ + "S: SerialNumber=%s\n"; +#endif static char *format_bandwidth = /* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */ @@ -83,7 +106,7 @@ "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 */ +/* 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 = @@ -106,12 +129,20 @@ static struct proc_dir_entry *usbdir = NULL, *driversdir = NULL; static struct proc_dir_entry *devicesdir = NULL; +static DECLARE_WAIT_QUEUE_HEAD(deviceconndiscwq); +static unsigned int conndiscevcnt = 0; + +/* this struct stores the poll state for /proc/bus/usb/devices pollers */ +struct usb_device_status { + unsigned int lastev; +}; + struct class_info { int class; char *class_name; }; -struct class_info clas_info [] = +static const struct class_info clas_info[] = { /* max. 5 chars. per name string */ {USB_CLASS_PER_INTERFACE, ">ifc"}, {USB_CLASS_AUDIO, "audio"}, @@ -127,78 +158,70 @@ /*****************************************************************/ -static char *class_decode (const int class) +extern inline void conndiscevent(void) { - int ix; + wake_up(&deviceconndiscwq); + conndiscevcnt++; +} - for (ix = 0; clas_info [ix].class != -1; ix++) - if (clas_info [ix].class == class) - break; +static const char *class_decode(const int class) +{ + int ix; - return (clas_info [ix].class_name); + for (ix = 0; clas_info[ix].class != -1; ix++) + if (clas_info[ix].class == class) + break; + return (clas_info[ix].class_name); } -static int usb_dump_endpoint_descriptor (const struct usb_endpoint_descriptor *desc, - char *buf, int *len) + +static char *usb_dump_endpoint_descriptor(char *start, char *end, const struct usb_endpoint_descriptor *desc) { char *EndpointType [4] = {"Ctrl", "Isoc", "Bulk", "Int."}; - *len += sprintf (buf + *len, format_endpt, - desc->bEndpointAddress, - (desc->bEndpointAddress & USB_DIR_IN) ? 'I' : 'O', - desc->bmAttributes, - EndpointType[desc->bmAttributes & 3], - desc->wMaxPacketSize, - desc->bInterval - ); - - return (*len >= DUMP_LIMIT) ? -1 : 0; + 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 int usb_dump_endpoint (const struct usb_endpoint_descriptor *endpoint, - char *buf, int *len) +static char *usb_dump_endpoint(char *start, char *end, const struct usb_endpoint_descriptor *endpoint) { - if (usb_dump_endpoint_descriptor (endpoint, buf, len) < 0) - return -1; - - return 0; + return usb_dump_endpoint_descriptor(start, end, endpoint); } -static int usb_dump_interface_descriptor (const struct usb_interface *iface, - int setno, char *buf, int *len) +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]; - - *len += sprintf (buf + *len, format_iface, - desc->bInterfaceNumber, - desc->bAlternateSetting, - desc->bNumEndpoints, - desc->bInterfaceClass, - class_decode (desc->bInterfaceClass), - desc->bInterfaceSubClass, - desc->bInterfaceProtocol, - iface->driver ? iface->driver->name : "(none)" - ); + struct usb_interface_descriptor *desc = &iface->altsetting[setno]; - return (*len >= DUMP_LIMIT) ? -1 : 0; + 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 int usb_dump_interface (const struct usb_interface *iface, - int setno, char *buf, int *len) +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; - struct usb_interface_descriptor *desc = - &iface->altsetting[setno]; - - if (usb_dump_interface_descriptor (iface, setno, buf, len) < 0) - return -1; + start = usb_dump_interface_descriptor(start, end, iface, setno); for (i = 0; i < desc->bNumEndpoints; i++) { - if (usb_dump_endpoint (desc->endpoint + i, buf, len) < 0) - return -1; + if (start > end) + return start; + start = usb_dump_endpoint(start, end, desc->endpoint + i); } - - return 0; + return start; } /* TBD: @@ -208,225 +231,299 @@ * 2. add status to each endpoint line */ -static int usb_dump_config_descriptor (const struct usb_config_descriptor *desc, - const int active, char *buf, int *len) +static char *usb_dump_config_descriptor(char *start, char *end, const struct usb_config_descriptor *desc, const int active) { - *len += sprintf (buf + *len, format_config, - active ? '*' : ' ', /* mark active/actual/current cfg. */ - desc->bNumInterfaces, - desc->bConfigurationValue, - desc->bmAttributes, - desc->MaxPower * 2 - ); - - return (*len >= DUMP_LIMIT) ? -1 : 0; + 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 int usb_dump_config (const struct usb_config_descriptor *config, - const int active, char *buf, int *len) +static char *usb_dump_config(char *start, char *end, const struct usb_config_descriptor *config, const int active) { int i, j; struct usb_interface *interface; - if (!config) { /* getting these some in 2.3.7; none in 2.3.6 */ - *len += sprintf (buf + *len, "(null Cfg. desc.)\n"); - return 0; - } - - if (usb_dump_config_descriptor (config, active, buf, len) < 0) - return -1; - + if (start > end) + return start; + if (!config) /* getting these some in 2.3.7; none in 2.3.6 */ + return start + sprintf(start, "(null Cfg. desc.)\n"); + start = usb_dump_config_descriptor(start, end, config, active); for (i = 0; i < config->bNumInterfaces; i++) { interface = config->interface + i; if (!interface) break; - - for (j = 0; j < interface->num_altsetting; j++) - if (usb_dump_interface (interface, j, buf, len) < 0) - return -1; + for (j = 0; j < interface->num_altsetting; j++) { + if (start > end) + return start; + start = usb_dump_interface(start, end, interface, j); + } } - - return 0; + return start; } /* * Dump the different USB descriptors. */ -static int usb_dump_device_descriptor (const struct usb_device_descriptor *desc, - char *buf, int *len) +static char *usb_dump_device_descriptor(char *start, char *end, const struct usb_device_descriptor *desc) { - *len += sprintf (buf + *len, format_device1, - desc->bcdUSB >> 8, desc->bcdUSB & 0xff, - desc->bDeviceClass, - class_decode (desc->bDeviceClass), - desc->bDeviceSubClass, - desc->bDeviceProtocol, - desc->bMaxPacketSize0, - desc->bNumConfigurations - ); - if (*len >= DUMP_LIMIT) return -1; - - *len += sprintf (buf + *len, format_device2, - desc->idVendor, desc->idProduct, - desc->bcdDevice >> 8, desc->bcdDevice & 0xff - ); - - return (*len >= DUMP_LIMIT) ? -1 : 0; + 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; } -static int usb_dump_desc (const struct usb_device *dev, char *buf, int *len) +/* + * Dump the different strings that this device holds. + */ +static char *usb_dump_device_strings (char *start, char *end, const struct usb_device *dev) { - int i; + if (start > end) + return start; - if (usb_dump_device_descriptor (&dev->descriptor, buf, len) < 0) - return -1; + if (dev->descriptor.iManufacturer) { + char * string = usb_string ((struct usb_device *)dev, + dev->descriptor.iManufacturer); + if (string) { + start += sprintf (start, format_string_manufacturer, + string + ); + if (start > end) + return start; + + } + } - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { - if (usb_dump_config (dev->config + i, - (dev->config + i) == dev->actconfig, /* active ? */ - buf, len) < 0) - return -1; + if (dev->descriptor.iProduct) { + char * string = usb_string ((struct usb_device *)dev, + dev->descriptor.iProduct); + if (string) { + start += sprintf (start, format_string_product, + string + ); + if (start > end) + return start; + + } } - return 0; +#ifdef ALLOW_SERIAL_NUMBER + if (dev->descriptor.iSerialNumber) { + char * string = usb_string ((struct usb_device *)dev, + dev->descriptor.iSerialNumber); + if (string) { + start += sprintf (start, format_string_serialnumber, + string + ); + } + } +#endif + + return start; } -static int usb_hcd_bandwidth (const struct usb_device *dev, char *buf, int *len) +static char *usb_dump_desc(char *start, char *end, const struct usb_device *dev) { - *len += sprintf (buf + *len, format_bandwidth, - dev->bus->bandwidth_allocated, - FRAME_TIME_MAX_USECS_ALLOC, - (100 * dev->bus->bandwidth_allocated + FRAME_TIME_MAX_USECS_ALLOC / 2) / - FRAME_TIME_MAX_USECS_ALLOC, - dev->bus->bandwidth_int_reqs, - dev->bus->bandwidth_isoc_reqs - ); + int i; - return (*len >= DUMP_LIMIT) ? -1 : 0; + if (start > end) + return start; + + start = usb_dump_device_descriptor(start, end, &dev->descriptor); + + if (start > end) + return start; + + start = usb_dump_device_strings (start, end, dev); + + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + if (start > end) + return start; + start = usb_dump_config(start, end, dev->config + i, + (dev->config + i) == dev->actconfig); /* active ? */ + } + return start; } + #ifdef PROC_EXTRA /* TBD: may want to add this code later */ -static int usb_dump_hub_descriptor (const struct usb_hub_descriptor * desc, - char *buf, int *len) +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; - - *len += sprintf (buf + *len, "Interface:"); + unsigned char *ptr = (unsigned char *)desc; + if (start > end) + return start; + start += sprintf(start, "Interface:"); while (leng) { - *len += sprintf (buf + *len, " %02x", *ptr); + start += sprintf(start, " %02x", *ptr); ptr++; leng--; } - *len += sprintf (buf + *len, "\n"); - - return (*len >= DUMP_LIMIT) ? -1 : 0; + start += sprintf(start, "\n"); + return start; } -static int usb_dump_string (const struct usb_device *dev, char *id, int index, - char *buf, int *len) +static char *usb_dump_string(char *start, char *end, const struct usb_device *dev, char *id, int index) { + if (start > end) + return start; + start += sprintf(start, "Interface:"); if (index <= dev->maxstring && dev->stringindex && dev->stringindex[index]) - *len += sprintf (buf + *len, "%s: %s ", id, dev->stringindex[index]); - - return (*len >= DUMP_LIMIT) ? -1 : 0; + start += sprintf(start, "%s: %s ", id, dev->stringindex[index]); + return start; } #endif /* PROC_EXTRA */ /*****************************************************************/ -static int usb_device_dump (char *buf, int *len, - const struct usb_device *usbdev, - int level, int index, int count) -{ - int chix; - int cnt = 0; - int parent_devnum; - - if (level > MAX_TOPO_LEVEL) return -1; - - parent_devnum = usbdev->parent ? (usbdev->parent->devnum == -1) ? 0 - : usbdev->parent->devnum : 0; - /* - * So the root hub's parent is 0 and any device that is - * plugged into the root hub has a parent of 0. - */ - *len += sprintf (buf + *len, format_topo, - level, parent_devnum, index, count, - usbdev->devnum, - usbdev->slow ? "1.5" : "12 ", - usbdev->maxchild - ); - /* - * level = topology-tier level; - * parent_devnum = parent device number; - * index = parent's connector number; - * count = device count at this level - */ - - if (*len >= DUMP_LIMIT) - return -1; - - if ((level == 0) && (usbdev->devnum < 0)) { /* for root hub */ - if (usb_hcd_bandwidth (usbdev, buf, len) < 0) - return -1; - } - else { /* for anything but a root hub */ - if (usb_dump_desc (usbdev, buf, len) < 0) - return -1; - } +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 < usbdev->maxchild; chix++) { - if (usbdev->children [chix]) { - if (usb_device_dump (buf, len, - usbdev->children [chix], - level + 1, chix, ++cnt) < 0) - return -1; - } + if (start > end) + return start; + if (usbdev->children[chix]) + start = usb_device_dump(start, end, usbdev->children[chix], bus, level + 1, chix, ++cnt); } - - return 0; + return start; } -static int usb_bus_list_dump (char *buf, int len) +static ssize_t usb_device_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) { - struct list_head *usb_bus_list = usb_bus_get_list (); - struct list_head *list = usb_bus_list->next; - - len = 0; - - /* - * Go thru each usb_bus. Within each usb_bus: each usb_device. - * Within each usb_device: all of its device & config. descriptors, - * marking the currently active ones. - */ - - - while (list != usb_bus_list) { - struct usb_bus *bus = list_entry (list, struct usb_bus, bus_list); - - if (usb_device_dump (buf, &len, bus->root_hub, 0, 0, 0) - < 0) - break; + struct list_head *usb_bus_list, *buslist; + struct usb_bus *bus; + char *page, *end; + ssize_t ret = 0; + unsigned int pos, len; + int busnum = 0; - list = list->next; + if (*ppos < 0) + return -EINVAL; + if (nbytes <= 0) + return 0; + if (!access_ok(VERIFY_WRITE, buf, nbytes)) + return -EFAULT; + if (!(page = (char*) __get_free_page(GFP_KERNEL))) + return -ENOMEM; + pos = *ppos; + usb_bus_list = usb_bus_get_list(); + /* enumerate busses */ + for (buslist = usb_bus_list->next; buslist != usb_bus_list; buslist = buslist->next, ++busnum) { + /* 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 + (PAGE_SIZE - 100), bus->root_hub, 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_page((unsigned long)page); + return ret; +} - if (len >= DUMP_LIMIT) { - len += sprintf (buf + len, "(truncated)\n"); - break; - } - } +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; + MOD_INC_USE_COUNT; + mask = POLLIN; + } + if (file->f_mode & FMODE_READ) + poll_wait(file, &deviceconndiscwq, wait); + if (st->lastev != conndiscevcnt) + mask |= POLLIN; + st->lastev = conndiscevcnt; + return mask; +} - return (len); +static int usb_device_open(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + MOD_INC_USE_COUNT; + return 0; } -static int usb_bus_list_dump_devices (char *buf, char **start, off_t offset, - int len, int *eof, void *data) +static int usb_device_release(struct inode *inode, struct file *file) { - return usb_bus_list_dump (buf, len); + if (file->private_data) { + kfree(file->private_data); + file->private_data = NULL; + } + MOD_DEC_USE_COUNT; + return 0; } /* @@ -434,34 +531,89 @@ * * We now walk the list of registered USB drivers. */ -static int usb_driver_list_dump (char *buf, char **start, off_t offset, - int len, int *eof, void *data) +static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) { - struct list_head *usb_driver_list = usb_driver_get_list (); + struct list_head *usb_driver_list = usb_driver_get_list(); struct list_head *tmp = usb_driver_list->next; - int cnt = 0; - - len = 0; - - while (tmp != usb_driver_list) { - struct usb_driver *driver = list_entry (tmp, struct usb_driver, - driver_list); - len += sprintf (buf + len, "%s\n", driver->name); - cnt++; - tmp = tmp->next; + char *page, *start, *end; + ssize_t ret = 0; + unsigned int pos, len; - if (len >= DUMP_LIMIT) - { - len += sprintf (buf + len, "(truncated)\n"); - return (len); + if (*ppos < 0) + return -EINVAL; + if (nbytes <= 0) + return 0; + if (!access_ok(VERIFY_WRITE, buf, nbytes)) + return -EFAULT; + if (!(page = (char*) __get_free_page(GFP_KERNEL))) + return -ENOMEM; + start = page; + end = page + (PAGE_SIZE - 100); + pos = *ppos; + for (; tmp != usb_driver_list; tmp = tmp->next) { + struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list); + start += sprintf (start, "%s\n", driver->name); + if (start > end) { + start += sprintf(start, "(truncated)\n"); + break; } } - - if (!cnt) - len += sprintf (buf + len, "(none)\n"); - return (len); + 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 usbdev_lseek(struct file * file, long long offset, int orig); + +static struct file_operations proc_usb_devlist_file_operations = { + usbdev_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 */ +}; + +static struct inode_operations proc_usb_devlist_inode_operations = { + &proc_usb_devlist_file_operations, /* file-ops */ +}; + +static struct file_operations proc_usb_drvlist_file_operations = { + usbdev_lseek, /* lseek */ + usb_driver_read, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* flush */ + NULL, /* no special release code */ + NULL /* can't fsync */ +}; + +static struct inode_operations proc_usb_drvlist_inode_operations = { + &proc_usb_drvlist_file_operations, /* file-ops */ +}; + + /* * proc entry for every device */ @@ -487,7 +639,7 @@ static ssize_t usbdev_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) { - struct inode *inode = file->f_dentry->d_inode; + struct inode *inode = file->f_dentry->d_inode; struct proc_dir_entry *dp = (struct proc_dir_entry *)inode->u.generic_ip; struct usb_device *dev = (struct usb_device *)dp->data; ssize_t ret = 0; @@ -548,7 +700,7 @@ i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.request, ctrl.requesttype, ctrl.value, ctrl.index, tbuf, ctrl.length, (ctrl.timeout * HZ + 500) / 1000); - if (!i && ctrl.length) { + if ((i > 0) && ctrl.length) { copy_to_user_ret(ctrl.data, tbuf, ctrl.length, -EFAULT); } } else { @@ -560,12 +712,12 @@ (ctrl.timeout * HZ + 500) / 1000); } free_page((unsigned long)tbuf); - if (i) { + if (i < 0) { printk(KERN_WARNING "procusb: EZUSB_CONTROL failed rqt %u rq %u len %u ret %d\n", ctrl.requesttype, ctrl.request, ctrl.length, i); - return -ENXIO; + return i; } - return 0; + return i; case EZUSB_BULK: if (obsolete_warn < 20) { @@ -593,7 +745,7 @@ return -EINVAL; } i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, (ctrl.timeout * HZ + 500) / 1000); - if (!i && len2) { + if ((i > 0) && len2) { copy_to_user_ret(bulk.data, tbuf, len2, -EFAULT); } } else { @@ -603,10 +755,10 @@ i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, (ctrl.timeout * HZ + 500) / 1000); } free_page((unsigned long)tbuf); - if (i) { + if (i < 0) { printk(KERN_WARNING "procusb: EZUSB_BULK failed ep 0x%x len %u ret %d\n", bulk.ep, bulk.len, i); - return -ENXIO; + return i; } return len2; @@ -628,23 +780,23 @@ free_page((unsigned long)tbuf); return -EINVAL; } - i = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), (devrequest *)&octrl, tbuf, octrl.dlen, HZ); - if (!i && octrl.dlen) { + i = usb_internal_control_msg(dev, usb_rcvctrlpipe(dev, 0), (devrequest *)&octrl, tbuf, octrl.dlen, HZ); + if ((i > 0) && octrl.dlen) { copy_to_user_ret(octrl.data, tbuf, octrl.dlen, -EFAULT); } } else { if (octrl.dlen) { copy_from_user_ret(tbuf, octrl.data, octrl.dlen, -EFAULT); } - i = dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), (devrequest *)&octrl, tbuf, octrl.dlen, HZ); - } + i = usb_internal_control_msg(dev, usb_sndctrlpipe(dev, 0), (devrequest *)&octrl, tbuf, octrl.dlen, HZ); + } free_page((unsigned long)tbuf); - if (i) { + if (i < 0) { printk(KERN_WARNING "procusb: EZUSB_OLD_CONTROL failed rqt %u rq %u len %u ret %d\n", octrl.requesttype, octrl.request, octrl.length, i); - return -ENXIO; + return i; } - return 0; + return i; case EZUSB_OLD_BULK: if (obsolete_warn < 20) { @@ -672,7 +824,7 @@ return -EINVAL; } i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, HZ*5); - if (!i && len2) { + if ((i > 0) && len2) { copy_to_user_ret(obulk.data, tbuf, len2, -EFAULT); } } else { @@ -682,10 +834,10 @@ i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, HZ*5); } free_page((unsigned long)tbuf); - if (i) { + if (i < 0) { printk(KERN_WARNING "procusb: EZUSB_OLD_BULK failed ep 0x%x len %u ret %d\n", obulk.ep, obulk.len, i); - return -ENXIO; + return i; } return len2; @@ -766,7 +918,7 @@ i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.request, ctrl.requesttype, ctrl.value, ctrl.index, tbuf, ctrl.length, (ctrl.timeout * HZ + 500) / 1000); - if (!i && ctrl.length) { + if ((i > 0) && ctrl.length) { copy_to_user_ret(ctrl.data, tbuf, ctrl.length, -EFAULT); } } else { @@ -778,10 +930,10 @@ ctrl.length, (ctrl.timeout * HZ + 500) / 1000); } free_page((unsigned long)tbuf); - if (i) { + if (i<0) { printk(KERN_WARNING "/proc/bus/usb: USB_PROC_CONTROL failed rqt %u rq %u len %u ret %d\n", ctrl.requesttype, ctrl.request, ctrl.length, i); - return -ENXIO; + return i; } return 0; @@ -839,7 +991,7 @@ i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), octrl.request, octrl.requesttype, octrl.value, octrl.index, tbuf, octrl.length, HZ); - if (!i && octrl.length) { + if ((i > 0) && octrl.length) { copy_to_user_ret(octrl.data, tbuf, octrl.length, -EFAULT); } } else { @@ -851,10 +1003,10 @@ octrl.length, HZ); } free_page((unsigned long)tbuf); - if (i) { + if (i < 0) { printk(KERN_WARNING "/proc/bus/usb: USB_PROC_OLD_CONTROL failed rqt %u rq %u len %u ret %d\n", octrl.requesttype, octrl.request, octrl.length, i); - return -ENXIO; + return i; } return 0; @@ -879,7 +1031,7 @@ return -EINVAL; } i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, HZ*5); - if (!i && len2) { + if ((i > 0) && len2) { copy_to_user_ret(obulk.data, tbuf, len2, -EFAULT); } } else { @@ -889,10 +1041,10 @@ i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, HZ*5); } free_page((unsigned long)tbuf); - if (i) { + if (i < 0) { printk(KERN_WARNING "/proc/bus/usb: USB_PROC_OLD_BULK failed ep 0x%x len %u ret %d\n", obulk.ep, obulk.len, i); - return -ENXIO; + return i; } return len2; @@ -948,7 +1100,7 @@ }; static struct inode_operations proc_usb_device_inode_operations = { - &proc_usb_device_file_operations, /* file-ops */ + &proc_usb_device_file_operations, /* file-ops */ }; void proc_usb_add_bus(struct usb_bus *bus) @@ -959,9 +1111,14 @@ if (!usbdir) return; sprintf(buf, "%03d", bus->busnum); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,31) + if (!(bus->proc_entry = create_proc_entry(buf, S_IFDIR, usbdir))) +#else if (!(bus->proc_entry = proc_mkdir(buf, usbdir))) +#endif return; bus->proc_entry->data = bus; + conndiscevent(); } /* devices need already be removed! */ @@ -970,6 +1127,7 @@ if (!bus->proc_entry) return; remove_proc_entry(bus->proc_entry->name, usbdir); + conndiscevent(); } void proc_usb_add_device(struct usb_device *dev) @@ -984,48 +1142,54 @@ return; dev->proc_entry->ops = &proc_usb_device_inode_operations; dev->proc_entry->data = dev; + conndiscevent(); } void proc_usb_remove_device(struct usb_device *dev) { if (dev->proc_entry) remove_proc_entry(dev->proc_entry->name, dev->bus->proc_entry); + conndiscevent(); } void proc_usb_cleanup (void) { if (driversdir) - remove_proc_entry ("drivers", usbdir); + remove_proc_entry("drivers", usbdir); if (devicesdir) - remove_proc_entry ("devices", usbdir); + remove_proc_entry("devices", usbdir); if (usbdir) - remove_proc_entry ("usb", proc_bus); + remove_proc_entry("usb", proc_bus); } int proc_usb_init (void) { - usbdir = proc_mkdir ("usb", proc_bus); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,31) + usbdir = create_proc_entry("usb", S_IFDIR, proc_bus); +#else + usbdir = proc_mkdir("usb", proc_bus); +#endif if (!usbdir) { printk ("proc_usb: cannot create /proc/bus/usb entry\n"); return -1; } - driversdir = create_proc_read_entry("drivers", 0, usbdir, - usb_driver_list_dump, NULL); + driversdir = create_proc_entry("drivers", 0, usbdir); if (!driversdir) { printk ("proc_usb: cannot create /proc/bus/usb/drivers entry\n"); - proc_usb_cleanup (); + proc_usb_cleanup(); return -1; } + driversdir->ops = &proc_usb_drvlist_inode_operations; - devicesdir = create_proc_read_entry ("devices", 0, usbdir, - usb_bus_list_dump_devices, NULL); + devicesdir = create_proc_entry("devices", 0, usbdir); if (!devicesdir) { printk ("proc_usb: cannot create /proc/bus/usb/devices entry\n"); proc_usb_cleanup (); return -1; } + devicesdir->ops = &proc_usb_devlist_inode_operations; return 0; } diff -u --recursive --new-file v2.3.33/linux/drivers/usb/uhci-debug.c linux/drivers/usb/uhci-debug.c --- v2.3.33/linux/drivers/usb/uhci-debug.c Thu Aug 26 13:05:39 1999 +++ linux/drivers/usb/uhci-debug.c Thu Dec 16 20:33:10 1999 @@ -1,208 +1,251 @@ /* - * UHCI-specific debugging code. Invaluable when something - * goes wrong, but don't get in my face. - * - * Kernel visible pointers are surrounded in []'s and bus - * visible pointers are surrounded in ()'s - * - * (C) Copyright 1999 Linus Torvalds - * (C) Copyright 1999 Johannes Erdfelt + * $Id: uhci-debug.c,v 1.12 1999/12/13 15:24:42 fliegl Exp $ */ - +#include #include #include +#include "usb.h" #include "uhci.h" - -void uhci_show_td(struct uhci_td * td) +#define DEBUG +#ifdef DEBUG +#define dbg printk +#else +#define dbg nix +static void nix (const char *format,...) { - char *spid; +} - printk("%08x ", td->link); - printk("e%d %s%s%s%s%s%s%s%s%s%sLength=%x ", - ((td->status >> 27) & 3), - (td->status & TD_CTRL_SPD) ? "SPD " : "", - (td->status & TD_CTRL_LS) ? "LS " : "", - (td->status & TD_CTRL_IOC) ? "IOC " : "", - (td->status & TD_CTRL_ACTIVE) ? "Active " : "", - (td->status & TD_CTRL_STALLED) ? "Stalled " : "", - (td->status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", - (td->status & TD_CTRL_BABBLE) ? "Babble " : "", - (td->status & TD_CTRL_NAK) ? "NAK " : "", - (td->status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", - (td->status & TD_CTRL_BITSTUFF) ? "BitStuff " : "", - td->status & 0x7ff); - - switch (td->info & 0xff) { - case USB_PID_SETUP: - spid = "SETUP"; - break; - case USB_PID_OUT: - spid = "OUT"; - break; - case USB_PID_IN: - spid = "IN"; - break; - default: - spid = "?"; - break; +#endif +void dump_urb (purb_t purb) +{ + printk ("urb :%p\n", purb); + printk ("next :%p\n", purb->next); + printk ("dev :%p\n", purb->dev); + printk ("pipe :%08X\n", purb->pipe); + printk ("status :%d\n", purb->status); + printk ("transfer_flags :%08X\n", purb->transfer_flags); + printk ("transfer_buffer :%p\n", purb->transfer_buffer); + printk ("transfer_buffer_length:%d\n", purb->transfer_buffer_length); + printk ("actual_length :%d\n", purb->actual_length); + printk ("setup_packet :%p\n", purb->setup_packet); + printk ("start_frame :%d\n", purb->start_frame); + printk ("number_of_packets :%d\n", purb->number_of_packets); + printk ("interval :%d\n", purb->interval); + printk ("error_count :%d\n", purb->error_count); + printk ("context :%p\n", purb->context); + printk ("complete :%p\n", purb->complete); +} + +void beep (long freq) +{ + long v; + char low, high; + + if (!freq) + outb (inb (0x61) & 252, 0x61); + else { + outb (inb (0x61) | 0x3, 0x61); + + v = 1193180L / freq; + + low = (char) (v & 255); + high = (char) ((v >> 8) & 255); + + outb (182, 0x43); + outb (low, 0x42); + outb (high, 0x42); } - - printk("MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ", - td->info >> 21, - ((td->info >> 19) & 1), - (td->info >> 15) & 15, - (td->info >> 8) & 127, - (td->info & 0xff), - spid); - printk("(buf=%08x)\n", td->buffer); -} - -static void uhci_show_sc(int port, unsigned short status) -{ - printk(" stat%d = %04x %s%s%s%s%s%s%s%s\n", - port, - status, - (status & USBPORTSC_SUSP) ? "PortSuspend " : "", - (status & USBPORTSC_PR) ? "PortReset " : "", - (status & USBPORTSC_LSDA) ? "LowSpeed " : "", - (status & USBPORTSC_RD) ? "ResumeDetect " : "", - (status & USBPORTSC_PEC) ? "EnableChange " : "", - (status & USBPORTSC_PE) ? "PortEnabled " : "", - (status & USBPORTSC_CSC) ? "ConnectChange " : "", - (status & USBPORTSC_CCS) ? "PortConnected " : ""); } -void uhci_show_status(struct uhci *uhci) +void uhci_show_qh (puhci_desc_t qh) { - unsigned int io_addr = uhci->io_addr; - unsigned short usbcmd, usbstat, usbint, usbfrnum; - unsigned int flbaseadd; - unsigned char sof; - unsigned short portsc1, portsc2; + if (qh->type != QH_TYPE) { + dbg (KERN_DEBUG MODSTR "qh has not QH_TYPE\n"); + return; + } + dbg (KERN_DEBUG MODSTR "uhci_show_qh %p (%08lX):\n", qh, virt_to_bus (qh)); - usbcmd = inw(io_addr + 0); - usbstat = inw(io_addr + 2); - usbint = inw(io_addr + 4); - usbfrnum = inw(io_addr + 6); - flbaseadd = inl(io_addr + 8); - sof = inb(io_addr + 12); - portsc1 = inw(io_addr + 16); - portsc2 = inw(io_addr + 18); - - printk(" usbcmd = %04x %s%s%s%s%s%s%s%s\n", - usbcmd, - (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", - (usbcmd & USBCMD_CF) ? "CF " : "", - (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", - (usbcmd & USBCMD_FGR) ? "FGR " : "", - (usbcmd & USBCMD_EGSM) ? "EGSM " : "", - (usbcmd & USBCMD_GRESET) ? "GRESET " : "", - (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", - (usbcmd & USBCMD_RS) ? "RS " : ""); - - printk(" usbstat = %04x %s%s%s%s%s%s\n", - usbstat, - (usbstat & USBSTS_HCH) ? "HCHalted " : "", - (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", - (usbstat & USBSTS_HSE) ? "HostSystemError " : "", - (usbstat & USBSTS_RD) ? "ResumeDetect " : "", - (usbstat & USBSTS_ERROR) ? "USBError " : "", - (usbstat & USBSTS_USBINT) ? "USBINT " : ""); - - printk(" usbint = %04x\n", usbint); - printk(" usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1, - 0xfff & (4*(unsigned int)usbfrnum)); - printk(" flbaseadd = %08x\n", flbaseadd); - printk(" sof = %02x\n", sof); - uhci_show_sc(1, portsc1); - uhci_show_sc(2, portsc2); -} - -#define uhci_link_to_qh(x) ((struct uhci_qh *) uhci_link_to_td(x)) - -struct uhci_td *uhci_link_to_td(unsigned int link) -{ - if (link & UHCI_PTR_TERM) - return NULL; - - return bus_to_virt(link & ~UHCI_PTR_BITS); -} - -void uhci_show_queue(struct uhci_qh *qh) -{ - struct uhci_td *td, *first; - int i = 0, count = 1000; - - if (qh->element & UHCI_PTR_QH) - printk(" Element points to QH (bug?)\n"); + if (qh->hw.qh.head & UHCI_PTR_TERM) + dbg (KERN_DEBUG MODSTR "Head Terminate\n"); + else { + if (qh->hw.qh.head & UHCI_PTR_QH) + dbg (KERN_DEBUG MODSTR "Head points to QH\n"); + else + dbg (KERN_DEBUG MODSTR "Head points to TD\n"); - if (qh->element & UHCI_PTR_DEPTH) - printk(" Depth traverse\n"); + dbg (KERN_DEBUG MODSTR "head: %08X\n", qh->hw.qh.head & ~UHCI_PTR_BITS); + } + if (qh->hw.qh.element & UHCI_PTR_TERM) + dbg (KERN_DEBUG MODSTR "Element Terminate\n"); + else { + + if (qh->hw.qh.element & UHCI_PTR_QH) + dbg (KERN_DEBUG MODSTR "Element points to QH\n"); + else + dbg (KERN_DEBUG MODSTR "Element points to TD\n"); + dbg (KERN_DEBUG MODSTR "element: %08X\n", qh->hw.qh.element & ~UHCI_PTR_BITS); + } +} - if (qh->element & UHCI_PTR_TERM) - printk(" Terminate\n"); +void uhci_show_td (puhci_desc_t td) +{ + char *spid; + dbg (KERN_DEBUG MODSTR "uhci_show_td %p (%08lX) ", td, virt_to_bus (td)); - if (!(qh->element & ~UHCI_PTR_BITS)) { - printk(" td 0: [NULL]\n"); - return; + switch (td->hw.td.info & 0xff) { + case USB_PID_SETUP: + spid = "SETUP"; + break; + case USB_PID_OUT: + spid = " OUT "; + break; + case USB_PID_IN: + spid = " IN "; + break; + default: + spid = " ? "; + break; } - if (qh->first) - first = qh->first; - else - first = uhci_link_to_td(qh->element); - - /* Make sure it doesn't runaway */ - for (td = first; td && count > 0; - td = uhci_link_to_td(td->link), --count) { - printk(" td %d: [%p]\n", i++, td); - printk(" "); - uhci_show_td(td); + dbg ("MaxLen=%02x DT%d EndPt=%x Dev=%x, PID=%x(%s) (buf=%08x)\n", + td->hw.td.info >> 21, + ((td->hw.td.info >> 19) & 1), + (td->hw.td.info >> 15) & 15, + (td->hw.td.info >> 8) & 127, + (td->hw.td.info & 0xff), + spid, + td->hw.td.buffer); + + dbg (KERN_DEBUG MODSTR "Len=%02x e%d %s%s%s%s%s%s%s%s%s%s\n", + td->hw.td.status & 0x7ff, + ((td->hw.td.status >> 27) & 3), + (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "", + (td->hw.td.status & TD_CTRL_LS) ? "LS " : "", + (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "", + (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "", + (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "", + (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", + (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "", + (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "", + (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", + (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : "" + ); +#if 1 + if (td->hw.td.link & UHCI_PTR_TERM) + dbg (KERN_DEBUG MODSTR "Link Terminate\n"); + else { + if (td->hw.td.link & UHCI_PTR_QH) + dbg (KERN_DEBUG MODSTR "%s, link points to QH @ %08x\n", + (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"), + td->hw.td.link & ~UHCI_PTR_BITS); + else + dbg (KERN_DEBUG MODSTR "%s, link points to TD @ %08x \n", + (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"), + td->hw.td.link & ~UHCI_PTR_BITS); + } +#endif +} - if (td == uhci_link_to_td(td->link)) { - printk(KERN_ERR "td links to itself!\n"); +void uhci_show_td_queue (puhci_desc_t td) +{ + dbg (KERN_DEBUG MODSTR "uhci_show_td_queue %p (%08lX):\n", td, virt_to_bus (td)); + while (1) { + uhci_show_td (td); + if (td->hw.td.link & UHCI_PTR_TERM) + break; + //if(!(td->hw.td.link&UHCI_PTR_DEPTH)) + // break; + if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS)) + td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS); + else { + dbg (KERN_DEBUG MODSTR "td points to itself!\n"); break; } +// schedule(); } } -static int uhci_is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh) +void uhci_show_queue (puhci_desc_t qh) { - int j; + dbg (KERN_DEBUG MODSTR "uhci_show_queue %p:\n", qh); + while (1) { + uhci_show_qh (qh); + + if (qh->hw.qh.element & UHCI_PTR_QH) + dbg (KERN_DEBUG MODSTR "Warning: qh->element points to qh!\n"); + else if (!(qh->hw.qh.element & UHCI_PTR_TERM)) + uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS)); - for (j = 0; j < UHCI_NUM_SKELQH; j++) - if (qh == uhci->skelqh + j) - return 1; + if (qh->hw.qh.head & UHCI_PTR_TERM) + break; - return 0; + if (qh != bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS)) + qh = bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS); + else { + dbg (KERN_DEBUG MODSTR "qh points to itself!\n"); + break; + } + } } -static const char *qh_names[] = {"interrupt2", "interrupt4", "interrupt8", - "interrupt16", "interrupt32", "interrupt64", - "interrupt128", "interrupt256", "control", - "bulk"}; - -void uhci_show_queues(struct uhci *uhci) +static void uhci_show_sc (int port, unsigned short status) { - int i; - struct uhci_qh *qh; - - for (i = 0; i < UHCI_NUM_SKELQH; ++i) { - printk(" %s: [%p] (%08X) (%08X)\n", qh_names[i], - &uhci->skelqh[i], - uhci->skelqh[i].link, uhci->skelqh[i].element); - - qh = uhci_link_to_qh(uhci->skelqh[i].link); - for (; qh; qh = uhci_link_to_qh(qh->link)) { - if (uhci_is_skeleton_qh(uhci, qh)) - break; + dbg (" stat%d = %04x %s%s%s%s%s%s%s%s\n", + port, + status, + (status & USBPORTSC_SUSP) ? "PortSuspend " : "", + (status & USBPORTSC_PR) ? "PortReset " : "", + (status & USBPORTSC_LSDA) ? "LowSpeed " : "", + (status & USBPORTSC_RD) ? "ResumeDetect " : "", + (status & USBPORTSC_PEC) ? "EnableChange " : "", + (status & USBPORTSC_PE) ? "PortEnabled " : "", + (status & USBPORTSC_CSC) ? "ConnectChange " : "", + (status & USBPORTSC_CCS) ? "PortConnected " : ""); +} - printk(" [%p] (%08X) (%08x)\n", - qh, qh->link, qh->element); +void uhci_show_status (puhci_t s) +{ + unsigned int io_addr = s->io_addr; + unsigned short usbcmd, usbstat, usbint, usbfrnum; + unsigned int flbaseadd; + unsigned char sof; + unsigned short portsc1, portsc2; - uhci_show_queue(qh); - } - } + usbcmd = inw (io_addr + 0); + usbstat = inw (io_addr + 2); + usbint = inw (io_addr + 4); + usbfrnum = inw (io_addr + 6); + flbaseadd = inl (io_addr + 8); + sof = inb (io_addr + 12); + portsc1 = inw (io_addr + 16); + portsc2 = inw (io_addr + 18); + + dbg (" usbcmd = %04x %s%s%s%s%s%s%s%s\n", + usbcmd, + (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", + (usbcmd & USBCMD_CF) ? "CF " : "", + (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", + (usbcmd & USBCMD_FGR) ? "FGR " : "", + (usbcmd & USBCMD_EGSM) ? "EGSM " : "", + (usbcmd & USBCMD_GRESET) ? "GRESET " : "", + (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", + (usbcmd & USBCMD_RS) ? "RS " : ""); + + dbg (" usbstat = %04x %s%s%s%s%s%s\n", + usbstat, + (usbstat & USBSTS_HCH) ? "HCHalted " : "", + (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", + (usbstat & USBSTS_HSE) ? "HostSystemError " : "", + (usbstat & USBSTS_RD) ? "ResumeDetect " : "", + (usbstat & USBSTS_ERROR) ? "USBError " : "", + (usbstat & USBSTS_USBINT) ? "USBINT " : ""); + + dbg (" usbint = %04x\n", usbint); + dbg (" usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1, + 0xfff & (4 * (unsigned int) usbfrnum)); + dbg (" flbaseadd = %08x\n", flbaseadd); + dbg (" sof = %02x\n", sof); + uhci_show_sc (1, portsc1); + uhci_show_sc (2, portsc2); } - diff -u --recursive --new-file v2.3.33/linux/drivers/usb/uhci-debug.h linux/drivers/usb/uhci-debug.h --- v2.3.33/linux/drivers/usb/uhci-debug.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/uhci-debug.h Thu Dec 16 01:27:42 1999 @@ -0,0 +1,7 @@ +void uhci_show_qh(puhci_desc_t qh); +void uhci_show_td(puhci_desc_t td); +void uhci_show_td_queue(puhci_desc_t td); +void uhci_show_queue(puhci_desc_t qh); +void uhci_show_status(puhci_t s); +void beep(long freq); +void dump_urb (purb_t purb); \ No newline at end of file diff -u --recursive --new-file v2.3.33/linux/drivers/usb/uhci.c linux/drivers/usb/uhci.c --- v2.3.33/linux/drivers/usb/uhci.c Tue Nov 23 22:42:21 1999 +++ linux/drivers/usb/uhci.c Fri Dec 17 16:49:45 1999 @@ -1,34 +1,24 @@ -/* - * Universal Host Controller Interface driver for USB. +/* + * Universal Host Controller Interface driver for USB (take II). + * + * (c) 1999 Georg Acher, acher@in.tum.de (executive slave) (base guitar) + * Deti Fliegl, deti@fliegl.de (executive slave) (lead voice) + * Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader) + * Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter) + * + * HW-initalization based on material of * * (C) Copyright 1999 Linus Torvalds * (C) Copyright 1999 Johannes Erdfelt * (C) Copyright 1999 Randy Dunlap * - * Intel documents this fairly well, and as far as I know there - * are no royalties or anything like that, but even so there are - * people who decided that they want to do the same thing in a - * completely different way. - * - * Oh, well. The intel version is the more common by far. As such, - * that's the one I care about right now. - * - * WARNING! The USB documentation is downright evil. Most of it - * is just crap, written by a committee. You're better off ignoring - * most of it, the important stuff is: - * - the low-level protocol (fairly simple but lots of small details) - * - working around the horridness of the rest + * $Id: uhci.c,v 1.139 1999/12/17 17:50:59 fliegl Exp $ */ -/* 4/4/1999 added data toggle for interrupt pipes -keryan */ -/* 5/16/1999 added global toggles for bulk and control */ -/* 6/25/1999 added fix for data toggles on bidirectional bulk endpoints */ -/* - * 1999-09-02: Thomas Sailer - * Added explicit frame list manipulation routines - * for inserting/removing iso td's to/from the frame list. - * START_ABSOLUTE fixes - */ + +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif #include #include @@ -41,2286 +31,2219 @@ #include #include #include -#include -#include +#include /* for in_interrupt() */ +#include #include #include #include #include +//#include +#include "usb.h" #include "uhci.h" +#include "uhci-debug.h" + +//#define DEBUG +#ifdef DEBUG +#define dbg(format, args...) printk(format, ## args) +#else +#define dbg(format, args...) +#endif + +#define _static static + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define __init +#define __exit +#endif + +#ifdef __alpha +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +extern long __kernel_thread (unsigned long, int (*)(void *), void *); +static inline long kernel_thread (int (*fn) (void *), void *arg, unsigned long flags) +{ + return __kernel_thread (flags | CLONE_VM, fn, arg); +} +#undef CONFIG_APM +#endif +#endif #ifdef CONFIG_APM #include -static int handle_apm_event(apm_event_t event); -static int apm_resume = 0; +static int handle_apm_event (apm_event_t event); #endif -static int uhci_debug = 1; +/* We added an UHCI_SLAB slab support just for debugging purposes. In real + life this compile option is NOT recommended, because slab caches are not + suitable for modules. +*/ -#define compile_assert(x) do { switch (0) { case 1: case !(x): } } while (0) +// #define _UHCI_SLAB +#ifdef _UHCI_SLAB +static kmem_cache_t *uhci_desc_kmem; +static kmem_cache_t *urb_priv_kmem; +#endif -static DECLARE_WAIT_QUEUE_HEAD(uhci_configure); +static int rh_submit_urb (purb_t purb); +static int rh_unlink_urb (purb_t purb); +static puhci_t devs = NULL; -static kmem_cache_t *uhci_td_cachep; -static kmem_cache_t *uhci_qh_cachep; +/*-------------------------------------------------------------------*/ +_static void queue_urb (puhci_t s, struct list_head *p, int do_lock) +{ + unsigned long flags=0; -static LIST_HEAD(uhci_list); + if (do_lock) + spin_lock_irqsave (&s->urb_list_lock, flags); + + list_add_tail (p, &s->urb_list); + + if (do_lock) + spin_unlock_irqrestore (&s->urb_list_lock, flags); +} -#define UHCI_DEBUG +/*-------------------------------------------------------------------*/ +_static void dequeue_urb (puhci_t s, struct list_head *p, int do_lock) +{ + unsigned long flags=0; + + if (do_lock) + spin_lock_irqsave (&s->urb_list_lock, flags); -/* - * function prototypes - */ + list_del (p); -static int uhci_get_current_frame_number(struct usb_device *usb_dev); + if (do_lock) + spin_unlock_irqrestore (&s->urb_list_lock, flags); +} -static int uhci_init_isoc(struct usb_device *usb_dev, - unsigned int pipe, - int frame_count, - void *context, - struct usb_isoc_desc **isocdesc); +/*-------------------------------------------------------------------*/ +_static int alloc_td (puhci_desc_t * new, int flags) +{ +#ifdef _UHCI_SLAB + *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL); +#else + *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL); +#endif + if (!*new) + return -ENOMEM; + + memset (*new, 0, sizeof (uhci_desc_t)); + (*new)->hw.td.link = UHCI_PTR_TERM | (flags & UHCI_PTR_BITS); // last by default -static void uhci_free_isoc(struct usb_isoc_desc *isocdesc); + (*new)->type = TD_TYPE; + INIT_LIST_HEAD (&(*new)->vertical); + INIT_LIST_HEAD (&(*new)->horizontal); + + return 0; +} +/*-------------------------------------------------------------------*/ +/* insert td at last position in td-list of qh (vertical) */ +_static int insert_td (puhci_t s, puhci_desc_t qh, puhci_desc_t new, int flags) +{ + uhci_desc_t *prev; + unsigned long xxx; + + spin_lock_irqsave (&s->td_lock, xxx); -static int uhci_run_isoc(struct usb_isoc_desc *isocdesc, - struct usb_isoc_desc *pr_isocdesc); + list_add_tail (&new->vertical, &qh->vertical); -static int uhci_kill_isoc(struct usb_isoc_desc *isocdesc); + if (qh->hw.qh.element & UHCI_PTR_TERM) { + // virgin qh without any tds + qh->hw.qh.element = virt_to_bus (new); /* QH's cannot have the DEPTH bit set */ + } + else { + // already tds inserted + prev = list_entry (new->vertical.prev, uhci_desc_t, vertical); + // implicitely remove TERM bit of prev + prev->hw.td.link = virt_to_bus (new) | (flags & UHCI_PTR_DEPTH); + } + + spin_unlock_irqrestore (&s->td_lock, xxx); + + return 0; +} +/*-------------------------------------------------------------------*/ +/* insert new_td after td (horizontal) */ +_static int insert_td_horizontal (puhci_t s, puhci_desc_t td, puhci_desc_t new, int flags) +{ + uhci_desc_t *next; + unsigned long xxx; + + spin_lock_irqsave (&s->td_lock, xxx); -/* - * Map status to standard result codes - * - * is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)] - * is True for output TDs and False for input TDs. - */ -static int uhci_map_status(int status, int dir_out) + next = list_entry (td->horizontal.next, uhci_desc_t, horizontal); + new->hw.td.link = td->hw.td.link; + list_add (&new->horizontal, &td->horizontal); + td->hw.td.link = virt_to_bus (new); + + spin_unlock_irqrestore (&s->td_lock, xxx); + + return 0; +} +/*-------------------------------------------------------------------*/ +_static int unlink_td (puhci_t s, puhci_desc_t element) { - if (!status) - return USB_ST_NOERROR; - if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ - return USB_ST_BITSTUFF; - if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ - if (dir_out) - return USB_ST_NORESPONSE; - else - return USB_ST_CRC; + uhci_desc_t *next, *prev; + int dir = 0; + unsigned long xxx; + + spin_lock_irqsave (&s->td_lock, xxx); + + next = list_entry (element->vertical.next, uhci_desc_t, vertical); + + if (next == element) { + dir = 1; + next = list_entry (element->horizontal.next, uhci_desc_t, horizontal); + prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal); } - if (status & TD_CTRL_NAK) /* NAK */ - return USB_ST_TIMEOUT; - if (status & TD_CTRL_BABBLE) /* Babble */ - return USB_ST_STALL; - if (status & TD_CTRL_DBUFERR) /* Buffer error */ - return USB_ST_BUFFERUNDERRUN; - if (status & TD_CTRL_STALLED) /* Stalled */ - return USB_ST_STALL; - if (status & TD_CTRL_ACTIVE) /* Active */ - return USB_ST_NOERROR; - - return USB_ST_INTERNALERROR; + else { + prev = list_entry (element->vertical.prev, uhci_desc_t, vertical); + } + + if (prev->type == TD_TYPE) + prev->hw.td.link = element->hw.td.link; + else + prev->hw.qh.element = element->hw.td.link; + + wmb (); + + if (dir == 0) + list_del (&element->vertical); + else + list_del (&element->horizontal); + + spin_unlock_irqrestore (&s->td_lock, xxx); + + return 0; +} +/*-------------------------------------------------------------------*/ +_static int delete_desc (puhci_desc_t element) +{ +#ifdef _UHCI_SLAB + kmem_cache_free(uhci_desc_kmem, element); +#else + kfree (element); +#endif + return 0; +} +/*-------------------------------------------------------------------*/ +// Allocates qh element +_static int alloc_qh (puhci_desc_t * new) +{ +#ifdef _UHCI_SLAB + *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL); +#else + *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL); +#endif + if (!*new) + return -ENOMEM; + + memset (*new, 0, sizeof (uhci_desc_t)); + (*new)->hw.qh.head = UHCI_PTR_TERM; + (*new)->hw.qh.element = UHCI_PTR_TERM; + (*new)->type = QH_TYPE; + INIT_LIST_HEAD (&(*new)->horizontal); + INIT_LIST_HEAD (&(*new)->vertical); + + dbg (KERN_DEBUG MODSTR "Allocated qh @ %p\n", *new); + + return 0; } +/*-------------------------------------------------------------------*/ +// inserts new qh before/after the qh at pos +// flags: 0: insert before pos, 1: insert after pos (for low speed transfers) +_static int insert_qh (puhci_t s, puhci_desc_t pos, puhci_desc_t new, int flags) +{ + puhci_desc_t old; + unsigned long xxx; + + spin_lock_irqsave (&s->qh_lock, xxx); + + if (!flags) { + // (OLD) (POS) -> (OLD) (NEW) (POS) + old = list_entry (pos->horizontal.prev, uhci_desc_t, horizontal); + list_add_tail (&new->horizontal, &pos->horizontal); + new->hw.qh.head = MAKE_QH_ADDR (pos) ; + + if (!(old->hw.qh.head & UHCI_PTR_TERM)) + old->hw.qh.head = MAKE_QH_ADDR (new) ; + } + else { + // (POS) (OLD) -> (POS) (NEW) (OLD) + old = list_entry (pos->horizontal.next, uhci_desc_t, horizontal); + list_add (&new->horizontal, &pos->horizontal); + pos->hw.qh.head = MAKE_QH_ADDR (new) ; + new->hw.qh.head = MAKE_QH_ADDR (old); + } -/* - * Return the result of a TD.. - */ -static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned long *rval) + wmb (); + + spin_unlock_irqrestore (&s->qh_lock, xxx); + + return 0; +} +/*-------------------------------------------------------------------*/ +_static int unlink_qh (puhci_t s, puhci_desc_t element) { - unsigned int status; - struct uhci_td *tmp; - int count = 1000, actlength, explength; - - /* Start at the TD first in the chain, if possible */ - if (td->qh && td->qh->first) - tmp = td->qh->first; - else - tmp = td; + puhci_desc_t next, prev; + unsigned long xxx; - if (!tmp) - return USB_ST_INTERNALERROR; + spin_lock_irqsave (&s->qh_lock, xxx); + + next = list_entry (element->horizontal.next, uhci_desc_t, horizontal); + prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal); + prev->hw.qh.head = element->hw.qh.head; + wmb (); + list_del (&element->horizontal); + + spin_unlock_irqrestore (&s->qh_lock, xxx); + + return 0; +} +/*-------------------------------------------------------------------*/ +_static int delete_qh (puhci_t s, puhci_desc_t qh) +{ + puhci_desc_t td; + struct list_head *p; - if (rval) - *rval = 0; + list_del (&qh->horizontal); + + while ((p = qh->vertical.next) != &qh->vertical) { + td = list_entry (p, uhci_desc_t, vertical); + unlink_td (s, td); + delete_desc (td); + } + + delete_desc (qh); + + return 0; +} +/*-------------------------------------------------------------------*/ +void clean_td_chain (puhci_desc_t td) +{ + struct list_head *p; + puhci_desc_t td1; - /* Locate the first failing td, if any */ - do { - status = uhci_status_bits(tmp->status); + if (!td) + return; + + while ((p = td->horizontal.next) != &td->horizontal) { + td1 = list_entry (p, uhci_desc_t, horizontal); + delete_desc (td1); + } + + delete_desc (td); +} +/*-------------------------------------------------------------------*/ +// Removes ALL qhs in chain (paranoia!) +_static void cleanup_skel (puhci_t s) +{ + unsigned int n; + puhci_desc_t td; - if (status) - break; + printk (KERN_DEBUG MODSTR "Cleanup_skel\n"); + + for (n = 0; n < 8; n++) { + td = s->int_chain[n]; + clean_td_chain (td); + } - /* The length field is only valid if the TD was completed */ - if (!(tmp->status & TD_CTRL_ACTIVE) && uhci_packetin(tmp->info)) { - explength = uhci_expected_length(tmp->info); - actlength = uhci_actual_length(tmp->status); - if (rval) - *rval += actlength; - - if (explength != actlength && - ((tmp->pipetype == PIPE_BULK) || (tmp->pipetype == PIPE_CONTROL))) { - /* If the packet is short, none of the */ - /* packets after this were processed, so */ - /* fix the DT accordingly */ - if (in_interrupt() || uhci_debug) - printk(KERN_DEBUG "Set toggle from %p rval %ld%c for status=%x to %d, exp=%d, act=%d\n", - tmp, rval ? *rval : 0, - rval ? '*' : '/', tmp->status, - uhci_toggle(tmp->info) ^ 1, - explength, actlength); - usb_settoggle(dev->usb, uhci_endpoint(tmp->info), - uhci_packetout(tmp->info), - uhci_toggle(tmp->info) ^ 1); - break; /* Short packet */ - } + if (s->iso_td) { + for (n = 0; n < 1024; n++) { + td = s->iso_td[n]; + clean_td_chain (td); } + kfree (s->iso_td); + } - if ((tmp->link & UHCI_PTR_TERM) || (tmp->link & UHCI_PTR_QH)) - break; + if (s->framelist) + free_page ((unsigned long) s->framelist); - tmp = uhci_ptr_to_virt(tmp->link); - } while (--count); + if (s->control_chain) { + // completed init_skel? + struct list_head *p; + puhci_desc_t qh, qh1; - if (!count) { - printk(KERN_ERR "runaway td's in uhci_td_result!\n"); - } else { - /* If we got to the last TD */ - - /* No error */ - if (!status) - return USB_ST_NOERROR; - - /* APC BackUPS Pro kludge */ - /* It tries to send all of the descriptor instead of */ - /* the amount we requested */ - if (tmp->status & TD_CTRL_IOC && - tmp->status & TD_CTRL_ACTIVE && - tmp->status & TD_CTRL_NAK && - tmp->pipetype == PIPE_CONTROL) - return USB_ST_NOERROR; - - /* We got to an error, but the controller hasn't finished */ - /* with it yet */ - if (tmp->status & TD_CTRL_ACTIVE) - return USB_ST_NOCHANGE; - - /* If this wasn't the last TD and SPD is set, ACTIVE */ - /* is not and NAK isn't then we received a short */ - /* packet */ - if (tmp->status & TD_CTRL_SPD && !(tmp->status & TD_CTRL_NAK)) - return USB_ST_NOERROR; - } - - /* Some debugging code */ - if (!count || (!in_interrupt() && uhci_debug)) { - printk(KERN_DEBUG "uhci_td_result() failed with status %x\n", - status); - - /* Print the chain for debugging purposes */ - if (td->qh) - uhci_show_queue(td->qh); - else - uhci_show_td(td); + qh = s->control_chain; + while ((p = qh->horizontal.next) != &qh->horizontal) { + qh1 = list_entry (p, uhci_desc_t, horizontal); + delete_qh (s, qh1); + } + delete_qh (s, qh); } + else { + if (s->control_chain) + kfree (s->control_chain); + if (s->bulk_chain) + kfree (s->bulk_chain); + if (s->chain_end) + kfree (s->chain_end); + } +} +/*-------------------------------------------------------------------*/ +// allocates framelist and qh-skeletons +// only HW-links provide continous linking, SW-links stay in their domain (ISO/INT) +_static int init_skel (puhci_t s) +{ + int n, ret; + puhci_desc_t qh, td; + + dbg (KERN_DEBUG MODSTR "init_skel\n"); + + s->framelist = (__u32 *) get_free_page (GFP_KERNEL); + + if (!s->framelist) + return -ENOMEM; + + memset (s->framelist, 0, 4096); + + dbg (KERN_DEBUG MODSTR "allocating iso desc pointer list\n"); + s->iso_td = (puhci_desc_t *) kmalloc (1024 * sizeof (puhci_desc_t), GFP_KERNEL); + + if (!s->iso_td) + goto init_skel_cleanup; - if (status & TD_CTRL_STALLED) { - /* endpoint has stalled - mark it halted */ - usb_endpoint_halt(dev->usb, uhci_endpoint(tmp->info), - uhci_packetout(tmp->info)); - return USB_ST_STALL; + s->control_chain = NULL; + s->bulk_chain = NULL; + s->chain_end = NULL; + + dbg (KERN_DEBUG MODSTR "allocating iso descs\n"); + for (n = 0; n < 1024; n++) { + // allocate skeleton iso/irq-tds + ret = alloc_td (&td, 0); + if (ret) + goto init_skel_cleanup; + s->iso_td[n] = td; + s->framelist[n] = ((__u32) virt_to_bus (td)); } - if ((status == TD_CTRL_ACTIVE) && (!rval)) - return USB_ST_DATAUNDERRUN; + dbg (KERN_DEBUG MODSTR "allocating qh: chain_end\n"); + ret = alloc_qh (&qh); + + if (ret) + goto init_skel_cleanup; + + s->chain_end = qh; - return uhci_map_status(status, uhci_packetout(tmp->info)); -} + dbg (KERN_DEBUG MODSTR "allocating qh: bulk_chain\n"); + ret = alloc_qh (&qh); + + if (ret) + goto init_skel_cleanup; + + insert_qh (s, s->chain_end, qh, 0); + s->bulk_chain = qh; + dbg (KERN_DEBUG MODSTR "allocating qh: control_chain\n"); + ret = alloc_qh (&qh); + + if (ret) + goto init_skel_cleanup; + + insert_qh (s, s->bulk_chain, qh, 0); + s->control_chain = qh; + for (n = 0; n < 8; n++) + s->int_chain[n] = 0; -/* - * Inserts a td into qh list at the top. - * - * Careful about atomicity: even on UP this - * requires a locked access due to the concurrent - * DMA engine. - * - * NOTE! This assumes that first->last is a valid - * list of TD's with the proper backpointers set - * up and all.. - */ -static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct uhci_td *first, struct uhci_td *last) -{ - unsigned int link = qh->element; - unsigned int new = virt_to_bus(first) | UHCI_PTR_DEPTH; + dbg (KERN_DEBUG MODSTR "Allocating skeleton INT-TDs\n"); + + for (n = 0; n < 8; n++) { + puhci_desc_t td; - for (;;) { - unsigned char success; + alloc_td (&td, 0); + if (!td) + goto init_skel_cleanup; + s->int_chain[n] = td; + if (n == 0) { + s->int_chain[0]->hw.td.link = virt_to_bus (s->control_chain); + } + else { + s->int_chain[n]->hw.td.link = virt_to_bus (s->int_chain[0]); + } + } - last->link = link; - first->backptr = &qh->element; - asm volatile("lock ; cmpxchg %4,%2 ; sete %0" - :"=q" (success), "=a" (link) - :"m" (qh->element), "1" (link), "r" (new) - :"memory"); - - if (success) { - /* Was there a successor entry? Fix it's backpointer */ - if ((link & UHCI_PTR_TERM) == 0) { - struct uhci_td *next = uhci_ptr_to_virt(link); - next->backptr = &last->link; + dbg (KERN_DEBUG MODSTR "Linking skeleton INT-TDs\n"); + + for (n = 0; n < 1024; n++) { + // link all iso-tds to the interrupt chains + int m, o; + //dbg("framelist[%i]=%x\n",n,s->framelist[n]); + for (o = 1, m = 2; m <= 128; o++, m += m) { + // n&(m-1) = n%m + if ((n & (m - 1)) == ((m - 1) / 2)) { + ((puhci_desc_t) s->iso_td[n])->hw.td.link = virt_to_bus (s->int_chain[o]); } - break; } } - qh->first = first; - first->qh = qh; - last->qh = qh; -} + //uhci_show_queue(s->control_chain); + dbg (KERN_DEBUG MODSTR "init_skel exit\n"); + return 0; // OK -static inline void uhci_insert_td_in_qh(struct uhci_qh *qh, struct uhci_td *td) -{ - uhci_insert_tds_in_qh(qh, td, td); + init_skel_cleanup: + cleanup_skel (s); + return -ENOMEM; } -static void uhci_insert_qh(struct uhci_qh *qh, struct uhci_qh *newqh) +/*-------------------------------------------------------------------*/ +_static void fill_td (puhci_desc_t td, int status, int info, __u32 buffer) { - newqh->link = qh->link; - qh->link = virt_to_bus(newqh) | UHCI_PTR_QH; + td->hw.td.status = status; + td->hw.td.info = info; + td->hw.td.buffer = buffer; } -static void uhci_remove_qh(struct uhci_qh *qh, struct uhci_qh *remqh) +/*-------------------------------------------------------------------*/ +// LOW LEVEL STUFF +// assembles QHs und TDs for control, bulk and iso +/*-------------------------------------------------------------------*/ +_static int uhci_submit_control_urb (purb_t purb) { - struct uhci_qh *lqh = qh; + puhci_desc_t qh, td; + puhci_t s = (puhci_t) purb->dev->bus->hcpriv; + purb_priv_t purb_priv = purb->hcpriv; + unsigned long destination, status; + int maxsze = usb_maxpacket (purb->dev, purb->pipe, usb_pipeout (purb->pipe)); + unsigned long len, bytesrequested; + char *data; - while (uhci_ptr_to_virt(lqh->link) != remqh) { - if (lqh->link & UHCI_PTR_TERM) - break; + dbg (KERN_DEBUG MODSTR "uhci_submit_control start\n"); + alloc_qh (&qh); // alloc qh for this request - lqh = (struct uhci_qh *)uhci_ptr_to_virt(lqh->link); - } + if (!qh) + return -ENOMEM; - if (lqh->link & UHCI_PTR_TERM) { - printk(KERN_DEBUG "couldn't find qh in chain!\n"); - return; - } + alloc_td (&td, UHCI_PTR_DEPTH); // get td for setup stage - lqh->link = remqh->link; -} + if (!td) { + delete_qh (s, qh); + return -ENOMEM; + } -/* - * Removes td from qh if present. - * - * NOTE! We keep track of both forward and back-pointers, - * so this should be trivial, right? - * - * Wrong. While all TD insert/remove operations are synchronous - * on the CPU, the UHCI controller can (and does) play with the - * very first forward pointer. So we need to validate the backptr - * before we change it, so that we don't by mistake reset the QH - * head to something old. - */ -static void uhci_remove_td(struct uhci_td *td) -{ - unsigned int *backptr = td->backptr; - unsigned int link = td->link; - unsigned int me; + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (purb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; - if (!backptr) - return; + /* 3 errors */ + status = (purb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | + (purb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); - td->backptr = NULL; + /* Build the TD for the control request, try forever, 8 bytes of data */ + fill_td (td, status, destination | (7 << 21), virt_to_bus (purb->setup_packet)); - /* - * This is the easy case: the UHCI will never change "td->link", - * so we can always just look at that and fix up the backpointer - * of any next element.. - */ - if (!(link & UHCI_PTR_TERM)) { - struct uhci_td *next = uhci_ptr_to_virt(link); - next->backptr = backptr; - } + /* If direction is "send", change the frame from SETUP (0x2D) + to OUT (0xE1). Else change it from SETUP to IN (0x69). */ - /* - * The nasty case is "backptr->next", which we need to - * update to "link" _only_ if "backptr" still points - * to us (it may not: maybe backptr is a QH->element - * pointer and the UHCI has changed the value). - */ - me = virt_to_bus(td) | (0xe & *backptr); - asm volatile("lock ; cmpxchg %0,%1" - : - :"r" (link), "m" (*backptr), "a" (me) - :"memory"); + destination ^= (USB_PID_SETUP ^ USB_PID_IN); /* SETUP -> IN */ + + if (usb_pipeout (purb->pipe)) + destination ^= (USB_PID_IN ^ USB_PID_OUT); /* IN -> OUT */ - /* Reset it just in case */ - td->link = UHCI_PTR_TERM; -} + insert_td (s, qh, td, 0); // queue 'setup stage'-td in qh +#if 0 + printk ("SETUP to pipe %x: %x %x %x %x %x %x %x %x\n", purb->pipe, + purb->setup_packet[0], purb->setup_packet[1], purb->setup_packet[2], purb->setup_packet[3], + purb->setup_packet[4], purb->setup_packet[5], purb->setup_packet[6], purb->setup_packet[7]); + //uhci_show_td(td); +#endif + /* Build the DATA TD's */ + len = purb->transfer_buffer_length; + bytesrequested = len; + data = purb->transfer_buffer; + + while (len > 0) { + int pktsze = len; -/* - * Only the USB core should call uhci_alloc_dev and uhci_free_dev - */ -static int uhci_alloc_dev(struct usb_device *usb_dev) -{ - struct uhci_device *dev; + alloc_td (&td, UHCI_PTR_DEPTH); + if (!td) { + delete_qh (s, qh); + return -ENOMEM; + } - /* Allocate the UHCI device private data */ - dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -1; + if (pktsze > maxsze) + pktsze = maxsze; - /* Initialize "dev" */ - memset(dev, 0, sizeof(*dev)); + destination ^= 1 << TD_TOKEN_TOGGLE; // toggle DATA0/1 - usb_dev->hcpriv = dev; - dev->usb = usb_dev; - atomic_set(&dev->refcnt, 1); + fill_td (td, status, destination | ((pktsze - 1) << 21), + virt_to_bus (data)); // Status, pktsze bytes of data - if (usb_dev->parent) - dev->uhci = usb_to_uhci(usb_dev->parent)->uhci; + insert_td (s, qh, td, UHCI_PTR_DEPTH); // queue 'data stage'-td in qh - return 0; -} + data += pktsze; + len -= pktsze; + } -static int uhci_free_dev(struct usb_device *usb_dev) -{ - struct uhci_device *dev = usb_to_uhci(usb_dev); + /* Build the final TD for control status */ + /* It's only IN if the pipe is out AND we aren't expecting data */ + destination &= ~0xFF; + + if (usb_pipeout (purb->pipe) | (bytesrequested == 0)) + destination |= USB_PID_IN; + else + destination |= USB_PID_OUT; - if (atomic_dec_and_test(&dev->refcnt)) - kfree(dev); + destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */ - return 0; -} + alloc_td (&td, UHCI_PTR_DEPTH); + + if (!td) { + delete_qh (s, qh); + return -ENOMEM; + } -static void uhci_inc_dev_use(struct uhci_device *dev) -{ - atomic_inc(&dev->refcnt); -} + /* no limit on errors on final packet , 0 bytes of data */ + fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21), + 0); -static void uhci_dec_dev_use(struct uhci_device *dev) -{ - uhci_free_dev(dev->usb); -} + insert_td (s, qh, td, UHCI_PTR_DEPTH); // queue status td -static struct uhci_td *uhci_td_alloc(struct uhci_device *dev) -{ - struct uhci_td *td; - td = kmem_cache_alloc(uhci_td_cachep, SLAB_KERNEL); - if (!td) - return NULL; + list_add (&qh->desc_list, &purb_priv->desc_list); -#ifdef UHCI_DEBUG - if ((__u32)td & UHCI_PTR_BITS) - printk("qh not 16 byte aligned!\n"); -#endif + /* Start it up... put low speed first */ + if (purb->pipe & TD_CTRL_LS) + insert_qh (s, s->control_chain, qh, 1); // insert after control chain + else + insert_qh (s, s->bulk_chain, qh, 0); // insert before bulk chain + //uhci_show_queue(s->control_chain); - td->link = UHCI_PTR_TERM; - td->buffer = 0; + dbg (KERN_DEBUG MODSTR "uhci_submit_control end\n"); + return 0; +} +/*-------------------------------------------------------------------*/ +_static int uhci_submit_bulk_urb (purb_t purb) +{ + puhci_t s = (puhci_t) purb->dev->bus->hcpriv; + purb_priv_t purb_priv = purb->hcpriv; + puhci_desc_t qh, td; + unsigned long destination, status; + char *data; + unsigned int pipe = purb->pipe; + int maxsze = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe)); + int info, len; + + /* shouldn't the clear_halt be done in the USB core or in the client driver? - Thomas */ + if (usb_endpoint_halted (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) && + usb_clear_halt (purb->dev, usb_pipeendpoint (pipe) | (pipe & USB_DIR_IN))) + return -EPIPE; + + if (!maxsze) + return -EMSGSIZE; + /* FIXME: should tell the client that the endpoint is invalid, i.e. not in the descriptor */ - td->backptr = NULL; - td->qh = NULL; - td->dev_id = NULL; - td->dev = dev; - td->flags = 0; - INIT_LIST_HEAD(&td->irq_list); - atomic_set(&td->refcnt, 1); + alloc_qh (&qh); // get qh for this request - uhci_inc_dev_use(dev); + if (!qh) + return -ENOMEM; - return td; -} + /* The "pipe" thing contains the destination in bits 8--18. */ + destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); -static void uhci_td_free(struct uhci_td *td) -{ - if (atomic_dec_and_test(&td->refcnt)) { - kmem_cache_free(uhci_td_cachep, td); + /* 3 errors */ + status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | + ((purb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD) | (3 << 27); - if (td->dev) - uhci_dec_dev_use(td->dev); - } -} + /* Build the TDs for the bulk request */ + len = purb->transfer_buffer_length; + data = purb->transfer_buffer; + dbg (KERN_DEBUG MODSTR "uhci_submit_bulk_urb: len %d\n", len); + + while (len > 0) { + int pktsze = len; -static struct uhci_qh *uhci_qh_alloc(struct uhci_device *dev) -{ - struct uhci_qh *qh; + alloc_td (&td, UHCI_PTR_DEPTH); - qh = kmem_cache_alloc(uhci_qh_cachep, SLAB_KERNEL); - if (!qh) - return NULL; + if (!td) { + delete_qh (s, qh); + return -ENOMEM; + } -#ifdef UHCI_DEBUG - if ((__u32)qh & UHCI_PTR_BITS) - printk("qh not 16 byte aligned!\n"); -#endif + if (pktsze > maxsze) + pktsze = maxsze; - qh->element = UHCI_PTR_TERM; - qh->link = UHCI_PTR_TERM; + // pktsze bytes of data + info = destination | ((pktsze - 1) << 21) | + (usb_gettoggle (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE); - qh->dev = dev; - qh->skel = NULL; - qh->first = NULL; - init_waitqueue_head(&qh->wakeup); - atomic_set(&qh->refcnt, 1); + fill_td (td, status, info, virt_to_bus (data)); - uhci_inc_dev_use(dev); + data += pktsze; + len -= pktsze; - return qh; -} + if (!len) + td->hw.td.status |= TD_CTRL_IOC; // last one generates INT + //dbg("insert td %p, len %i\n",td,pktsze); -static void uhci_qh_free(struct uhci_qh *qh) -{ - if (atomic_dec_and_test(&qh->refcnt)) { - kmem_cache_free(uhci_qh_cachep, qh); + insert_td (s, qh, td, UHCI_PTR_DEPTH); - if (qh->dev) - uhci_dec_dev_use(qh->dev); + /* Alternate Data0/1 (start with Data0) */ + usb_dotoggle (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); } -} - -/* - * UHCI interrupt list operations.. - */ -static spinlock_t irqlist_lock = SPIN_LOCK_UNLOCKED; -static void uhci_add_irq_list(struct uhci *uhci, struct uhci_td *td, usb_device_irq completed, void *dev_id) -{ - unsigned long flags; + list_add (&qh->desc_list, &purb_priv->desc_list); - td->completed = completed; - td->dev_id = dev_id; + insert_qh (s, s->chain_end, qh, 0); // insert before end marker + //uhci_show_queue(s->bulk_chain); - spin_lock_irqsave(&irqlist_lock, flags); - list_add(&td->irq_list, &uhci->interrupt_list); - spin_unlock_irqrestore(&irqlist_lock, flags); + dbg (KERN_DEBUG MODSTR "uhci_submit_bulk_urb: exit\n"); + return 0; } +/*-------------------------------------------------------------------*/ +// unlinks an urb by dequeuing its qh, waits some frames and forgets it +// Problem: unlinking in interrupt requires waiting for one frame (udelay) +// to allow the whole structures to be safely removed +_static int uhci_unlink_urb (purb_t purb) +{ + puhci_t s; + puhci_desc_t qh; + puhci_desc_t td; + purb_priv_t purb_priv; + unsigned long flags=0; + struct list_head *p; -static void uhci_remove_irq_list(struct uhci_td *td) -{ - unsigned long flags; + if (!purb) // you never know... - spin_lock_irqsave(&irqlist_lock, flags); - list_del(&td->irq_list); - spin_unlock_irqrestore(&irqlist_lock, flags); -} + return -1; -/* - * frame list manipulation. Used for Isochronous transfers. - * the list of (iso) TD's enqueued in a frame list entry - * is basically a doubly linked list with link being - * the forward pointer and backptr the backward ptr. - * the frame list entry itself doesn't have a back ptr - * (therefore the list is not circular), and the forward pointer - * stops at link entries having the UHCI_PTR_TERM or the UHCI_PTR_QH - * bit set. Maybe it could be extended to handle the QH's also, - * but it doesn't seem necessary right now. - * The layout looks as follows: - * frame list pointer -> iso td's (if any) -> - * periodic interrupt td (if framelist 0) -> irq qh -> control qh -> bulk qh - */ - -static spinlock_t framelist_lock = SPIN_LOCK_UNLOCKED; - -static void uhci_add_frame_list(struct uhci *uhci, struct uhci_td *td, unsigned framenum) -{ - unsigned long flags; - struct uhci_td *nexttd; + s = (puhci_t) purb->dev->bus->hcpriv; // get pointer to uhci struct - framenum %= UHCI_NUMFRAMES; - spin_lock_irqsave(&framelist_lock, flags); - td->backptr = &uhci->fl->frame[framenum]; - td->link = uhci->fl->frame[framenum]; - if (!(td->link & (UHCI_PTR_TERM | UHCI_PTR_QH))) { - nexttd = (struct uhci_td *)uhci_ptr_to_virt(td->link); - nexttd->backptr = &td->link; - } - wmb(); - uhci->fl->frame[framenum] = virt_to_bus(td); - spin_unlock_irqrestore(&framelist_lock, flags); -} - -static void uhci_remove_frame_list(struct uhci *uhci, struct uhci_td *td) -{ - unsigned long flags; - struct uhci_td *nexttd; + if (usb_pipedevice (purb->pipe) == s->rh.devnum) + return rh_unlink_urb (purb); - if (!td->backptr) - return; - spin_lock_irqsave(&framelist_lock, flags); - *(td->backptr) = td->link; - if (!(td->link & (UHCI_PTR_TERM | UHCI_PTR_QH))) { - nexttd = (struct uhci_td *)uhci_ptr_to_virt(td->link); - nexttd->backptr = td->backptr; + if(!in_interrupt()) { + // is the following really necessary? dequeue_urb has its own spinlock (GA) + spin_lock_irqsave (&s->unlink_urb_lock, flags); // do not allow interrupts } - spin_unlock_irqrestore(&framelist_lock, flags); - td->backptr = NULL; - /* - * attention: td->link might still be in use by the - * hardware if the td is still active and the hardware - * was processing it. So td->link should be preserved - * until the frame number changes. Don't know what to do... - * udelay(1000) doesn't sound nice, and schedule() - * can't be used as this is called from within interrupt context. - */ - /* - * we should do the same thing as we do with the QH's - * see uhci_insert_tds_in_qh and uhci_remove_td --jerdfelt - */ - /* for now warn if there's a possible problem */ - if (td->status & TD_CTRL_ACTIVE) { - unsigned frn = inw(uhci->io_addr + USBFRNUM); - __u32 link = uhci->fl->frame[frn % UHCI_NUMFRAMES]; - if (!(link & (UHCI_PTR_TERM | UHCI_PTR_QH))) { - struct uhci_td *tdl = (struct uhci_td *)uhci_ptr_to_virt(link); - for (;tdl;) { - if (tdl == td) { - printk(KERN_WARNING "uhci_remove_frame_list: td possibly still in use!!\n"); - break; - } - if (tdl->link & (UHCI_PTR_TERM | UHCI_PTR_QH)) - break; - tdl = (struct uhci_td *)uhci_ptr_to_virt(tdl->link); - } + + //dbg("unlink_urb called %p\n",purb); + if (purb->status == USB_ST_URB_PENDING) { + // URB probably still in work + purb_priv = purb->hcpriv; + dequeue_urb (s, &purb->urb_list,1); + if(!in_interrupt()) { + spin_unlock_irqrestore (&s->unlink_urb_lock, flags); // allow interrupts from here } - } -} + switch (usb_pipetype (purb->pipe)) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + for (p = purb_priv->desc_list.next; p != &purb_priv->desc_list; p = p->next) { + td = list_entry (p, uhci_desc_t, desc_list); + unlink_td (s, td); + } + // wait at least 1 Frame + if (in_interrupt ()) + udelay (1000); + else + schedule_timeout (1 + 1 * HZ / 1000); + while ((p = purb_priv->desc_list.next) != &purb_priv->desc_list) { + td = list_entry (p, uhci_desc_t, desc_list); + list_del (p); + delete_desc (td); + } + break; -/* - * This function removes and disallocates all structures set up for a transfer. - * It takes the qh out of the skeleton, removes the tq and the td's. - * It only removes the associated interrupt handler if removeirq is set. - * The *td argument is any td in the list of td's. - */ -static void uhci_remove_transfer(struct uhci_td *td, char removeirq) -{ - int count = 1000; - struct uhci_td *curtd; - unsigned int nextlink; + case PIPE_BULK: + case PIPE_CONTROL: + qh = list_entry (purb_priv->desc_list.next, uhci_desc_t, desc_list); - if (td->qh && td->qh->first) - curtd = td->qh->first; - else - curtd = td; + unlink_qh (s, qh); // remove this qh from qh-list + // wait at least 1 Frame - /* Remove the QH from the skeleton and then free it */ - uhci_remove_qh(td->qh->skel, td->qh); - uhci_qh_free(td->qh); - - do { - nextlink = curtd->link; - - /* IOC? => remove handler */ - /* HACK: Don't remove if already removed. Prevents deadlock */ - /* in uhci_interrupt_notify and callbacks */ - if (removeirq && (td->status & TD_CTRL_IOC) && - td->irq_list.next != &td->irq_list) - uhci_remove_irq_list(td); - - /* Remove the TD and then free it */ - uhci_remove_td(curtd); - uhci_td_free(curtd); + if (in_interrupt ()) + udelay (1000); + else + schedule_timeout (1 + 1 * HZ / 1000); + delete_qh (s, qh); // remove it physically - if (nextlink & UHCI_PTR_TERM) /* Tail? */ - break; + } + purb->status = USB_ST_URB_KILLED; // mark urb as killed - curtd = (struct uhci_td *)uhci_ptr_to_virt(nextlink & ~UHCI_PTR_BITS); - } while (count--); +#ifdef _UHCI_SLAB + kmem_cache_free(urb_priv_kmem, purb->hcpriv); +#else + kfree (purb->hcpriv); +#endif + if (purb->complete) { + dbg (KERN_DEBUG MODSTR "unlink_urb: calling completion\n"); + purb->complete ((struct urb *) purb); + usb_dec_dev_use (purb->dev); + } + return 0; + } + else { + if(!in_interrupt()) + spin_unlock_irqrestore (&s->unlink_urb_lock, flags); // allow interrupts from here + } - if (!count) - printk(KERN_ERR "runaway td's!?\n"); + return 0; } +/*-------------------------------------------------------------------*/ +// In case of ASAP iso transfer, search the URB-list for already queued URBs +// for this EP and calculate the earliest start frame for the new +// URB (easy seamless URB continuation!) +_static int find_iso_limits (purb_t purb, unsigned int *start, unsigned int *end) +{ + purb_t u, last_urb = NULL; + puhci_t s = (puhci_t) purb->dev->bus->hcpriv; + struct list_head *p = s->urb_list.next; + int ret=-1; + unsigned long flags; + + spin_lock_irqsave (&s->urb_list_lock, flags); -/* - * Request an interrupt handler.. - * - * Returns 0 (success) or negative (failure). - * Also returns/sets a "handle pointer" that release_irq can use to stop this - * interrupt. (It's really a pointer to the TD.) - */ -static int uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, - usb_device_irq handler, int period, - void *dev_id, void **handle, long bustime) -{ - struct uhci_device *dev = usb_to_uhci(usb_dev); - struct uhci_td *td = uhci_td_alloc(dev); - struct uhci_qh *qh = uhci_qh_alloc(dev); - unsigned int destination, status; - - if (!td || !qh) { - if (td) - uhci_td_free(td); - if (qh) - uhci_qh_free(qh); - return USB_ST_INTERNALERROR; + for (; p != &s->urb_list; p = p->next) { + u = list_entry (p, urb_t, urb_list); + // look for pending URBs with identical pipe handle + // works only because iso doesn't toggle the data bit! + if ((purb->pipe == u->pipe) && (purb->dev == u->dev) && (u->status == USB_ST_URB_PENDING)) { + if (!last_urb) + *start = u->start_frame; + last_urb = u; + } } + + if (last_urb) { + *end = (last_urb->start_frame + last_urb->number_of_packets) & 1023; + ret=0; + } + + spin_unlock_irqrestore(&s->urb_list_lock, flags); + + return ret; // no previous urb found - /* Destination: pipe destination with INPUT */ - destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid(pipe); - - /* Infinite errors is 0, so no bits */ - status = (pipe & TD_CTRL_LS) | TD_CTRL_IOC | TD_CTRL_ACTIVE | - TD_CTRL_SPD; - - td->link = UHCI_PTR_TERM; /* Terminate */ - td->status = status; /* In */ - td->info = destination | ((usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)) - 1) << 21) | - (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << TD_TOKEN_TOGGLE); - - td->buffer = virt_to_bus(dev->data); - td->qh = qh; - td->dev = dev; - td->pipetype = PIPE_INTERRUPT; - td->bandwidth_alloc = bustime; - - /* if period 0, set _REMOVE flag */ - if (!period) - td->flags |= UHCI_TD_REMOVE; - - qh->skel = &dev->uhci->skelqh[__interval_to_skel(period)]; - - uhci_add_irq_list(dev->uhci, td, handler, dev_id); - - uhci_insert_td_in_qh(qh, td); - - /* Add it into the skeleton */ - uhci_insert_qh(qh->skel, qh); - - *handle = (void *)td; - - return 0; } +/*-------------------------------------------------------------------*/ +// adjust start_frame according to scheduling constraints (ASAP etc) -/* - * Release an interrupt handler previously allocated using - * uhci_request_irq. This function does no validity checking, so make - * sure you're not releasing an already released handle as it may be - * in use by something else.. - * - * This function can NOT be called from an interrupt. - */ -static int uhci_release_irq(struct usb_device *usb, void *handle) +_static void jnx_show_desc (puhci_desc_t d) { - struct uhci_td *td; - struct uhci_qh *qh; - - td = (struct uhci_td *)handle; - if (!td) - return USB_ST_INTERNALERROR; - - qh = td->qh; - - /* Remove it from the internal irq_list */ - uhci_remove_irq_list(td); - - /* Remove the interrupt TD and QH */ - uhci_remove_td(td); - uhci_remove_qh(qh->skel, qh); - - if (td->completed != NULL) - td->completed(USB_ST_REMOVED, NULL, 0, td->dev_id); + switch (d->type) { + case TD_TYPE: + printk (KERN_DEBUG MODSTR "td @ 0x%08lx: link 0x%08x status 0x%08x info 0x%08x buffer 0x%08x\n", + (unsigned long) d, d->hw.td.link, d->hw.td.status, d->hw.td.info, d->hw.td.buffer); + if (!(d->hw.td.link & UHCI_PTR_TERM)) + jnx_show_desc ((puhci_desc_t) bus_to_virt (d->hw.td.link & ~UHCI_PTR_BITS)); + break; - /* Free the TD and QH */ - uhci_td_free(td); - uhci_qh_free(qh); + case QH_TYPE: + printk (KERN_DEBUG MODSTR "qh @ 0x%08lx: head 0x%08x element 0x%08x\n", + (unsigned long) d, d->hw.qh.head, d->hw.qh.element); + if (!(d->hw.qh.element & UHCI_PTR_TERM)) + jnx_show_desc ((puhci_desc_t) bus_to_virt (d->hw.qh.element & ~UHCI_PTR_BITS)); + if (!(d->hw.qh.head & UHCI_PTR_TERM)) + jnx_show_desc ((puhci_desc_t) bus_to_virt (d->hw.qh.head & ~UHCI_PTR_BITS)); + break; - return USB_ST_NOERROR; -} /* uhci_release_irq() */ + default: + printk (KERN_DEBUG MODSTR "desc @ 0x%08lx: invalid type %u\n", + (unsigned long) d, d->type); + } +} -/* - * uhci_get_current_frame_number() - * - * returns the current frame number for a USB bus/controller. - */ -static int uhci_get_current_frame_number(struct usb_device *usb_dev) +/*-------------------------------------------------------------------*/ +_static int iso_find_start (purb_t purb) { - return inw (usb_to_uhci(usb_dev)->uhci->io_addr + USBFRNUM); -} + puhci_t s = (puhci_t) purb->dev->bus->hcpriv; + unsigned int now; + unsigned int start_limit = 0, stop_limit = 0, queued_size; + int limits; + now = UHCI_GET_CURRENT_FRAME (s) & 1023; -/* - * uhci_init_isoc() - * - * Allocates some data structures. - * Initializes parts of them from the function parameters. - * - * It does not associate any data/buffer pointers or - * driver (caller) callback functions with the allocated - * data structures. Such associations are left until - * uhci_run_isoc(). - * - * Returns 0 for success or negative value for error. - * Sets isocdesc before successful return. - */ -static int uhci_init_isoc(struct usb_device *usb_dev, - unsigned int pipe, - int frame_count, /* bandwidth % = 100 * this / 1024 */ - void *context, - struct usb_isoc_desc **isocdesc) -{ - struct usb_isoc_desc *id; - int i; + if ((unsigned) purb->number_of_packets > 900) + return -EFBIG; + + limits = find_iso_limits (purb, &start_limit, &stop_limit); + queued_size = (stop_limit - start_limit) & 1023; - *isocdesc = NULL; + if (purb->transfer_flags & USB_ISO_ASAP) { + // first iso + if (limits) { + // 10ms setup should be enough //FIXME! + purb->start_frame = (now + 10) & 1023; + } + else { + purb->start_frame = stop_limit; //seamless linkage - /* Check some parameters. */ - if ((frame_count <= 0) || (frame_count > UHCI_NUMFRAMES)) { -#ifdef CONFIG_USB_DEBUG_ISOC - printk (KERN_DEBUG "uhci_init_isoc: invalid frame_count (%d)\n", - frame_count); + if (((now - purb->start_frame) & 1023) <= (unsigned) purb->number_of_packets) { + printk (KERN_DEBUG MODSTR "iso_find_start: warning, ASAP gap, should not happen\n"); + printk (KERN_DEBUG MODSTR "iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x\n", + now, purb->start_frame, purb->number_of_packets, purb->pipe); + { + puhci_t s = (puhci_t) purb->dev->bus->hcpriv; + struct list_head *p = s->urb_list.next; + purb_t u; + int a = -1, b = -1; + unsigned long flags; + spin_lock_irqsave (&s->urb_list_lock, flags); + + for (; p != &s->urb_list; p = p->next) { + u = list_entry (p, urb_t, urb_list); + if (purb->dev != u->dev) + continue; + printk (KERN_DEBUG MODSTR "urb: pipe 0x%08x status %d start_frame %u number_of_packets %u\n", + u->pipe, u->status, u->start_frame, u->number_of_packets); + if (!usb_pipeisoc (u->pipe)) + continue; + if (a == -1) + a = u->start_frame; + b = (u->start_frame + u->number_of_packets - 1) & 1023; + } + spin_unlock_irqrestore(&s->urb_list_lock, flags); +#if 0 + if (a != -1 && b != -1) { + do { + jnx_show_desc (s->iso_td[a]); + a = (a + 1) & 1023; + } + while (a != b); + } #endif - return -EINVAL; - } + } + purb->start_frame = (now + 5) & 1023; // 5ms setup should be enough //FIXME! + //return -EAGAIN; //FIXME - if (!usb_pipeisoc (pipe)) { -#ifdef CONFIG_USB_DEBUG_ISOC - printk (KERN_DEBUG "uhci_init_isoc: NOT an Isoc. pipe\n"); -#endif - return -EINVAL; + } + } } - - id = kmalloc (sizeof (*id) + - (sizeof (struct isoc_frame_desc) * frame_count), GFP_KERNEL); - if (!id) - return -ENOMEM; - - memset (id, 0, sizeof (*id) + - (sizeof (struct isoc_frame_desc) * frame_count)); - - id->td = kmalloc (sizeof (struct uhci_td) * frame_count, GFP_KERNEL); - if (!id->td) { - kfree (id); - return -ENOMEM; + else { + purb->start_frame &= 1023; + if (((now - purb->start_frame) & 1023) < (unsigned) purb->number_of_packets) { + printk (KERN_DEBUG MODSTR "iso_find_start: now between start_frame and end\n"); + return -EAGAIN; + } } - memset (id->td, 0, sizeof (struct uhci_td) * frame_count); - - for (i = 0; i < frame_count; i++) - INIT_LIST_HEAD(&((struct uhci_td *)(id->td))[i].irq_list); - - id->frame_count = frame_count; - id->frame_size = usb_maxpacket (usb_dev, pipe, usb_pipeout(pipe)); - /* TBD: or make this a parameter to allow for frame_size - that is less than maxpacketsize */ - id->start_frame = -1; - id->end_frame = -1; - id->usb_dev = usb_dev; - id->pipe = pipe; - id->context = context; + /* check if either start_frame or start_frame+number_of_packets-1 lies between start_limit and stop_limit */ + if (limits) + return 0; + if (((purb->start_frame - start_limit) & 1023) < queued_size || + ((purb->start_frame + purb->number_of_packets - 1 - start_limit) & 1023) < queued_size) { + printk (KERN_DEBUG MODSTR "iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u\n", + purb->start_frame, purb->number_of_packets, start_limit, stop_limit); + return -EAGAIN; + } - *isocdesc = id; return 0; -} /* end uhci_init_isoc */ +} +/*-------------------------------------------------------------------*/ +// submits USB interrupt (ie. polling ;-) +// ASAP-flag set implicitely +// if period==0, the the transfer is only done once (usb_scsi need this...) + +_static int uhci_submit_int_urb (purb_t purb) +{ + puhci_t s = (puhci_t) purb->dev->bus->hcpriv; + purb_priv_t purb_priv = purb->hcpriv; + int nint, n, ret; + puhci_desc_t td; + int status, destination; + int now; + int info; + unsigned int pipe = purb->pipe; -/* - * uhci_run_isoc() - * - * Associates data/buffer pointers/lengths and - * driver (caller) callback functions with the - * allocated Isoc. data structures and TDs. - * - * Then inserts the TDs into the USB controller frame list - * for its processing. - * And inserts the callback function into its TD. - * - * pr_isocdesc (previous Isoc. desc.) may be NULL. - * It is used only for chaining one list of TDs onto the - * end of the previous list of TDs. - * - * Returns 0 (success) or error code (negative value). - */ -static int uhci_run_isoc (struct usb_isoc_desc *isocdesc, - struct usb_isoc_desc *pr_isocdesc) -{ - struct uhci_device *dev = usb_to_uhci (isocdesc->usb_dev); - struct uhci *uhci = dev->uhci; - unsigned long destination, status; - struct uhci_td *td; - int ix, cur_frame, pipeinput, frlen; - int cb_frames = 0; - struct isoc_frame_desc *fd; - unsigned char *bufptr; - - if (!isocdesc->callback_fn) { -#ifdef CONFIG_USB_DEBUG_ISOC - printk (KERN_DEBUG "uhci_run_isoc: caller must have a callback function\n"); -#endif - return -EINVAL; - } + //printk("SUBMIT INT\n"); - /* Check buffer size large enough for maxpacketsize * frame_count. */ - if (isocdesc->buf_size < (isocdesc->frame_count * isocdesc->frame_size)) { -#ifdef CONFIG_USB_DEBUG_ISOC - printk (KERN_DEBUG "uhci_init_isoc: buf_size too small (%d < %d)\n", - isocdesc->buf_size, isocdesc->frame_count * isocdesc->frame_size); -#endif + if (purb->interval < 0 || purb->interval >= 256) return -EINVAL; - } - /* Check buffer ptr for Null. */ - if (!isocdesc->data) { -#ifdef CONFIG_USB_DEBUG_ISOC - printk (KERN_DEBUG "uhci_init_isoc: data ptr is null\n"); -#endif - return -EINVAL; + if (purb->interval == 0) + nint = 0; + else { + for (nint = 0, n = 1; nint <= 8; nint++, n += n) // round interval down to 2^n + { + if (purb->interval < n) { + purb->interval = n / 2; + break; + } + } + nint--; } + dbg(KERN_INFO "Rounded interval to %i, chain %i\n", purb->interval, nint); -#ifdef NEED_ALIGNMENT - /* Check data page alignment. */ - if (((int)(isocdesc->data) & (PAGE_SIZE - 1)) != 0) { -#ifdef CONFIG_USB_DEBUG_ISOC - printk (KERN_DEBUG "uhci_init_isoc: buffer must be page-aligned (%p)\n", - isocdesc->data); -#endif - return -EINVAL; - } -#endif /* NEED_ALIGNMENT */ + now = UHCI_GET_CURRENT_FRAME (s) & 1023; + purb->start_frame = now; // remember start frame, just in case... - /* - * Check start_type unless pr_isocdesc is used. - */ - if (!pr_isocdesc && (isocdesc->start_type > START_TYPE_MAX)) { -#ifdef CONFIG_USB_DEBUG_ISOC - printk (KERN_DEBUG "uhci_run_isoc: invalid start_type (%d)\n", - isocdesc->start_type); -#endif - return -EINVAL; - } + purb->number_of_packets = 1; - /* - * Check start_frame for inside a valid range. - * Only allow transfer requests to be made 1.000 second - * into the future. - */ - cur_frame = uhci_get_current_frame_number (isocdesc->usb_dev); + // INT allows only one packet + if (purb->transfer_buffer_length > usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe))) + return -EINVAL; - /* if not START_ASAP (i.e., RELATIVE or ABSOLUTE): */ - if (!pr_isocdesc) { - if (isocdesc->start_type == START_RELATIVE) { - if ((isocdesc->start_frame < 0) || (isocdesc->start_frame > CAN_SCHEDULE_FRAMES)) { -#ifdef CONFIG_USB_DEBUG_ISOC - printk (KERN_DEBUG "uhci_init_isoc: bad start_frame value (%d)\n", - isocdesc->start_frame); -#endif - return -EINVAL; - } - } /* end START_RELATIVE */ - else - if (isocdesc->start_type == START_ABSOLUTE) { /* within the scope of cur_frame */ - ix = USB_WRAP_FRAMENR(isocdesc->start_frame - cur_frame); - if (ix < START_FRAME_FUDGE || /* too small */ - ix > CAN_SCHEDULE_FRAMES) { /* too large */ -#ifdef CONFIG_USB_DEBUG_ISOC - printk (KERN_DEBUG "uhci_init_isoc: bad start_frame value (%d,%d)\n", - isocdesc->start_frame, cur_frame); -#endif - return -EINVAL; - } - } /* end START_ABSOLUTE */ - } /* end not pr_isocdesc */ + ret = alloc_td (&td, UHCI_PTR_DEPTH); - /* - * Set the start/end frame numbers. - */ - if (pr_isocdesc) { - isocdesc->start_frame = pr_isocdesc->end_frame + 1; - } else if (isocdesc->start_type == START_RELATIVE) { - if (isocdesc->start_frame < START_FRAME_FUDGE) - isocdesc->start_frame = START_FRAME_FUDGE; - isocdesc->start_frame += cur_frame; - } else if (isocdesc->start_type == START_ASAP) { - isocdesc->start_frame = cur_frame + START_FRAME_FUDGE; - } - - /* and see if start_frame needs any correction */ - /* only wrap to USB frame numbers, the frame_list insertion routine - takes care of the wrapping to the frame_list size */ - isocdesc->start_frame = USB_WRAP_FRAMENR(isocdesc->start_frame); - - /* and fix the end_frame value */ - isocdesc->end_frame = USB_WRAP_FRAMENR(isocdesc->start_frame + isocdesc->frame_count - 1); - - isocdesc->prev_completed_frame = -1; - isocdesc->cur_completed_frame = -1; - - destination = (isocdesc->pipe & PIPE_DEVEP_MASK) | - usb_packetid (isocdesc->pipe); - status = TD_CTRL_ACTIVE | TD_CTRL_IOS; /* mark Isoc.; can't be low speed */ - pipeinput = usb_pipein (isocdesc->pipe); - cur_frame = isocdesc->start_frame; - bufptr = isocdesc->data; - - /* - * Build the Data TDs. - * TBD: Not using frame_spacing (Yet). Defaults to 1 (every frame). - * (frame_spacing is a way to request less bandwidth.) - * This can also be done by using frame_length = 0 in the - * frame_desc array, but this way won't take less bandwidth - * allocation into account. - */ - if (isocdesc->frame_spacing <= 0) - isocdesc->frame_spacing = 1; + if (ret) + return -ENOMEM; - for (ix = 0, td = isocdesc->td, fd = isocdesc->frames; - ix < isocdesc->frame_count; ix++, td++, fd++) { - frlen = fd->frame_length; - if (frlen > isocdesc->frame_size) - frlen = isocdesc->frame_size; - -#ifdef NOTDEF - td->info = destination | /* use Actual len on OUT; max. on IN */ - (pipeinput ? ((isocdesc->frame_size - 1) << 21) - : ((frlen - 1) << 21)); -#endif - - td->dev_id = isocdesc; /* can get dev_id or context from isocdesc */ - td->status = status; - td->info = destination | ((frlen - 1) << 21); - td->buffer = virt_to_bus (bufptr); - td->dev = dev; - td->pipetype = PIPE_ISOCHRONOUS; - td->isoc_td_number = ix; /* 0-based; does not wrap/overflow back to 0 */ - - if (isocdesc->callback_frames && - (++cb_frames >= isocdesc->callback_frames)) { - td->status |= TD_CTRL_IOC; - td->completed = isocdesc->callback_fn; - cb_frames = 0; - uhci_add_irq_list (dev->uhci, td, isocdesc->callback_fn, isocdesc); - } + status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | + (purb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (1 << 27); - bufptr += fd->frame_length; /* or isocdesc->frame_size; */ + destination = (purb->pipe & PIPE_DEVEP_MASK) | usb_packetid (purb->pipe) | + (((purb->transfer_buffer_length - 1) & 0x7ff) << 21); - /* - * Insert the TD in the frame list. - */ - uhci_add_frame_list(uhci, td, cur_frame); - cur_frame = USB_WRAP_FRAMENR(cur_frame+1); - } /* end for ix */ + info = destination | (usb_gettoggle (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE); - /* - * Add IOC on the last TD. - */ - td--; - if (!(td->status & TD_CTRL_IOC)) { - td->status |= TD_CTRL_IOC; - td->completed = isocdesc->callback_fn; - uhci_add_irq_list(dev->uhci, td, isocdesc->callback_fn, isocdesc); /* TBD: D.K. ??? */ - } + fill_td (td, status, info, virt_to_bus (purb->transfer_buffer)); + list_add_tail (&td->desc_list, &purb_priv->desc_list); + insert_td_horizontal (s, s->int_chain[nint], td, UHCI_PTR_DEPTH); // store in INT-TDs - return 0; -} /* end uhci_run_isoc */ + usb_dotoggle (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); -/* - * uhci_kill_isoc() - * - * Marks a TD list as Inactive and removes it from the Isoc. - * TD frame list. - * - * Does not free any memory resources. - * - * Returns 0 for success or negative value for error. - */ -static int uhci_kill_isoc (struct usb_isoc_desc *isocdesc) -{ - struct uhci_device *dev = usb_to_uhci (isocdesc->usb_dev); - struct uhci *uhci = dev->uhci; - struct uhci_td *td; - int ix; - - if (USB_WRAP_FRAMENR(isocdesc->start_frame) != isocdesc->start_frame) { -#ifdef CONFIG_USB_DEBUG_ISOC - printk (KERN_DEBUG "uhci_kill_isoc: invalid start_frame (%d)\n", - isocdesc->start_frame); +#if 0 + td = tdm[purb->number_of_packets]; + fill_td (td, TD_CTRL_IOC, 0, 0); + insert_td_horizontal (s, s->iso_td[(purb->start_frame + (purb->number_of_packets) * purb->interval + 1) & 1023], td, UHCI_PTR_DEPTH); + list_add_tail (&td->desc_list, &purb_priv->desc_list); #endif - return -EINVAL; - } - - for (ix = 0, td = isocdesc->td; ix < isocdesc->frame_count; ix++, td++) { - /* Deactivate and unlink */ - uhci_remove_frame_list(uhci, td); - td->status &= ~(TD_CTRL_ACTIVE | TD_CTRL_IOC); - } /* end for ix */ - isocdesc->start_frame = -1; return 0; -} /* end uhci_kill_isoc */ - -static void uhci_free_isoc(struct usb_isoc_desc *isocdesc) -{ - int i; - - /* If still Active, kill it. */ - if (isocdesc->start_frame >= 0) - uhci_kill_isoc(isocdesc); - - /* Remove all TD's from the IRQ list. */ - for (i = 0; i < isocdesc->frame_count; i++) - uhci_remove_irq_list(((struct uhci_td *)isocdesc->td) + i); - - /* Free the associate memory. */ - if (isocdesc->td) - kfree(isocdesc->td); - - kfree(isocdesc); -} /* end uhci_free_isoc */ - -/* - * Control thread operations: we just mark the last TD - * in a control thread as an interrupt TD, and wake up - * the front-end on completion. - * - * We need to remove the TD from the lists (both interrupt - * list and TD lists) by hand if something bad happens! - */ - -static int uhci_generic_completed(int status, void *buffer, int len, void *dev_id) +} +/*-------------------------------------------------------------------*/ +_static int uhci_submit_iso_urb (purb_t purb) { - wait_queue_head_t *wakeup = (wait_queue_head_t *)dev_id; + puhci_t s = (puhci_t) purb->dev->bus->hcpriv; + purb_priv_t purb_priv = purb->hcpriv; + int n, ret; + puhci_desc_t td, *tdm; + int status, destination; + unsigned long flags; + spinlock_t lock; - if (waitqueue_active(wakeup)) - wake_up(wakeup); - else - printk("waitqueue empty!\n"); + spin_lock_init (&lock); + spin_lock_irqsave (&lock, flags); // Disable IRQs to schedule all ISO-TDs in time - return 0; /* Don't re-instate */ -} + ret = iso_find_start (purb); // adjusts purb->start_frame for later use -/* td points to the last td in the list, which interrupts on completion */ -static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last, int timeout) -{ - DECLARE_WAITQUEUE(wait, current); - struct uhci_qh *qh = uhci_qh_alloc(dev); - unsigned long rval; - int ret; + if (ret) + goto err; - if (!qh) - return USB_ST_INTERNALERROR; + tdm = (puhci_desc_t *) kmalloc (purb->number_of_packets * sizeof (puhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL); - current->state = TASK_UNINTERRUPTIBLE; - add_wait_queue(&qh->wakeup, &wait); + if (!tdm) { + ret = -ENOMEM; + goto err; + } - uhci_add_irq_list(dev->uhci, last, uhci_generic_completed, &qh->wakeup); - - uhci_insert_tds_in_qh(qh, first, last); + // First try to get all TDs + for (n = 0; n < purb->number_of_packets; n++) { + dbg (KERN_DEBUG MODSTR "n:%d purb->iso_frame_desc[n].length:%d\n", n, purb->iso_frame_desc[n].length); + if (!purb->iso_frame_desc[n].length) { + // allows ISO striping by setting length to zero in iso_descriptor + tdm[n] = 0; + continue; + } + ret = alloc_td (&td, UHCI_PTR_DEPTH); + if (ret) { + int i; // Cleanup allocated TDs + + for (i = 0; i < n; n++) + if (tdm[i]) + kfree (tdm[i]); + kfree (tdm); + ret = -ENOMEM; + goto err; + } + tdm[n] = td; + } - /* Add it into the skeleton */ - uhci_insert_qh(&dev->uhci->skel_control_qh, qh); + status = TD_CTRL_ACTIVE | TD_CTRL_IOS; //| (purb->transfer_flags&USB_DISABLE_SPD?0:TD_CTRL_SPD); - /* wait a user specified reasonable amount of time */ - schedule_timeout(timeout); + destination = (purb->pipe & PIPE_DEVEP_MASK) | usb_packetid (purb->pipe); - remove_wait_queue(&qh->wakeup, &wait); + // Queue all allocated TDs + for (n = 0; n < purb->number_of_packets; n++) { + td = tdm[n]; + if (!td) + continue; + if (n + 1 >= purb->number_of_packets) + status |= TD_CTRL_IOC; - /* Clean up in case it failed.. */ - uhci_remove_irq_list(last); + fill_td (td, status, destination | (((purb->iso_frame_desc[n].length - 1) & 0x7ff) << 21), + virt_to_bus (purb->transfer_buffer + purb->iso_frame_desc[n].offset)); + list_add_tail (&td->desc_list, &purb_priv->desc_list); + insert_td_horizontal (s, s->iso_td[(purb->start_frame + n) & 1023], td, UHCI_PTR_DEPTH); // store in iso-tds + //uhci_show_td(td); - /* Remove it from the skeleton */ - uhci_remove_qh(&dev->uhci->skel_control_qh, qh); + } - /* Need to check result before free'ing the qh */ - ret = uhci_td_result(dev, last, &rval); + kfree (tdm); + dbg ("ISO-INT# %i, start %i, now %i\n", purb->number_of_packets, purb->start_frame, UHCI_GET_CURRENT_FRAME (s) & 1023); + ret = 0; - uhci_qh_free(qh); + err: + spin_unlock_irqrestore (&lock, flags); + return ret; - return (ret < 0) ? ret : rval; } - -/* - * Send or receive a control message on a pipe. - * - * Note that the "pipe" structure is set up to map - * easily to the uhci destination fields. - * - * A control message is built up from three parts: - * - The command itself - * - [ optional ] data phase - * - Status complete phase - * - * The data phase can be an arbitrary number of TD's - * although we currently had better not have more than - * 29 TD's here (we have 31 TD's allocated for control - * operations, and two of them are used for command and - * status). - * - * 29 TD's is a minimum of 232 bytes worth of control - * information, that's just ridiculously high. Most - * control messages have just a few bytes of data. - * - * 232 is not ridiculously high with many of the - * configurations on audio devices, etc. anyway, - * there is no restriction on length of transfers - * anymore - */ -static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, - void *data, int len, int timeout) +/*-------------------------------------------------------------------*/ +_static int search_dev_ep (puhci_t s, purb_t purb) { - struct uhci_device *dev = usb_to_uhci(usb_dev); - struct uhci_td *first, *td, *prevtd; - unsigned long destination, status; - int ret, count; - int maxsze = usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)); - __u32 nextlink; - unsigned long bytesrequested = len; - - first = td = uhci_td_alloc(dev); - if (!td) - return USB_ST_INTERNALERROR; - - /* The "pipe" thing contains the destination in bits 8--18 */ - destination = (pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; - - /* 3 errors */ - status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_SPD | (3 << 27); + unsigned long flags; + struct list_head *p = s->urb_list.next; + purb_t tmp; - /* - * Build the TD for the control request - */ - td->status = status; /* Try forever */ - td->info = destination | (7 << 21); /* 8 bytes of data */ - td->buffer = virt_to_bus(cmd); - td->pipetype = PIPE_CONTROL; + dbg (KERN_DEBUG MODSTR "search_dev_ep:\n"); + spin_lock_irqsave (&s->urb_list_lock, flags); - /* - * If direction is "send", change the frame from SETUP (0x2D) - * to OUT (0xE1). Else change it from SETUP to IN (0x69). - */ - destination ^= (USB_PID_SETUP ^ USB_PID_IN); /* SETUP -> IN */ - if (usb_pipeout(pipe)) - destination ^= (USB_PID_IN ^ USB_PID_OUT); /* IN -> OUT */ + for (; p != &s->urb_list; p = p->next) { + tmp = list_entry (p, urb_t, urb_list); + dbg (KERN_DEBUG MODSTR "urb: %p\n", tmp); + // we can accept this urb if it is not queued at this time + // or if non-iso transfer requests should be scheduled for the same device and pipe + if ((usb_pipetype (purb->pipe) != PIPE_ISOCHRONOUS && + tmp->dev == purb->dev && tmp->pipe == purb->pipe) || (purb == tmp)) + return 1; // found another urb already queued for processing - prevtd = td; - td = uhci_td_alloc(dev); - if (!td) { - uhci_td_free(prevtd); - return USB_ST_INTERNALERROR; } - prevtd->link = virt_to_bus(td) | UHCI_PTR_DEPTH; - - /* - * Build the DATA TD's - */ - while (len > 0) { - /* Build the TD for control status */ - int pktsze = len; + spin_unlock_irqrestore (&s->urb_list_lock, flags); - if (pktsze > maxsze) - pktsze = maxsze; + return 0; +} +/*-------------------------------------------------------------------*/ +_static int uhci_submit_urb (purb_t purb) +{ + puhci_t s; + purb_priv_t purb_priv; + int ret = 0; - /* Alternate Data0/1 (start with Data1) */ - destination ^= 1 << TD_TOKEN_TOGGLE; - - td->status = status; /* Status */ - td->info = destination | ((pktsze - 1) << 21); /* pktsze bytes of data */ - td->buffer = virt_to_bus(data); - td->backptr = &prevtd->link; - td->pipetype = PIPE_CONTROL; + if (!purb->dev || !purb->dev->bus) + return -ENODEV; - data += pktsze; - len -= pktsze; + s = (puhci_t) purb->dev->bus->hcpriv; + //printk( MODSTR"submit_urb: %p type %d\n",purb,usb_pipetype(purb->pipe)); - prevtd = td; - td = uhci_td_alloc(dev); - if (!td) - return USB_ST_INTERNALERROR; - prevtd->link = virt_to_bus(td) | UHCI_PTR_DEPTH; /* Update previous TD */ - } + if (usb_pipedevice (purb->pipe) == s->rh.devnum) + return rh_submit_urb (purb); /* virtual root hub */ - /* - * Build the final TD for control status - * - * It's IN if the pipe is an output pipe or we're not expecting - * data back. - */ - destination &= ~0xFF; - if (usb_pipeout(pipe) || !bytesrequested) - destination |= USB_PID_IN; - else - destination |= USB_PID_OUT; + usb_inc_dev_use (purb->dev); - destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */ + if (search_dev_ep (s, purb)) { + usb_dec_dev_use (purb->dev); + return -ENXIO; // no such address - td->status = status | TD_CTRL_IOC; /* no limit on errors on final packet */ - td->info = destination | (UHCI_NULL_DATA_SIZE << 21); /* 0 bytes of data */ - td->buffer = 0; - td->backptr = &prevtd->link; - td->link = UHCI_PTR_TERM; /* Terminate */ - td->pipetype = PIPE_CONTROL; - - /* Start it up.. */ - ret = uhci_run_control(dev, first, td, timeout); - - count = 1000; - td = first; - do { - nextlink = td->link; - uhci_remove_td(td); - uhci_td_free(td); + } - if (nextlink & UHCI_PTR_TERM) /* Tail? */ - break; +#ifdef _UHCI_SLAB + purb_priv = kmem_cache_alloc(urb_priv_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL); +#else + purb_priv = kmalloc (sizeof (urb_priv_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL); +#endif + if (!purb_priv) { + usb_dec_dev_use (purb->dev); + return -ENOMEM; + } - td = uhci_ptr_to_virt(nextlink); - } while (--count); + purb->hcpriv = purb_priv; + INIT_LIST_HEAD (&purb_priv->desc_list); + purb_priv->short_control_packet=0; + dbg (KERN_DEBUG MODSTR "submit_urb: scheduling %p\n", purb); + + switch (usb_pipetype (purb->pipe)) { + case PIPE_ISOCHRONOUS: + ret = uhci_submit_iso_urb (purb); + break; + case PIPE_INTERRUPT: + ret = uhci_submit_int_urb (purb); + break; + case PIPE_CONTROL: + //dump_urb (purb); + ret = uhci_submit_control_urb (purb); + break; + case PIPE_BULK: + ret = uhci_submit_bulk_urb (purb); + break; + default: + ret = -EINVAL; + } - if (!count) - printk(KERN_ERR "runaway td's!?\n"); + dbg (KERN_DEBUG MODSTR "submit_urb: scheduled with ret: %d\n", ret); -#ifdef UHCI_DEBUG - if (ret >= 0 && ret != bytesrequested && bytesrequested) - printk("requested %ld bytes, got %d\n", bytesrequested, ret); + if (ret != USB_ST_NOERROR) { + usb_dec_dev_use (purb->dev); +#ifdef _UHCI_SLAB + kmem_cache_free(urb_priv_kmem, purb_priv); +#else + kfree (purb_priv); #endif + return ret; + } - return ret; + purb->status = USB_ST_URB_PENDING; + queue_urb (s, &purb->urb_list,1); + dbg (KERN_DEBUG MODSTR "submit_urb: exit\n"); + + return 0; } +/*------------------------------------------------------------------- + Virtual Root Hub + -------------------------------------------------------------------*/ + +_static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, /* __u16 bcdUSB; v1.0 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x00, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; -/* - * Bulk thread operations: we just mark the last TD - * in a bulk thread as an interrupt TD, and wake up - * the front-end on completion. - * - * We need to remove the TD from the lists (both interrupt - * list and TD lists) by hand if something bad happens! - */ -/* td points to the last td in the list, which interrupts on completion */ -static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last, unsigned long *rval, int timeout) +/* Configuration descriptor */ +_static __u8 root_hub_config_des[] = { - DECLARE_WAITQUEUE(wait, current); - struct uhci_qh *qh = uhci_qh_alloc(dev); - int ret; - - if (!qh) - return USB_ST_INTERNALERROR; - - current->state = TASK_UNINTERRUPTIBLE; - add_wait_queue(&qh->wakeup, &wait); - - uhci_add_irq_list(dev->uhci, last, uhci_generic_completed, &qh->wakeup); - - uhci_insert_tds_in_qh(qh, first, last); + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; - /* Add it into the skeleton */ - uhci_insert_qh(&dev->uhci->skel_bulk_qh, qh); - /* wait a user specified reasonable amount of time */ - schedule_timeout(timeout); +_static __u8 root_hub_hub_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x02, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; - remove_wait_queue(&qh->wakeup, &wait); +/*-------------------------------------------------------------------------*/ +/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ +_static int rh_send_irq (purb_t purb) +{ - /* Clean up in case it failed.. */ - uhci_remove_irq_list(last); + int len = 1; + int i; + puhci_t uhci = purb->dev->bus->hcpriv; + unsigned int io_addr = uhci->io_addr; + __u16 data = 0; - uhci_remove_qh(&dev->uhci->skel_bulk_qh, qh); + for (i = 0; i < uhci->rh.numports; i++) { + data |= ((inw (io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); + len = (i + 1) / 8 + 1; + } - ret = uhci_td_result(dev, last, rval); + *(__u16 *) purb->transfer_buffer = cpu_to_le16 (data); + purb->actual_length = len; + purb->status = USB_ST_NOERROR; - uhci_qh_free(qh); + if ((data > 0) && (uhci->rh.send != 0)) { + dbg (KERN_DEBUG MODSTR "Root-Hub INT complete: port1: %x port2: %x data: %x\n", + inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data); + purb->complete (purb); - return ret; + } + return USB_ST_NOERROR; } -/* - * Send or receive a bulk message on a pipe. - * - * Note that the "pipe" structure is set up to map - * easily to the uhci destination fields. - * - * A bulk message is only built up from - * the data phase - */ -static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *rval, int timeout) -{ - struct uhci_device *dev = usb_to_uhci(usb_dev); - struct uhci_td *first, *td, *prevtd, *curtd; - unsigned long destination, status; - unsigned int nextlink; - int ret, count; - int maxsze = usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)); - - if (usb_endpoint_halted(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) && - usb_clear_halt(usb_dev, usb_pipeendpoint(pipe) | (pipe & USB_DIR_IN))) - return USB_ST_STALL; - - /* The "pipe" thing contains the destination in bits 8--18. */ - destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); - - /* 3 errors */ - status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_SPD | (3 << 27); - - /* - * Build the TDs for the bulk request - */ - first = td = uhci_td_alloc(dev); - if (!td) - return USB_ST_INTERNALERROR; - - prevtd = first; // This is fake, but at least it's not NULL - while (len > 0) { - /* Build the TD for control status */ - int pktsze = len; - - if (pktsze > maxsze) - pktsze = maxsze; +/*-------------------------------------------------------------------------*/ +/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */ +_static int rh_init_int_timer (purb_t purb); - td->status = status; /* Status */ - td->info = destination | ((pktsze-1) << 21) | - (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), - usb_pipeout(pipe)) << TD_TOKEN_TOGGLE); /* pktsze bytes of data */ - td->buffer = virt_to_bus(data); - td->backptr = &prevtd->link; - td->pipetype = PIPE_BULK; +_static void rh_int_timer_do (unsigned long ptr) +{ + int len; - data += maxsze; - len -= maxsze; + purb_t purb = (purb_t) ptr; + puhci_t uhci = purb->dev->bus->hcpriv; + if (uhci->rh.send) { + len = rh_send_irq (purb); if (len > 0) { - prevtd = td; - td = uhci_td_alloc(dev); - if (!td) - return USB_ST_INTERNALERROR; - - prevtd->link = virt_to_bus(td) | UHCI_PTR_DEPTH;/* Update previous TD */ + purb->actual_length = len; + if (purb->complete) + purb->complete (purb); } - - /* Alternate Data0/1 (start with Data0) */ - usb_dotoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); } + rh_init_int_timer (purb); +} - td->link = UHCI_PTR_TERM; /* Terminate */ - td->status |= TD_CTRL_IOC; - - /* CHANGE DIRECTION HERE! SAVE IT SOMEWHERE IN THE ENDPOINT!!! */ - - /* Start it up.. */ - ret = uhci_run_bulk(dev, first, td, rval, timeout); - - count = 1000; - curtd = first; - - do { - nextlink = curtd->link; - uhci_remove_td(curtd); - uhci_td_free(curtd); - - if (nextlink & UHCI_PTR_TERM) /* Tail? */ - break; - - curtd = uhci_ptr_to_virt(nextlink); - } while (--count); +/*-------------------------------------------------------------------------*/ +/* Root Hub INTs are polled by this timer */ +_static int rh_init_int_timer (purb_t purb) +{ + puhci_t uhci = purb->dev->bus->hcpriv; - if (!count) - printk(KERN_DEBUG "uhci: runaway td's!?\n"); + uhci->rh.interval = purb->interval; + init_timer (&uhci->rh.rh_int_timer); + uhci->rh.rh_int_timer.function = rh_int_timer_do; + uhci->rh.rh_int_timer.data = (unsigned long) purb; + uhci->rh.rh_int_timer.expires = jiffies + (HZ * (purb->interval < 30 ? 30 : purb->interval)) / 1000; + add_timer (&uhci->rh.rh_int_timer); - return ret < 0; + return 0; } -static void *uhci_request_bulk(struct usb_device *usb_dev, unsigned int pipe, - usb_device_irq handler, void *data, int len, void *dev_id) -{ - struct uhci_device *dev = usb_to_uhci(usb_dev); - struct uhci *uhci = dev->uhci; - struct uhci_td *first, *td, *prevtd; - struct uhci_qh *bulk_qh = uhci_qh_alloc(dev); - unsigned long destination, status; - int maxsze = usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)); +/*-------------------------------------------------------------------------*/ +#define OK(x) len = (x); break - /* The "pipe" thing contains the destination in bits 8--18. */ - destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid(pipe); +#define CLR_RH_PORTSTAT(x) \ + status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \ + status = (status & 0xfff5) & ~(x); \ + outw(status, io_addr+USBPORTSC1+2*(wIndex-1)) + +#define SET_RH_PORTSTAT(x) \ + status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \ + status = (status & 0xfff5) | (x); \ + outw(status, io_addr+USBPORTSC1+2*(wIndex-1)) + + +/*-------------------------------------------------------------------------*/ +/**** + ** Root Hub Control Pipe + *************************/ + + +_static int rh_submit_urb (purb_t purb) +{ + struct usb_device *usb_dev = purb->dev; + puhci_t uhci = usb_dev->bus->hcpriv; + unsigned int pipe = purb->pipe; + devrequest *cmd = (devrequest *) purb->setup_packet; + void *data = purb->transfer_buffer; + int leni = purb->transfer_buffer_length; + int len = 0; + int status = 0; + int stat = USB_ST_NOERROR; + int i; + unsigned int io_addr = uhci->io_addr; + __u16 cstatus; - /* Infinite errors is 0 */ - status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_SPD; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + if (usb_pipetype (pipe) == PIPE_INTERRUPT) { + dbg (KERN_DEBUG MODSTR "Root-Hub submit IRQ: every %d ms\n", purb->interval); + uhci->rh.urb = purb; + uhci->rh.send = 1; + uhci->rh.interval = purb->interval; + rh_init_int_timer (purb); - /* Build the TDs for the bulk request */ - first = td = uhci_td_alloc(dev); - prevtd = td; - while (len > 0) { - /* Build the TD for control status */ - int pktsze = len; + return USB_ST_NOERROR; + } - if (pktsze > maxsze) - pktsze = maxsze; - td->status = status; /* Status */ - td->info = destination | ((pktsze-1) << 21) | - (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << TD_TOKEN_TOGGLE); /* pktsze bytes of data */ - td->buffer = virt_to_bus(data); - td->backptr = &prevtd->link; - td->qh = bulk_qh; - td->dev = dev; - td->pipetype = PIPE_BULK; + bmRType_bReq = cmd->requesttype | cmd->request << 8; + wValue = le16_to_cpu (cmd->value); + wIndex = le16_to_cpu (cmd->index); + wLength = le16_to_cpu (cmd->length); + + for (i = 0; i < 8; i++) + uhci->rh.c_p_r[i] = 0; + + dbg (KERN_DEBUG MODSTR "Root-Hub: adr: %2x cmd(%1x): %04 %04 %04 %04\n", + uhci->rh.devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ - data += pktsze; - len -= pktsze; + case RH_GET_STATUS: + *(__u16 *) data = cpu_to_le16 (1); + OK (2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data = cpu_to_le16 (0); + OK (2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data = cpu_to_le16 (0); + OK (2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data = cpu_to_le32 (0); + OK (4); /* hub power ** */ + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + status = inw (io_addr + USBPORTSC1 + 2 * (wIndex - 1)); + cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | + ((status & USBPORTSC_PEC) >> (3 - 1)) | + (uhci->rh.c_p_r[wIndex - 1] << (0 + 4)); + status = (status & USBPORTSC_CCS) | + ((status & USBPORTSC_PE) >> (2 - 1)) | + ((status & USBPORTSC_SUSP) >> (12 - 2)) | + ((status & USBPORTSC_PR) >> (9 - 4)) | + (1 << 8) | /* power on ** */ + ((status & USBPORTSC_LSDA) << (-8 + 9)); + + *(__u16 *) data = cpu_to_le16 (status); + *(__u16 *) (data + 2) = cpu_to_le16 (cstatus); + OK (4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): + OK (0); + } + break; - if (len > 0) { - prevtd = td; - td = uhci_td_alloc(dev); - prevtd->link = virt_to_bus(td) | UHCI_PTR_DEPTH; + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case (RH_C_HUB_OVER_CURRENT): + OK (0); /* hub power over current ** */ } + break; - /* Alternate Data0/1 */ - usb_dotoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); - } + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + CLR_RH_PORTSTAT (USBPORTSC_PE); + OK (0); + case (RH_PORT_SUSPEND): + CLR_RH_PORTSTAT (USBPORTSC_SUSP); + OK (0); + case (RH_PORT_POWER): + OK (0); /* port power ** */ + case (RH_C_PORT_CONNECTION): + SET_RH_PORTSTAT (USBPORTSC_CSC); + OK (0); + case (RH_C_PORT_ENABLE): + SET_RH_PORTSTAT (USBPORTSC_PEC); + OK (0); + case (RH_C_PORT_SUSPEND): +/*** WR_RH_PORTSTAT(RH_PS_PSSC); */ + OK (0); + case (RH_C_PORT_OVER_CURRENT): + OK (0); /* port power over current ** */ + case (RH_C_PORT_RESET): + uhci->rh.c_p_r[wIndex - 1] = 0; + OK (0); + } + break; - first->backptr = NULL; - td->link = 1; /* Terminate */ - td->status = status | TD_CTRL_IOC; /* IOC */ + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + SET_RH_PORTSTAT (USBPORTSC_SUSP); + OK (0); + case (RH_PORT_RESET): + SET_RH_PORTSTAT (USBPORTSC_PR); + wait_ms (10); + uhci->rh.c_p_r[wIndex - 1] = 1; + CLR_RH_PORTSTAT (USBPORTSC_PR); + udelay (10); + SET_RH_PORTSTAT (USBPORTSC_PE); + wait_ms (10); + SET_RH_PORTSTAT (0xa); + OK (0); + case (RH_PORT_POWER): + OK (0); /* port power ** */ + case (RH_PORT_ENABLE): + SET_RH_PORTSTAT (USBPORTSC_PE); + OK (0); + } + break; - uhci_add_irq_list(dev->uhci, td, handler, dev_id); + case RH_SET_ADDRESS: + uhci->rh.devnum = wValue; + OK (0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min (leni, min (sizeof (root_hub_dev_des), wLength)); + memcpy (data, root_hub_dev_des, len); + OK (len); + case (0x02): /* configuration descriptor */ + len = min (leni, min (sizeof (root_hub_config_des), wLength)); + memcpy (data, root_hub_config_des, len); + OK (len); + case (0x03): /*string descriptors */ + stat = -EPIPE; + } + break; - uhci_insert_tds_in_qh(bulk_qh, first, td); + case RH_GET_DESCRIPTOR | RH_CLASS: + root_hub_hub_des[2] = uhci->rh.numports; + len = min (leni, min (sizeof (root_hub_hub_des), wLength)); + memcpy (data, root_hub_hub_des, len); + OK (len); + + case RH_GET_CONFIGURATION: + *(__u8 *) data = 0x01; + OK (1); + + case RH_SET_CONFIGURATION: + OK (0); + default: + stat = -EPIPE; + } - bulk_qh->skel = &uhci->skel_bulk_qh; - uhci_insert_qh(&uhci->skel_bulk_qh, bulk_qh); - /* Return last td for removal */ - return td; + dbg (KERN_DEBUG MODSTR "Root-Hub stat port1: %x port2: %x \n", + inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2)); + + purb->actual_length = len; + purb->status = stat; + if (purb->complete) + purb->complete (purb); + return USB_ST_NOERROR; } +/*-------------------------------------------------------------------------*/ -/* - * Remove a handler from a pipe. This terminates the transfer. - * We have some assumptions here: - * There is only one queue using this pipe. (the one we remove) - * Any data that is in the queue is useless for us, we throw it away. - */ -static int uhci_terminate_bulk(struct usb_device *dev, void *first) +_static int rh_unlink_urb (purb_t purb) { - /* none found? there is nothing to remove! */ - if (!first) - return USB_ST_REMOVED; + puhci_t uhci = purb->dev->bus->hcpriv; - uhci_remove_transfer(first, 1); - - return USB_ST_NOERROR; + dbg (KERN_DEBUG MODSTR "Root-Hub unlink IRQ\n"); + uhci->rh.send = 0; + del_timer (&uhci->rh.rh_int_timer); + return 0; } +/*-------------------------------------------------------------------*/ -struct usb_operations uhci_device_operations = { - uhci_alloc_dev, - uhci_free_dev, - uhci_control_msg, - uhci_bulk_msg, - uhci_request_irq, - uhci_release_irq, - uhci_request_bulk, - uhci_terminate_bulk, - uhci_get_current_frame_number, - uhci_init_isoc, - uhci_free_isoc, - uhci_run_isoc, - uhci_kill_isoc -}; +#define UHCI_DEBUG /* - * This is just incredibly fragile. The timings must be just - * right, and they aren't really documented very well. + * Map status to standard result codes * - * Note the short delay between disabling reset and enabling - * the port.. - */ -static void uhci_reset_port(unsigned int port) -{ - unsigned short status; - - status = inw(port); - outw(status | USBPORTSC_PR, port); /* reset port */ - wait_ms(10); - outw(status & ~USBPORTSC_PR, port); - udelay(5); - - status = inw(port); - outw(status | USBPORTSC_PE, port); /* enable port */ - wait_ms(10); - - status = inw(port); - if (!(status & USBPORTSC_PE)) { - outw(status | USBPORTSC_PE, port); /* one more try at enabling port */ - wait_ms(50); - } - -} - -/* - * This gets called if the connect status on the root - * hub (and the root hub only) changes. + * is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status) + * is True for output TDs and False for input TDs. */ -static void uhci_connect_change(struct uhci *uhci, unsigned int port, unsigned int nr) +_static int uhci_map_status (int status, int dir_out) { - struct usb_device *usb_dev; - struct uhci_device *dev; - unsigned short status; - struct uhci_device *root_hub = usb_to_uhci(uhci->bus->root_hub); - -#ifdef UHCI_DEBUG - printk(KERN_INFO "uhci_connect_change: called for %d\n", nr); -#endif - - /* - * Even if the status says we're connected, - * the fact that the status bits changed may - * that we got disconnected and then reconnected. - * - * So start off by getting rid of any old devices.. - */ - usb_disconnect(&root_hub->usb->children[nr]); - - status = inw(port); - - /* If we have nothing connected, then clear change status and disable the port */ - status = (status & ~USBPORTSC_PE) | USBPORTSC_PEC; - if (!(status & USBPORTSC_CCS)) { - outw(status, port); - return; + if (!status) + return USB_ST_NOERROR; + if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ + return USB_ST_BITSTUFF; + if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ + if (dir_out) + return USB_ST_NORESPONSE; + else + return USB_ST_CRC; } + if (status & TD_CTRL_NAK) /* NAK */ + return USB_ST_TIMEOUT; + if (status & TD_CTRL_BABBLE) /* Babble */ + return -EPIPE; + if (status & TD_CTRL_DBUFERR) /* Buffer error */ + return USB_ST_BUFFERUNDERRUN; + if (status & TD_CTRL_STALLED) /* Stalled */ + return -EPIPE; + if (status & TD_CTRL_ACTIVE) /* Active */ + return USB_ST_NOERROR; - /* - * Ok, we got a new connection. Allocate a device to it, - * and find out what it wants to do.. - */ - usb_dev = usb_alloc_dev(root_hub->usb, root_hub->usb->bus); - if (!usb_dev) - return; - - dev = usb_dev->hcpriv; - - usb_connect(usb_dev); - - root_hub->usb->children[nr] = usb_dev; - - wait_ms(200); /* wait for powerup */ - uhci_reset_port(port); - - /* Get speed information */ - usb_dev->slow = (inw(port) & USBPORTSC_LSDA) ? 1 : 0; - - /* - * Ok, all the stuff specific to the root hub has been done. - * The rest is generic for any new USB attach, regardless of - * hub type. - */ - if (usb_new_device(usb_dev)) { - unsigned short status = inw(port); - - printk(KERN_INFO "uhci: disabling port %d\n", - nr + 1); - outw(status & ~USBPORTSC_PE, port); - } + return USB_ST_INTERNALERROR; } /* - * This gets called when the root hub configuration - * has changed. Just go through each port, seeing if - * there is something interesting happening. + * Only the USB core should call uhci_alloc_dev and uhci_free_dev */ -static void uhci_check_configuration(struct uhci *uhci) +_static int uhci_alloc_dev (struct usb_device *usb_dev) { - struct uhci_device *root_hub = usb_to_uhci(uhci->bus->root_hub); - unsigned int io_addr = uhci->io_addr + USBPORTSC1; - int maxchild = root_hub->usb->maxchild; - int nr = 0; - - do { - unsigned short status = inw(io_addr); - - if (status & USBPORTSC_CSC) - uhci_connect_change(uhci, io_addr, nr); - - nr++; io_addr += 2; - } while (nr < maxchild); -} - -static int fixup_isoc_desc (struct uhci_td *td) -{ - struct usb_isoc_desc *isocdesc = td->dev_id; - struct uhci_td *prtd; - struct isoc_frame_desc *frm; - int first_comp = isocdesc->cur_completed_frame + 1; /* 0-based */ - int cur_comp = td->isoc_td_number; /* 0-based */ - int ix, fx; - int num_comp; - - if (first_comp >= isocdesc->frame_count) - first_comp = 0; - num_comp = cur_comp - first_comp + 1; - -#ifdef CONFIG_USB_DEBUG_ISOC - printk ("fixup_isoc_desc.1: td = %p, id = %p, first_comp = %d, cur_comp = %d, num_comp = %d\n", - td, isocdesc, first_comp, cur_comp, num_comp); -#endif - - for (ix = 0, fx = first_comp, prtd = ((struct uhci_td *)(isocdesc->td))+first_comp, frm = &isocdesc->frames [first_comp]; - ix < num_comp; ix++) { - frm->frame_length = uhci_actual_length (prtd->status); - isocdesc->total_length += frm->frame_length; - - if ((frm->frame_status = uhci_map_status (uhci_status_bits (prtd->status), - uhci_packetout (prtd->info)))) - isocdesc->error_count++; - - prtd++; - frm++; - if (++fx >= isocdesc->frame_count) { /* wrap fx, prtd, and frm */ - fx = 0; - prtd = isocdesc->td; - frm = isocdesc->frames; - } /* end wrap */ - } /* end for */ - - /* - * Update some other fields for drivers. - */ - isocdesc->prev_completed_frame = isocdesc->cur_completed_frame; - isocdesc->cur_completed_frame = cur_comp; - isocdesc->total_completed_frames += num_comp; /* 1-based */ - -#ifdef CONFIG_USB_DEBUG_ISOC - printk ("fixup_isoc_desc.2: total_comp_frames = %d, total_length = %d, error_count = %d\n", - isocdesc->total_completed_frames, isocdesc->total_length, isocdesc->error_count); -#endif /* CONFIG_USB_DEBUG_ISOC */ + return 0; +} +_static int uhci_free_dev (struct usb_device *usb_dev) +{ return 0; } -static int uhci_isoc_callback(struct uhci *uhci, struct uhci_td *td, int status, unsigned long rval) +/* + * uhci_get_current_frame_number() + * + * returns the current frame number for a USB bus/controller. + */ +_static int uhci_get_current_frame_number (struct usb_device *usb_dev) { - struct usb_isoc_desc *isocdesc = td->dev_id; - int ret; + return UHCI_GET_CURRENT_FRAME ((puhci_t) usb_dev->bus->hcpriv); +} - /* - * Fixup the isocdesc for the driver: total_completed_frames, - * error_count, total_length, frames array. - * - * ret = callback_fn (int error_count, void *buffer, - * int len, void *isocdesc); - */ +struct usb_operations uhci_device_operations = +{ + uhci_alloc_dev, + uhci_free_dev, + uhci_get_current_frame_number, + uhci_submit_urb, + uhci_unlink_urb +}; - fixup_isoc_desc (td); +/* + * For IN-control transfers, process_transfer gets a bit more complicated, + * since there are devices that return less data (eg. strings) than they + * have announced. This leads to a queue abort due to the short packet, + * the status stage is not executed. If this happens, the status stage + * is manually re-executed. + * FIXME: Stall-condition may override 'nearly' successful CTRL-IN-transfer + * when the transfered length fits exactly in maxsze-packets. A bit + * more intelligence is needed to detect this and finish without error. + */ +_static int process_transfer (puhci_t s, purb_t purb) +{ + int ret = USB_ST_NOERROR; + purb_priv_t purb_priv = purb->hcpriv; + struct list_head *qhl = purb_priv->desc_list.next; + puhci_desc_t qh = list_entry (qhl, uhci_desc_t, desc_list); + struct list_head *p = qh->vertical.next; + puhci_desc_t desc= list_entry (purb_priv->desc_list.prev, uhci_desc_t, desc_list); + puhci_desc_t last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical); + int data_toggle = usb_gettoggle (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe)); // save initial data_toggle + + + // extracted and remapped info from TD + int maxlength; + int actual_length; + int status = USB_ST_NOERROR; + + dbg (KERN_DEBUG MODSTR "process_transfer: urb contains bulk/control request\n"); + + + /* if the status phase has been retriggered and the + queue is empty or the last status-TD is inactive, the retriggered + status stage is completed + */ +#if 1 + if (purb_priv->short_control_packet && + ((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE)))) + goto transfer_finished; +#endif + purb->actual_length=0; + + for (; p != &qh->vertical; p = p->next) { + desc = list_entry (p, uhci_desc_t, vertical); + + if (desc->hw.td.status & TD_CTRL_ACTIVE) // do not process active TDs + return ret; + + // extract transfer parameters from TD + actual_length = (desc->hw.td.status + 1) & 0x7ff; + maxlength = (((desc->hw.td.info >> 21) & 0x7ff) + 1) & 0x7ff; + status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (purb->pipe)); + + // see if EP is stalled + if (status == -EPIPE) { + // set up stalled condition + usb_endpoint_halt (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe)); + } - ret = td->completed (isocdesc->error_count, bus_to_virt (td->buffer), - isocdesc->total_length, isocdesc); + // if any error occured stop processing of further TDs + if (status != USB_ST_NOERROR) { + // only set ret if status returned an error + uhci_show_td (desc); + ret = status; + purb->error_count++; + break; + } + else if ((desc->hw.td.info & 0xff) != USB_PID_SETUP) + purb->actual_length += actual_length; - /* - * Isoc. handling of return value from td->completed (callback function) - */ +#if 0 + if (i++==0) + uhci_show_td (desc); // show first TD of each transfer +#endif - switch (ret) { - case CB_CONTINUE: /* similar to the REMOVE condition below */ - /* TBD */ - uhci_td_free (td); - break; + // go into error condition to keep data_toggle unchanged if short packet occurs + if ((purb->transfer_flags & USB_DISABLE_SPD) && (actual_length < maxlength)) { + ret = USB_ST_SHORT_PACKET; + printk (KERN_DEBUG MODSTR "process_transfer: SPD!!\n"); + break; // exit after this TD because SP was detected - case CB_REUSE: /* similar to the re-add condition below, - * but Not ACTIVE */ - /* TBD */ - /* usb_dev = td->dev->usb; */ - - /* Safe since uhci_interrupt_notify holds the lock */ - list_add(&td->irq_list, &uhci->interrupt_list); - - td->status = (td->status & (TD_CTRL_SPD | TD_CTRL_C_ERR_MASK | - TD_CTRL_LS | TD_CTRL_IOS | TD_CTRL_IOC)) | - TD_CTRL_IOC; - - /* The HC removes it, so re-add it */ - /* Insert into a QH? */ - uhci_insert_td_in_qh(td->qh, td); - break; + } - case CB_RESTART: /* similar to re-add, but mark ACTIVE */ - /* TBD */ - /* usb_dev = td->dev->usb; */ - - list_add(&td->irq_list, &uhci->interrupt_list); - - td->status = (td->status & (TD_CTRL_SPD | TD_CTRL_C_ERR_MASK | - TD_CTRL_LS | TD_CTRL_IOS | TD_CTRL_IOC)) | - TD_CTRL_ACTIVE | TD_CTRL_IOC; + data_toggle = uhci_toggle (desc->hw.td.info); + //printk(KERN_DEBUG MODSTR"process_transfer: len:%d status:%x mapped:%x toggle:%d\n", actual_length, desc->hw.td.status,status, data_toggle); +#if 1 + if ((usb_pipetype (purb->pipe) == PIPE_CONTROL) && (actual_length < maxlength)) { + if (uhci_packetid(last_desc->hw.td.info) == USB_PID_OUT) { + uhci_show_td (last_desc); + qh->hw.qh.element = virt_to_bus (last_desc); // re-trigger status stage + printk("uhci: short packet during control transfer, retrigger status stage\n"); + purb_priv->short_control_packet=1; + return 0; + } + } +#endif + } + usb_settoggle (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe), !data_toggle); + transfer_finished: - /* The HC removes it, so re-add it */ - uhci_insert_td_in_qh(td->qh, td); - break; + /* APC BackUPS Pro kludge */ + /* It tries to send all of the descriptor instead of */ + /* the amount we requested */ + if (desc->hw.td.status & TD_CTRL_IOC && + status & TD_CTRL_ACTIVE && + status & TD_CTRL_NAK ) + { + ret=0; + purb->status=0; + } - case CB_ABORT: /* kill/abort */ - /* TBD */ - uhci_kill_isoc (isocdesc); - break; - } /* end isoc. TD switch */ + unlink_qh (s, qh); + delete_qh (s, qh); - return 0; + purb->status = status; + + + dbg(KERN_DEBUG MODSTR"process_transfer: urb %p, wanted len %d, len %d status %x err %d\n", + purb,purb->transfer_buffer_length,purb->actual_length, purb->status, purb->error_count); + //dbg(KERN_DEBUG MODSTR"process_transfer: exit\n"); + return ret; } -static int uhci_bulk_callback(struct uhci *uhci, struct uhci_td *td, int status, unsigned long rval) +_static int process_interrupt (puhci_t s, purb_t purb) { - if (td->completed(status, bus_to_virt(td->buffer), rval, td->dev_id)) { - struct usb_device *usb_dev = td->dev->usb; + int i, ret = USB_ST_URB_PENDING; + purb_priv_t purb_priv = purb->hcpriv; + struct list_head *p = purb_priv->desc_list.next; + puhci_desc_t desc = list_entry (purb_priv->desc_list.prev, uhci_desc_t, desc_list); + int data_toggle = usb_gettoggle (purb->dev, usb_pipeendpoint (purb->pipe), + usb_pipeout (purb->pipe)); // save initial data_toggle + // extracted and remapped info from TD + + int actual_length; + int status = USB_ST_NOERROR; + + //printk(KERN_DEBUG MODSTR"urb contains interrupt request\n"); + + for (i = 0; p != &purb_priv->desc_list; p = p->next, i++) // Maybe we allow more than one TD later ;-) + { + desc = list_entry (p, uhci_desc_t, desc_list); + + if (desc->hw.td.status & TD_CTRL_NAK) { + // NAKed transfer + //printk("TD NAK Status @%p %08x\n",desc,desc->hw.td.status); + goto err; + } - /* This is safe since uhci_interrupt_notify holds the lock */ - list_add(&td->irq_list, &uhci->interrupt_list); + if (desc->hw.td.status & TD_CTRL_ACTIVE) { + // do not process active TDs + //printk("TD ACT Status @%p %08x\n",desc,desc->hw.td.status); + goto err; + } - /* Reset the status */ - td->status = (td->status & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | TD_CTRL_SPD; + if (!desc->hw.td.status & TD_CTRL_IOC) { + // do not process one-shot TDs + goto err; + } + // extract transfer parameters from TD - /* Reset the info */ - td->info = (td->info & (PIPE_DEVEP_MASK | 0xFF | (TD_CTRL_ACTLEN_MASK << 21))) | - (usb_gettoggle(usb_dev, uhci_endpoint(td->info), uhci_packetout(td->info)) << TD_TOKEN_TOGGLE); /* pktsze bytes of data */ + actual_length = (desc->hw.td.status + 1) & 0x7ff; + status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (purb->pipe)); - usb_dotoggle(usb_dev, uhci_endpoint(td->info), uhci_packetout(td->info)); - /* The HC only removes it when it completed */ - /* successfully, so force remove and re-add it */ - uhci_remove_td(td); - uhci_insert_td_in_qh(td->qh, td); - } + // see if EP is stalled + if (status == -EPIPE) { + // set up stalled condition + usb_endpoint_halt (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe)); + } - return 0; -} + // if any error occured: ignore this td, and continue + if (status != USB_ST_NOERROR) { + purb->error_count++; + goto err; + } + else + purb->actual_length = actual_length; -static int uhci_callback(struct uhci *uhci, struct uhci_td *td, int status, unsigned long rval) -{ - if (td->completed(status, bus_to_virt(td->buffer), rval, td->dev_id)) { - struct usb_device *usb_dev = td->dev->usb; + // FIXME: SPD? - if (td->pipetype != PIPE_INTERRUPT) - return 0; + data_toggle = uhci_toggle (desc->hw.td.info); - /* This is safe since uhci_interrupt_notify holds the lock */ - list_add(&td->irq_list, &uhci->interrupt_list); + if (purb->complete && status != USB_ST_TIMEOUT) { + // for last td, no user completion is needed + dbg (KERN_DEBUG MODSTR "process_interrupt: calling completion\n"); + purb->status = status; + purb->complete ((struct urb *) purb); + purb->status = USB_ST_URB_PENDING; + } - usb_dotoggle(usb_dev, uhci_endpoint(td->info), uhci_packetout(td->info)); - td->info &= ~(1 << TD_TOKEN_TOGGLE); /* clear data toggle */ - td->info |= usb_gettoggle(usb_dev, uhci_endpoint(td->info), - uhci_packetout(td->info)) << TD_TOKEN_TOGGLE; /* toggle between data0 and data1 */ - td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC; - /* The HC only removes it when it completed */ - /* successfully, so force remove and re-add it */ - uhci_remove_td(td); - uhci_insert_td_in_qh(td->qh, td); - } else if (td->flags & UHCI_TD_REMOVE) { - struct usb_device *usb_dev = td->dev->usb; + // Recycle INT-TD if interval!=0, else mark TD as one-shot + if (purb->interval) { + desc->hw.td.status |= TD_CTRL_ACTIVE; + desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE); + desc->hw.td.info |= (usb_gettoggle (purb->dev, usb_pipeendpoint (purb->pipe), + usb_pipeout (purb->pipe)) << TD_TOKEN_TOGGLE); + usb_dotoggle (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe)); + } + else { + desc->hw.td.status &= ~TD_CTRL_IOC; + } - /* marked for removal */ - td->flags &= ~UHCI_TD_REMOVE; - usb_dotoggle(usb_dev, uhci_endpoint(td->info), uhci_packetout(td->info)); - uhci_remove_qh(td->qh->skel, td->qh); - uhci_qh_free(td->qh); - if (td->pipetype == PIPE_INTERRUPT) - usb_release_bandwidth(usb_dev, td->bandwidth_alloc); - uhci_td_free(td); + err: } - return 0; + return ret; } -static void uhci_interrupt_notify(struct uhci *uhci) -{ - struct list_head *tmp, *head = &uhci->interrupt_list; - int status; - - spin_lock(&irqlist_lock); - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, irq_list); - unsigned long rval; - tmp = tmp->next; +_static int process_iso (puhci_t s, purb_t purb) +{ + int i; + int ret = USB_ST_NOERROR; + purb_priv_t purb_priv = purb->hcpriv; + struct list_head *p = purb_priv->desc_list.next; + puhci_desc_t desc = list_entry (purb_priv->desc_list.prev, uhci_desc_t, desc_list); + + dbg ( /*KERN_DEBUG */ MODSTR "urb contains iso request\n"); + if (desc->hw.td.status & TD_CTRL_ACTIVE) + return USB_ST_PARTIAL_ERROR; // last TD not finished + + purb->error_count = 0; + purb->actual_length = 0; + purb->status = USB_ST_NOERROR; + + for (i = 0; p != &purb_priv->desc_list; p = p->next, i++) { + desc = list_entry (p, uhci_desc_t, desc_list); + + //uhci_show_td(desc); + if (desc->hw.td.status & TD_CTRL_ACTIVE) { + // means we have completed the last TD, but not the TDs before + printk (KERN_DEBUG MODSTR "TD still active (%x)- grrr. paranoia!\n", desc->hw.td.status); + ret = USB_ST_PARTIAL_ERROR; + purb->iso_frame_desc[i].status = ret; + unlink_td (s, desc); + goto err; + } - /* We're interested if there was an error or if the chain of */ - /* TD's completed successfully */ - status = uhci_td_result(td->dev, td, &rval); - if (status == USB_ST_NOCHANGE) - continue; + unlink_td (s, desc); - /* remove from IRQ list */ - list_del(&td->irq_list); - INIT_LIST_HEAD(&td->irq_list); + if (purb->number_of_packets <= i) { + dbg (KERN_DEBUG MODSTR "purb->number_of_packets (%d)<=(%d)\n", purb->number_of_packets, i); + ret = USB_ST_URB_INVALID_ERROR; + goto err; + } - switch (td->pipetype) { - case PIPE_ISOCHRONOUS: - uhci_isoc_callback(uhci, td, status, rval); - break; - case PIPE_BULK: - uhci_bulk_callback(uhci, td, status, rval); - break; - default: - uhci_callback(uhci, td, status, rval); + if (purb->iso_frame_desc[i].offset + purb->transfer_buffer != bus_to_virt (desc->hw.td.buffer)) { + // Hm, something really weird is going on + dbg (KERN_DEBUG MODSTR "Pointer Paranoia: %p!=%p\n", purb->iso_frame_desc[i].offset + purb->transfer_buffer, bus_to_virt (desc->hw.td.buffer)); + ret = USB_ST_URB_INVALID_ERROR; + purb->iso_frame_desc[i].status = ret; + goto err; } + purb->iso_frame_desc[i].actual_length = (desc->hw.td.status + 1) & 0x7ff; + purb->iso_frame_desc[i].status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (purb->pipe)); + purb->actual_length += purb->iso_frame_desc[i].actual_length; + + err: + + if (purb->iso_frame_desc[i].status != USB_ST_NOERROR) { + purb->error_count++; + purb->status = purb->iso_frame_desc[i].status; + } + dbg (KERN_DEBUG MODSTR "process_iso: len:%d status:%x\n", + purb->iso_frame_desc[i].length, purb->iso_frame_desc[i].status); - /* If completed does not wants to reactivate, then */ - /* it's responsible for free'ing the TD's and QH's */ - /* or another function (such as run_control) */ + delete_desc (desc); + list_del (p); } - spin_unlock(&irqlist_lock); + dbg ( /*KERN_DEBUG */ MODSTR "process_iso: exit %i (%d)\n", i, ret); + return ret; } -/* - * Check port status - Connect Status Change - for - * each of the attached ports (defaults to two ports, - * but at least in theory there can be more of them). - * - * Wake up the configurator if something happened, we - * can't really do much at interrupt time. - */ -static void uhci_root_hub_events(struct uhci *uhci, unsigned int io_addr) + +_static int process_urb (puhci_t s, struct list_head *p) { - if (waitqueue_active(&uhci_configure)) { - struct uhci_device *root_hub = usb_to_uhci(uhci->bus->root_hub); - int ports = root_hub->usb->maxchild; - - io_addr += USBPORTSC1; - do { - if (inw(io_addr) & USBPORTSC_CSC) { - wake_up(&uhci_configure); - return; + int ret = USB_ST_NOERROR; + purb_t purb = list_entry (p, urb_t, urb_list); + spin_lock(&s->urb_list_lock); + + dbg ( /*KERN_DEBUG */ MODSTR "found queued urb: %p\n", purb); + switch (usb_pipetype (purb->pipe)) { + case PIPE_CONTROL: + case PIPE_BULK: + ret = process_transfer (s, purb); + break; + case PIPE_ISOCHRONOUS: + ret = process_iso (s, purb); + break; + case PIPE_INTERRUPT: + ret = process_interrupt (s, purb); + break; + } + + spin_unlock(&s->urb_list_lock); + + if (purb->status != USB_ST_URB_PENDING) { + int proceed = 0; + dbg ( /*KERN_DEBUG */ MODSTR "dequeued urb: %p\n", purb); + dequeue_urb (s, p, 1); + +#ifdef _UHCI_SLAB + kmem_cache_free(urb_priv_kmem, purb->hcpriv); +#else + kfree (purb->hcpriv); +#endif + + if ((usb_pipetype (purb->pipe) != PIPE_INTERRUPT)) { + purb_t tmp = purb->next; // pointer to first urb + int is_ring = 0; + + if (purb->next) { + do { + if (tmp->status != USB_ST_URB_PENDING) { + proceed = 1; + break; + } + tmp = tmp->next; + } + while (tmp != NULL && tmp != purb->next); + if (tmp == purb->next) + is_ring = 1; + } + + // In case you need the current URB status for your completion handler + if (purb->complete && (!proceed || (purb->transfer_flags & USB_URB_EARLY_COMPLETE))) { + dbg (KERN_DEBUG MODSTR "process_transfer: calling early completion\n"); + purb->complete ((struct urb *) purb); + if (!proceed && is_ring) + uhci_submit_urb (purb); + } + + if (proceed && purb->next) { + // if there are linked urbs - handle submitting of them right now. + tmp = purb->next; // pointer to first urb + + do { + if ((tmp->status != USB_ST_URB_PENDING) && uhci_submit_urb (tmp) != USB_ST_NOERROR) + break; + tmp = tmp->next; + } + while (tmp != NULL && tmp != purb->next); // submit until we reach NULL or our own pointer or submit fails + + if (purb->complete && !(purb->transfer_flags & USB_URB_EARLY_COMPLETE)) { + dbg ( /*KERN_DEBUG */ MODSTR "process_transfer: calling completion\n"); + purb->complete ((struct urb *) purb); + } } - io_addr += 2; - } while (--ports > 0); + usb_dec_dev_use (purb->dev); + } } + + return ret; } -static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs) +_static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs) { - struct uhci *uhci = __uhci; - unsigned int io_addr = uhci->io_addr; + puhci_t s = __uhci; + unsigned int io_addr = s->io_addr; unsigned short status; + struct list_head *p, *p2; /* * Read the interrupt status, and write it back to clear the * interrupt cause */ - status = inw(io_addr + USBSTS); - if (!status) /* shared interrupt, not mine */ - return; - outw(status, io_addr + USBSTS); + dbg ("interrupt\n"); + status = inw (io_addr + USBSTS); - /* Walk the list of pending TD's to see which ones completed.. */ - uhci_interrupt_notify(uhci); + if (!status) /* shared interrupt, not mine */ + return; - /* Check if there are any events on the root hub.. */ - uhci_root_hub_events(uhci, io_addr); -} + if (status != 1) { + printk (KERN_DEBUG MODSTR "interrupt, status %x\n", status); + //uhci_show_status (s); + } + //beep(1000); + /* + * the following is very subtle and was blatantly wrong before + * traverse the list in *reverse* direction, because new entries + * may be added at the end. + * also, because process_urb may unlink the current urb, + * we need to advance the list before + * - Thomas Sailer + */ -/* - * We init one packet, and mark it just IOC and _not_ - * active. Which will result in no actual USB traffic, - * but _will_ result in an interrupt every second. - * - * Which is exactly what we want. - */ -static void uhci_init_ticktd(struct uhci *uhci) -{ - struct uhci_device *dev = usb_to_uhci(uhci->bus->root_hub); - struct uhci_td *td = uhci_td_alloc(dev); + spin_lock(&s->unlink_urb_lock); + spin_lock (&s->urb_list_lock); + p = s->urb_list.prev; + spin_unlock (&s->urb_list_lock); - if (!td) { - printk(KERN_ERR "unable to allocate ticktd\n"); - return; + while (p != &s->urb_list) { + p2 = p; + p = p->prev; + process_urb (s, p2); } - /* Don't clobber the frame */ - td->link = uhci->fl->frame[0]; - td->backptr = &uhci->fl->frame[0]; - td->status = TD_CTRL_IOC; - /* (ignored) input packet, 0 bytes, device 127 */ - td->info = (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN; - td->buffer = 0; - td->qh = NULL; - td->pipetype = -1; - - uhci->fl->frame[0] = virt_to_bus(td); + spin_unlock(&s->unlink_urb_lock); - uhci->ticktd = td; + outw (status, io_addr + USBSTS); +#ifdef __alpha + mb (); // ? +#endif + dbg ("done\n"); } -static void reset_hc(struct uhci *uhci) +_static void reset_hc (puhci_t s) { - unsigned int io_addr = uhci->io_addr; + unsigned int io_addr = s->io_addr; + s->apm_state = 0; /* Global reset for 50ms */ - outw(USBCMD_GRESET, io_addr + USBCMD); - wait_ms(50); - outw(0, io_addr + USBCMD); - wait_ms(10); + outw (USBCMD_GRESET, io_addr + USBCMD); + wait_ms (50); + outw (0, io_addr + USBCMD); + wait_ms (10); } -static void start_hc(struct uhci *uhci) +_static void start_hc (puhci_t s) { - unsigned int io_addr = uhci->io_addr; + unsigned int io_addr = s->io_addr; int timeout = 1000; - uhci_init_ticktd(uhci); - /* * Reset the HC - this will force us to get a * new notification of any already connected * ports due to the virtual disconnect that it * implies. */ - outw(USBCMD_HCRESET, io_addr + USBCMD); - while (inw(io_addr + USBCMD) & USBCMD_HCRESET) { + outw (USBCMD_HCRESET, io_addr + USBCMD); + + while (inw (io_addr + USBCMD) & USBCMD_HCRESET) { if (!--timeout) { - printk(KERN_ERR "USBCMD_HCRESET timed out!\n"); + printk (KERN_ERR MODSTR "USBCMD_HCRESET timed out!\n"); break; } } /* Turn on all interrupts */ - outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, - io_addr + USBINTR); + outw (USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR); /* Start at frame 0 */ - outw(0, io_addr + USBFRNUM); - outl(virt_to_bus(uhci->fl), io_addr + USBFLBASEADD); + outw (0, io_addr + USBFRNUM); + outl (virt_to_bus (s->framelist), io_addr + USBFLBASEADD); /* Run and mark it configured with a 64-byte max packet */ - outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); + outw (USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); + s->apm_state = 1; } -/* - * Allocate a frame list, and four regular queues. - * - * The hardware doesn't really know any difference - * in the queues, but the order does matter for the - * protocols higher up. The order is: - * - * - any isochronous events handled before any - * of the queues. We don't do that here, because - * we'll create the actual TD entries on demand. - * - The first queue is the "interrupt queue". - * - The second queue is the "control queue". - * - The third queue is "bulk data". - * - * We could certainly have multiple queues of the same - * type, and maybe we should. We could have per-device - * queues, for example. We begin small. - * - * Queues are dynamically allocated for devices now, - * this code only sets up the skeleton queue - */ -static struct uhci *alloc_uhci(unsigned int io_addr, unsigned int io_size) +_static void __exit uhci_cleanup_dev(puhci_t s) { - int i, port; - struct uhci *uhci; - struct usb_bus *bus; - struct uhci_device *dev; - struct usb_device *usb; + struct usb_device *root_hub = s->bus->root_hub; + if (root_hub) + usb_disconnect (&root_hub); - uhci = kmalloc(sizeof(*uhci), GFP_KERNEL); - if (!uhci) - return NULL; - - memset(uhci, 0, sizeof(*uhci)); - - uhci->irq = -1; - uhci->io_addr = io_addr; - uhci->io_size = io_size; - INIT_LIST_HEAD(&uhci->interrupt_list); - - /* We need exactly one page (per UHCI specs), how convenient */ - uhci->fl = (void *)__get_free_page(GFP_KERNEL); - if (!uhci->fl) - goto au_free_uhci; - - bus = usb_alloc_bus(&uhci_device_operations); - if (!bus) - goto au_free_fl; + usb_deregister_bus (s->bus); - uhci->bus = bus; - bus->hcpriv = uhci; + reset_hc (s); + release_region (s->io_addr, s->io_size); + free_irq (s->irq, s); + usb_free_bus (s->bus); + cleanup_skel (s); + kfree (s); - /* - * Allocate the root_hub - */ - usb = usb_alloc_dev(NULL, bus); - if (!usb) - goto au_free_bus; +} + +_static int __init uhci_start_usb (puhci_t s) +{ /* start it up */ + /* connect the virtual root hub */ + struct usb_device *usb_dev; + + usb_dev = usb_alloc_dev (NULL, s->bus); + if (!usb_dev) + return -1; - usb->bus = bus; + s->bus->root_hub = usb_dev; + usb_connect (usb_dev); - dev = usb_to_uhci(usb); - dev->uhci = uhci; + if (usb_new_device (usb_dev) != 0) { + usb_free_dev (usb_dev); + return -1; + } + + return 0; +} + +_static int __init alloc_uhci (int irq, unsigned int io_addr, unsigned int io_size) +{ + puhci_t s; + struct usb_bus *bus; - uhci->bus->root_hub = uhci_to_usb(dev); + s = kmalloc (sizeof (uhci_t), GFP_KERNEL); + if (!s) + return -1; + + memset (s, 0, sizeof (uhci_t)); + INIT_LIST_HEAD (&s->urb_list); + spin_lock_init (&s->urb_list_lock); + spin_lock_init (&s->qh_lock); + spin_lock_init (&s->td_lock); + spin_lock_init (&s->unlink_urb_lock); + s->irq = -1; + s->io_addr = io_addr; + s->io_size = io_size; + s->next = devs; //chain new uhci device into global list + + bus = usb_alloc_bus (&uhci_device_operations); + if (!bus) { + kfree (s); + return -1; + } - /* Initialize the root hub */ + s->bus = bus; + bus->hcpriv = s; /* UHCI specs says devices must have 2 ports, but goes on to say */ /* they may have more but give no way to determine how many they */ /* have, so default to 2 */ /* According to the UHCI spec, Bit 7 is always set to 1. So we try */ /* to use this to our advantage */ - for (port = 0; port < (io_size - 0x10) / 2; port++) { + + for (s->maxports = 0; s->maxports < (io_size - 0x10) / 2; s->maxports++) { unsigned int portstatus; - portstatus = inw(io_addr + 0x10 + (port * 2)); + portstatus = inw (io_addr + 0x10 + (s->maxports * 2)); + printk ("port %i, adr %x status %x\n", s->maxports, + io_addr + 0x10 + (s->maxports * 2), portstatus); if (!(portstatus & 0x0080)) break; } - printk(KERN_DEBUG "Detected %d ports\n", port); + dbg (KERN_DEBUG MODSTR "Detected %d ports\n", s->maxports); /* This is experimental so anything less than 2 or greater than 8 is */ /* something weird and we'll ignore it */ - if (port < 2 || port > 8) { - printk(KERN_DEBUG "Port count misdetected, forcing to 2 ports\n"); - port = 2; - } - - usb->maxchild = port; - usb_init_root_hub(usb); - - /* - * 9 Interrupt queues; link int2 thru int256 to int1 first, - * then link int1 to control and control to bulk - */ - for (i = 1; i < 9; i++) { - struct uhci_qh *qh = &uhci->skelqh[i]; - - qh->link = virt_to_bus(&uhci->skel_int1_qh) | UHCI_PTR_QH; - qh->element = UHCI_PTR_TERM; + if (s->maxports < 2 || s->maxports > 8) { + dbg (KERN_DEBUG "Port count misdetected, forcing to 2 ports\n"); + s->maxports = 2; } - uhci->skel_int1_qh.link = virt_to_bus(&uhci->skel_control_qh) | UHCI_PTR_QH; - uhci->skel_int1_qh.element = UHCI_PTR_TERM; - - uhci->skel_control_qh.link = virt_to_bus(&uhci->skel_bulk_qh) | UHCI_PTR_QH; - uhci->skel_control_qh.element = UHCI_PTR_TERM; + s->rh.numports = s->maxports; - uhci->skel_bulk_qh.link = UHCI_PTR_TERM; - uhci->skel_bulk_qh.element = UHCI_PTR_TERM; - - /* - * Fill the frame list: make all entries point to - * the proper interrupt queue. - * - * This is probably silly, but it's a simple way to - * scatter the interrupt queues in a way that gives - * us a reasonable dynamic range for irq latencies. - */ - for (i = 0; i < 1024; i++) { - struct uhci_qh *irq = &uhci->skel_int2_qh; - if (i & 1) { - irq++; - if (i & 2) { - irq++; - if (i & 4) { - irq++; - if (i & 8) { - irq++; - if (i & 16) { - irq++; - if (i & 32) { - irq++; - if (i & 64) { - irq++; - } - } - } - } - } - } - } - uhci->fl->frame[i] = virt_to_bus(irq) | UHCI_PTR_QH; + if (init_skel (s)) { + usb_free_bus (bus); + kfree(s); + return -1; } - return uhci; - -/* - * error exits: - */ - -au_free_bus: - usb_free_bus(bus); -au_free_fl: - free_page((unsigned long)uhci->fl); -au_free_uhci: - kfree(uhci); - return NULL; -} - -/* - * De-allocate all resources.. - */ -static void release_uhci(struct uhci *uhci) -{ - if (uhci->irq >= 0) { - free_irq(uhci->irq, uhci); - uhci->irq = -1; + request_region (s->io_addr, io_size, MODNAME); + reset_hc (s); + usb_register_bus (s->bus); + + start_hc (s); + + if (request_irq (irq, uhci_interrupt, SA_SHIRQ, MODNAME, s)) { + usb_free_bus (bus); + reset_hc (s); + release_region (s->io_addr, s->io_size); + cleanup_skel(s); + kfree(s); + return -1; } - if (uhci->ticktd) { - uhci_td_free(uhci->ticktd); - uhci->ticktd = NULL; - } + s->irq = irq; - if (uhci->fl) { - free_page((unsigned long)uhci->fl); - uhci->fl = NULL; + if(uhci_start_usb (s) < 0) { + uhci_cleanup_dev(s); + return -1; } - - usb_free_bus(uhci->bus); - kfree(uhci); -} - -static int uhci_control_thread(void *__uhci) -{ - struct uhci *uhci = (struct uhci *)__uhci; - - uhci->control_running = 1; - - lock_kernel(); - - /* - * This thread doesn't need any user-level access, - * so get rid of all our resources.. - */ - exit_mm(current); - exit_files(current); - - strcpy(current->comm, "uhci-control"); - - /* - * Ok, all systems are go.. - */ - do { - siginfo_t info; - int unsigned long signr; - -#ifdef CONFIG_APM - if (apm_resume) { - apm_resume = 0; - start_hc(uhci); - continue; - } -#endif - uhci_check_configuration(uhci); - - interruptible_sleep_on(&uhci_configure); - - if (signal_pending(current)) { - /* sending SIGUSR1 makes us print out some info */ - spin_lock_irq(¤t->sigmask_lock); - signr = dequeue_signal(¤t->blocked, &info); - spin_unlock_irq(¤t->sigmask_lock); - - if (signr == SIGUSR1) { - printk(KERN_DEBUG "UHCI queue dump:\n"); - uhci_show_queues(uhci); - } else if (signr == SIGUSR2) { - uhci_debug = !uhci_debug; - printk(KERN_DEBUG "UHCI debug toggle = %x\n", - uhci_debug); - } else - break; - } - } while (uhci->control_continue); - -/* - MOD_DEC_USE_COUNT; -*/ - - uhci->control_running = 0; + + //chain new uhci device into global list + devs = s; return 0; -} - -/* - * If we've successfully found a UHCI, now is the time to increment the - * module usage count, start the control thread, and return success.. - */ -static int found_uhci(int irq, unsigned int io_addr, unsigned int io_size) -{ - int retval; - struct uhci *uhci; - - uhci = alloc_uhci(io_addr, io_size); - if (!uhci) - return -ENOMEM; - - INIT_LIST_HEAD(&uhci->uhci_list); - list_add(&uhci->uhci_list, &uhci_list); - - request_region(uhci->io_addr, io_size, "usb-uhci"); - - reset_hc(uhci); - - usb_register_bus(uhci->bus); - start_hc(uhci); - - uhci->control_continue = 1; - - retval = -EBUSY; - if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "uhci", uhci) == 0) { - int pid; - - uhci->irq = irq; - pid = kernel_thread(uhci_control_thread, uhci, - CLONE_FS | CLONE_FILES | CLONE_SIGHAND); - if (pid >= 0) { - uhci->control_pid = pid; - - return(pid); - } - - retval = pid; - } - - reset_hc(uhci); - release_region(uhci->io_addr, uhci->io_size); - - release_uhci(uhci); - return retval; } -static int start_uhci(struct pci_dev *dev) +_static int __init start_uhci (struct pci_dev *dev) { int i; /* Search for the IO base address.. */ for (i = 0; i < 6; i++) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,8) unsigned int io_addr = dev->resource[i].start; unsigned int io_size = - dev->resource[i].end - dev->resource[i].start + 1; - - /* IO address? */ + dev->resource[i].end - dev->resource[i].start + 1; if (!(dev->resource[i].flags & 1)) continue; +#else + unsigned int io_addr = dev->base_address[i]; + unsigned int io_size = 0x14; + if (!(io_addr & 1)) + continue; + io_addr &= ~1; +#endif /* Is it already in use? */ - if (check_region(io_addr, io_size)) + if (check_region (io_addr, io_size)) break; - +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,8) + pci_enable_device (dev); +#endif /* disable legacy emulation */ - pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT); - - pci_enable_device(dev); + pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT); - return found_uhci(dev->irq, io_addr, io_size); + return alloc_uhci(dev->irq, io_addr, io_size); } return -1; } #ifdef CONFIG_APM -static int handle_apm_event(apm_event_t event) +_static int handle_apm_event (apm_event_t event) { static int down = 0; - + puhci_t s = devs; + printk ("handle_apm_event(%d)\n", event); switch (event) { case APM_SYS_SUSPEND: case APM_USER_SUSPEND: if (down) { - printk(KERN_DEBUG "uhci: received extra suspend event\n"); + dbg (KERN_DEBUG MODSTR "received extra suspend event\n"); break; } + while (s) { + reset_hc (s); + s = s->next; + } down = 1; break; case APM_NORMAL_RESUME: case APM_CRITICAL_RESUME: if (!down) { - printk(KERN_DEBUG "uhci: received bogus resume event\n"); + dbg (KERN_DEBUG MODSTR "received bogus resume event\n"); break; } down = 0; - if (waitqueue_active(&uhci_configure)) { - apm_resume = 1; - wake_up(&uhci_configure); + while (s) { + start_hc (s); + s = s->next; } break; } @@ -2328,116 +2251,90 @@ } #endif -int uhci_init(void) +int __init uhci_init (void) { - int retval; + int retval = -ENODEV; struct pci_dev *dev = NULL; u8 type; + int i=0; - uhci_td_cachep = kmem_cache_create("uhci_td", - sizeof(struct uhci_td), 0, - SLAB_HWCACHE_ALIGN, NULL, NULL); +#ifdef _UHCI_SLAB + char *slabname=kmalloc(16, GFP_KERNEL); - if (!uhci_td_cachep) + if(!slabname) return -ENOMEM; - uhci_qh_cachep = kmem_cache_create("uhci_qh", - sizeof(struct uhci_qh), 0, - SLAB_HWCACHE_ALIGN, NULL, NULL); + strcpy(slabname, "uhci_desc"); + uhci_desc_kmem = kmem_cache_create(slabname, sizeof(uhci_desc_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + + if(!uhci_desc_kmem) { + printk(KERN_ERR MODSTR"kmem_cache_create for uhci_desc failed (out of memory)\n"); + return -ENOMEM; + } + + slabname=kmalloc(16, GFP_KERNEL); - if (!uhci_qh_cachep) + if(!slabname) return -ENOMEM; - retval = -ENODEV; + strcpy(slabname, "urb_priv"); + urb_priv_kmem = kmem_cache_create(slabname, sizeof(urb_priv_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + + if(!urb_priv_kmem) { + printk(KERN_ERR MODSTR"kmem_cache_create for urb_priv_t failed (out of memory)\n"); + return -ENOMEM; + } +#endif + printk (KERN_INFO MODSTR VERSTR "\n"); + for (;;) { - dev = pci_find_class(PCI_CLASS_SERIAL_USB << 8, dev); + dev = pci_find_class (PCI_CLASS_SERIAL_USB << 8, dev); if (!dev) break; /* Is it UHCI */ - pci_read_config_byte(dev, PCI_CLASS_PROG, &type); + pci_read_config_byte (dev, PCI_CLASS_PROG, &type); if (type != 0) continue; /* Ok set it up */ - retval = start_uhci(dev); - if (retval < 0) - continue; + retval = start_uhci (dev); + if (!retval) + i++; + } #ifdef CONFIG_APM - apm_register_callback(&handle_apm_event); + if(i) + apm_register_callback (&handle_apm_event); #endif - return 0; - } return retval; } -void uhci_cleanup(void) +void __exit uhci_cleanup (void) { - struct list_head *next, *tmp, *head = &uhci_list; - int ret, i; - - tmp = head->next; - while (tmp != head) { - struct uhci *uhci = list_entry(tmp, struct uhci, uhci_list); - struct uhci_device *root_hub = usb_to_uhci(uhci->bus->root_hub); - - next = tmp->next; - - list_del(&uhci->uhci_list); - INIT_LIST_HEAD(&uhci->uhci_list); - - /* Check if the process is still running */ - ret = kill_proc(uhci->control_pid, 0, 1); - if (!ret) { - /* Try a maximum of 10 seconds */ - int count = 10 * 100; - - uhci->control_continue = 0; - wake_up(&uhci_configure); - - while (uhci->control_running && --count) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - } - - if (!count) - printk(KERN_ERR "uhci: giving up on killing uhci-control\n"); - } - - if (root_hub) - for (i = 0; i < root_hub->usb->maxchild; i++) - usb_disconnect(root_hub->usb->children + i); - - usb_deregister_bus(uhci->bus); - - reset_hc(uhci); - release_region(uhci->io_addr, uhci->io_size); - - release_uhci(uhci); - - tmp = next; - } - - if (kmem_cache_destroy(uhci_qh_cachep)) - printk(KERN_INFO "uhci: not all QH's were freed\n"); - - if (kmem_cache_destroy(uhci_td_cachep)) - printk(KERN_INFO "uhci: not all TD's were freed\n"); + puhci_t s; + while ((s = devs)) { + devs = devs->next; + uhci_cleanup_dev(s); + } +#ifdef _UHCI_SLAB + kmem_cache_shrink(uhci_desc_kmem); + kmem_cache_shrink(urb_priv_kmem); +#endif } #ifdef MODULE -int init_module(void) +int init_module (void) { - return uhci_init(); + return uhci_init (); } -void cleanup_module(void) +void cleanup_module (void) { #ifdef CONFIG_APM - apm_unregister_callback(&handle_apm_event); + apm_unregister_callback (&handle_apm_event); #endif - uhci_cleanup(); + uhci_cleanup (); } -#endif //MODULE +#endif //MODULE diff -u --recursive --new-file v2.3.33/linux/drivers/usb/uhci.h linux/drivers/usb/uhci.h --- v2.3.33/linux/drivers/usb/uhci.h Sun Nov 7 16:37:34 1999 +++ linux/drivers/usb/uhci.h Fri Dec 17 16:49:45 1999 @@ -1,13 +1,12 @@ #ifndef __LINUX_UHCI_H #define __LINUX_UHCI_H -#include - -#include "usb.h" - /* - * Universal Host Controller Interface data structures and defines + $Id: uhci.h,v 1.30 1999/12/15 17:57:25 fliegl Exp $ */ +#define MODNAME "usb-uhci" +#define MODSTR MODNAME": " +#define VERSTR "version v0.9 time " __TIME__ " " __DATE__ /* Command register */ #define USBCMD 0 @@ -55,7 +54,7 @@ /* Legacy support register */ #define USBLEGSUP 0xc0 -#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ +#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ #define UHCI_NULL_DATA_SIZE 0x7ff /* for UHCI controller TD */ @@ -68,27 +67,6 @@ #define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ #define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */ -struct uhci_td; - -struct uhci_qh { - /* Hardware fields */ - __u32 link; /* Next queue */ - __u32 element; /* Queue element pointer */ - - /* Software fields */ - atomic_t refcnt; /* Reference counting */ - struct uhci_device *dev; /* The owning device */ - struct uhci_qh *skel; /* Skeleton head */ - - struct uhci_td *first; /* First TD in the chain */ - - wait_queue_head_t wakeup; -} __attribute__((aligned(16))); - -struct uhci_framelist { - __u32 frame[UHCI_NUMFRAMES]; -} __attribute__((aligned(4096))); - /* * for TD : */ @@ -104,20 +82,19 @@ #define TD_CTRL_NAK (1 << 19) /* NAK Received */ #define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ #define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ -#define TD_CTRL_ACTLEN_MASK 0x7ff /* actual length, encoded as n - 1 */ +#define TD_CTRL_ACTLEN_MASK 0x7ff /* actual length, encoded as n - 1 */ #define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) #define uhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000) -#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ - +#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ #define uhci_ptr_to_virt(x) bus_to_virt(x & ~UHCI_PTR_BITS) /* * for TD : */ -#define UHCI_TD_REMOVE 0x0001 /* Remove when done */ +#define UHCI_TD_REMOVE 0x0001 /* Remove when done */ /* * for TD : (a.k.a. Token) @@ -125,7 +102,6 @@ #define TD_TOKEN_TOGGLE 19 #define uhci_maxlen(token) ((token) >> 21) -#define uhci_expected_length(info) (((info >> 21) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ #define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) #define uhci_endpoint(token) (((token) >> 15) & 0xf) #define uhci_devaddr(token) (((token) >> 8) & 0x7f) @@ -134,201 +110,143 @@ #define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN) #define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN) -/* - * The documentation says "4 words for hardware, 4 words for software". - * - * That's silly, the hardware doesn't care. The hardware only cares that - * the hardware words are 16-byte aligned, and we can have any amount of - * sw space after the TD entry as far as I can tell. - * - * But let's just go with the documentation, at least for 32-bit machines. - * On 64-bit machines we probably want to take advantage of the fact that - * hw doesn't really care about the size of the sw-only area. - * - * Alas, not anymore, we have more than 4 words for software, woops - */ -struct uhci_td { - /* Hardware fields */ +/* ------------------------------------------------------------------------------------ + New TD/QH-structures + ------------------------------------------------------------------------------------ */ +typedef enum { + TD_TYPE, QH_TYPE +} uhci_desc_type_t; + +typedef struct { __u32 link; __u32 status; __u32 info; __u32 buffer; +} uhci_td_t, *puhci_td_t; - /* Software fields */ - unsigned int *backptr; /* Where to remove this from.. */ - struct list_head irq_list; /* Active interrupt list.. */ - - usb_device_irq completed; /* Completion handler routine */ - void *dev_id; - - atomic_t refcnt; /* Reference counting */ - - struct uhci_device *dev; /* The owning device */ - struct uhci_qh *qh; /* QH this TD is a part of (ignored for Isochronous) */ - unsigned char pipetype; /* Control, Bulk, Interrupt, Isoc */ - int flags; /* Remove, etc */ - int isoc_td_number; /* 0-relative number within a usb_isoc_desc. */ - int bandwidth_alloc; /* in microsecs; used only for Interrupt - * transfers, to return its bandwidth */ -} __attribute__((aligned(16))); - -/* - * Note the alignment requirements of the entries - * - * Each UHCI device has pre-allocated QH and TD entries. - * You can use more than the pre-allocated ones, but I - * don't see you usually needing to. - */ -struct uhci; - -/* The usb device part must be first! Not anymore -jerdfelt */ -struct uhci_device { - struct usb_device *usb; - - atomic_t refcnt; - - struct uhci *uhci; - - unsigned long data[16]; +typedef struct { + __u32 head; + __u32 element; /* Queue element pointer */ +} uhci_qh_t, *puhci_qh_t; + +typedef struct { + union { + uhci_td_t td; + uhci_qh_t qh; + } hw; + uhci_desc_type_t type; + struct list_head horizontal; + struct list_head vertical; + struct list_head desc_list; +} uhci_desc_t, *puhci_desc_t; + +typedef struct { + struct list_head desc_list; // list pointer to all corresponding TDs/QHs associated with this request + int short_control_packet; +} urb_priv_t, *purb_priv_t; + +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *urb; + void *int_addr; + int send; + int interval; + int numports; + int c_p_r[8]; + struct timer_list rh_int_timer; }; -#define uhci_to_usb(uhci) ((uhci)->usb) -#define usb_to_uhci(usb) ((struct uhci_device *)(usb)->hcpriv) - -/* - * There are various standard queues. We set up several different - * queues for each of the three basic queue types: interrupt, - * control, and bulk. - * - * - There are various different interrupt latencies: ranging from - * every other USB frame (2 ms apart) to every 256 USB frames (ie - * 256 ms apart). Make your choice according to how obnoxious you - * want to be on the wire, vs how critical latency is for you. - * - The control list is done every frame. - * - There are 4 bulk lists, so that up to four devices can have a - * bulk list of their own and when run concurrently all four lists - * will be be serviced. - * - * This is a bit misleading, there are various interrupt latencies, but they - * vary a bit, interrupt2 isn't exactly 2ms, it can vary up to 4ms since the - * other queues can "override" it. interrupt4 can vary up to 8ms, etc. Minor - * problem - * - * In the case of the root hub, these QH's are just head's of qh's. Don't - * be scared, it kinda makes sense. Look at this wonderful picture care of - * Linus: - * - * generic- -> dev1- -> generic- -> dev1- -> control- -> bulk- -> ... - * iso-QH iso-QH irq-QH irq-QH QH QH - * | | | | | | - * End dev1-iso-TD1 End dev1-irq-TD1 ... ... - * | - * dev1-iso-TD2 - * | - * .... - * - * This may vary a bit (the UHCI docs don't explicitly say you can put iso - * transfers in QH's and all of their pictures don't have that either) but - * other than that, that is what we're doing now - * - * And now we don't put Iso transfers in QH's, so we don't waste one on it - * --jerdfelt - * - * To keep with Linus' nomenclature, this is called the QH skeleton. These - * labels (below) are only signficant to the root hub's QH's - */ -#define UHCI_NUM_SKELQH 11 - -#define skel_int1_qh skelqh[0] -#define skel_int2_qh skelqh[1] -#define skel_int4_qh skelqh[2] -#define skel_int8_qh skelqh[3] -#define skel_int16_qh skelqh[4] -#define skel_int32_qh skelqh[5] -#define skel_int64_qh skelqh[6] -#define skel_int128_qh skelqh[7] -#define skel_int256_qh skelqh[8] - -#define skel_control_qh skelqh[9] - -#define skel_bulk_qh skelqh[10] - -/* - * Search tree for determining where fits in the - * skelqh[] skeleton. - * - * An interrupt request should be placed into the slowest skelqh[] - * which meets the interval/period/frequency requirement. - * An interrupt request is allowed to be faster than but not slower. - * - * For a given , this function returns the appropriate/matching - * skelqh[] index value. - * - * NOTE: For UHCI, we don't really need int256_qh since the maximum interval - * is 255 ms. However, we do need an int1_qh since 1 is a valid interval - * and we should meet that frequency when requested to do so. - * This will require some change(s) to the UHCI skeleton. - */ -static inline int __interval_to_skel(int interval) -{ - if (interval < 16) { - if (interval < 4) { - if (interval < 2) { - return 0; /* int1 for 0-1 ms */ - } - return 1; /* int2 for 2-3 ms */ - } - if (interval < 8) { - return 2; /* int4 for 4-7 ms */ - } - return 3; /* int8 for 8-15 ms */ - } - if (interval < 64) { - if (interval < 32) { - return 4; /* int16 for 16-31 ms */ - } - return 5; /* int32 for 32-63 ms */ - } - if (interval < 128) - return 6; /* int64 for 64-127 ms */ - return 7; /* int128 for 128-255 ms (Max.) */ -} - -/* - * This describes the full uhci information. - * - * Note how the "proper" USB information is just - * a subset of what the full implementation needs. - */ -struct uhci { +typedef struct uhci { int irq; unsigned int io_addr; unsigned int io_size; + unsigned int maxports; - int control_pid; - int control_running; - int control_continue; - - struct list_head uhci_list; + int apm_state; - struct usb_bus *bus; + struct uhci *next; // chain of uhci device contexts - struct uhci_qh skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */ + struct list_head urb_list; // list of all pending urbs - struct uhci_framelist *fl; /* Frame list */ - struct list_head interrupt_list; /* List of interrupt-active TD's for this uhci */ + spinlock_t urb_list_lock; // lock to keep consistency + + struct usb_bus *bus; // our bus + + spinlock_t unlink_urb_lock; // lock for unlink_urb + + __u32 *framelist; + uhci_desc_t **iso_td; + uhci_desc_t *int_chain[8]; + uhci_desc_t *control_chain; + uhci_desc_t *bulk_chain; + uhci_desc_t *chain_end; + spinlock_t qh_lock; + spinlock_t td_lock; + struct virt_root_hub rh; //private data of the virtual root hub +} uhci_t, *puhci_t; + + +#define MAKE_TD_ADDR(a) (virt_to_bus(a)&~UHCI_PTR_QH) +#define MAKE_QH_ADDR(a) (virt_to_bus(a)|UHCI_PTR_QH) +#define UHCI_GET_CURRENT_FRAME(uhci) (inw ((uhci)->io_addr + USBFRNUM)) + +/* ------------------------------------------------------------------------------------ + Virtual Root HUB + ------------------------------------------------------------------------------------ */ +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +/* Our Vendor Specific feature */ +#define RH_REMOVE_EP 0x00 + + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 - struct uhci_td *ticktd; -}; - -/* needed for the debugging code */ -struct uhci_td *uhci_link_to_td(unsigned int element); - -/* Debugging code */ -void uhci_show_td(struct uhci_td *td); -void uhci_show_status(struct uhci *uhci); -void uhci_show_queue(struct uhci_qh *qh); -void uhci_show_queues(struct uhci *uhci); +#define min(a,b) (((a)<(b))?(a):(b)) #endif - diff -u --recursive --new-file v2.3.33/linux/drivers/usb/usb-core.c linux/drivers/usb/usb-core.c --- v2.3.33/linux/drivers/usb/usb-core.c Wed Dec 8 14:11:27 1999 +++ linux/drivers/usb/usb-core.c Thu Dec 16 01:27:42 1999 @@ -9,6 +9,7 @@ * library, while this file handles starting drivers, etc. * */ +#include #include #include @@ -19,9 +20,6 @@ # 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); # endif @@ -39,9 +37,6 @@ # ifdef CONFIG_USB_UHCI uhci_init(); # endif -# ifdef CONFIG_USB_OHCI - ohci_init(); -# endif # ifdef CONFIG_USB_OHCI_HCD ohci_hcd_init(); # endif @@ -75,6 +70,9 @@ # ifdef CONFIG_USB_SCSI usb_scsi_init(); # endif +# ifdef CONFIG_USB_DABUSB + dabusb_init(); +# endif #endif return 0; } @@ -88,14 +86,18 @@ #ifdef CONFIG_USB_PROC proc_usb_cleanup (); #endif - usb_hub_cleanup(); + usb_hub_cleanup(); #ifndef MODULE + # ifdef CONFIG_USB_MOUSE usb_mouse_cleanup(); # endif # ifdef CONFIG_USB_HP_SCANNER usb_hp_scanner_cleanup(); # endif +# ifdef CONFIG_USB_DABUSB + dabusb_cleanup(); +# endif #endif } diff -u --recursive --new-file v2.3.33/linux/drivers/usb/usb-debug.c linux/drivers/usb/usb-debug.c --- v2.3.33/linux/drivers/usb/usb-debug.c Thu Aug 26 13:05:39 1999 +++ linux/drivers/usb/usb-debug.c Thu Dec 16 01:27:42 1999 @@ -4,8 +4,8 @@ * I just want these out of the way where they aren't in your * face, but so that you can still use them.. */ +#include #include - #include "usb.h" static void usb_show_endpoint(struct usb_endpoint_descriptor *endpoint) diff -u --recursive --new-file v2.3.33/linux/drivers/usb/usb-serial.c linux/drivers/usb/usb-serial.c --- v2.3.33/linux/drivers/usb/usb-serial.c Wed Dec 15 10:43:16 1999 +++ linux/drivers/usb/usb-serial.c Fri Dec 17 16:54:22 1999 @@ -14,6 +14,13 @@ * * See README.serial for more information on using this driver. * + * version 0.2.2 (12/16/99) gkh + * Changed major number to the new allocated number. We're legal now! + * + * version 0.2.1 (12/14/99) gkh + * Fixed bug that happens when device node is opened when there isn't a + * device attached to it. Thanks to marek@webdesign.no for noticing this. + * * version 0.2.0 (11/10/99) gkh * Split up internals to make it easier to add different types of serial * converters to the code. @@ -83,7 +90,7 @@ #define PERACOM_SERIAL_CONVERTER 0x0001 -#define SERIAL_MAJOR 240 +#define SERIAL_MAJOR 188 /* Nice legal number now */ #define NUM_PORTS 4 /* Have to pick a number for now. Need to look */ /* into dynamically creating them at insertion time. */ @@ -640,7 +647,7 @@ /* Need to do device specific setup here (control lines, baud rate, etc.) */ /* FIXME!!! */ - + return (0); } @@ -715,7 +722,7 @@ serial->bulk_out_transfer = usb_request_bulk (serial->dev, serial->bulk_out_pipe, serial_write_irq, serial->bulk_out_buffer, 1, serial); return; -} +} static int etek_write_room (struct tty_struct *tty) @@ -798,7 +805,7 @@ serial->bulk_in_inuse = 1; serial->bulk_in_transfer = usb_request_bulk (serial->dev, serial->bulk_in_pipe, serial_read_irq, serial->bulk_in_buffer, serial->bulk_in_size, serial); } - + return (0); } @@ -877,7 +884,7 @@ } return; -} +} static int generic_write_room (struct tty_struct *tty) diff -u --recursive --new-file v2.3.33/linux/drivers/usb/usb.c linux/drivers/usb/usb.c --- v2.3.33/linux/drivers/usb/usb.c Tue Nov 23 22:42:21 1999 +++ linux/drivers/usb/usb.c Fri Dec 17 16:54:00 1999 @@ -5,6 +5,7 @@ * (C) Copyright Johannes Erdfelt 1999 * (C) Copyright Andreas Gal 1999 * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 (new USB architecture) * * NOTE! This is not actually a driver at all, rather this is * just a collection of helper routines that implement the @@ -13,6 +14,8 @@ * Think of this as a "USB library" rather than anything else. * It should be considered a slave, with no callbacks. Callbacks * are evil. + * + * $Id: usb.c,v 1.37 1999/12/17 10:48:08 fliegl Exp $ */ #ifndef EXPORT_SYMTAB @@ -26,9 +29,21 @@ #include #include #include +#include /* for in_interrupt() */ #include "usb.h" +#define MODSTR "usbcore: " + +#ifdef USB_DEBUG + #define dbg printk +#else + #define dbg nix +static void nix(const char *format, ...) +{ +} +#endif + /* * Prototypes for the device driver probing/loading functions */ @@ -36,8 +51,6 @@ static int usb_find_interface_driver(struct usb_device *, unsigned int); static void usb_check_support(struct usb_device *); -static int usb_debug = 1; - /* * We have a per-interface "registered driver" list. */ @@ -54,15 +67,14 @@ if (new_driver->fops != NULL) { if (usb_minors[new_driver->minor/16]) { - printk(KERN_ERR "Error registering %s driver\n", + printk(KERN_ERR "Error registering %s driver\n", new_driver->name); - return USB_ST_NOTSUPPORTED; + return -EINVAL; } usb_minors[new_driver->minor/16] = new_driver; } printk("usbcore: Registered new driver %s\n", new_driver->name); - /* Add it to the list of known drivers */ list_add(&new_driver->driver_list, &usb_driver_list); @@ -77,7 +89,7 @@ while (tmp != &usb_bus_list) { struct usb_bus *bus = list_entry(tmp,struct usb_bus, bus_list); - tmp = tmp->next; + tmp = tmp->next; usb_check_support(bus->root_hub); } return 0; @@ -100,21 +112,21 @@ if (dev->children[i]) usb_drivers_purge(driver, dev->children[i]); - if (!dev->actconfig) - return; - - for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { - struct usb_interface *interface = &dev->actconfig->interface[i]; + if (!dev->actconfig) + return; + + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { + struct usb_interface *interface = &dev->actconfig->interface[i]; - if (interface->driver == driver) { - driver->disconnect(dev, interface->private_data); + if (interface->driver == driver) { + driver->disconnect(dev, interface->private_data); usb_driver_release_interface(driver, interface); - /* - * This will go through the list looking for another - * driver that can handle the device - */ - usb_find_interface_driver(dev, i); - } + /* + * This will go through the list looking for another + * driver that can handle the device + */ + usb_find_interface_driver(dev, i); + } } } @@ -157,12 +169,12 @@ if (low_speed) /* no isoc. here */ { if (input_dir) - { - tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L; + { + tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L; return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); } else - { + { tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L; return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); } @@ -308,13 +320,13 @@ if (dev->children[i]) usb_check_support(dev->children[i]); - if (!dev->actconfig) - return; + if (!dev->actconfig) + return; /* now we check this device */ - if (dev->devnum > 0) - for (i = 0; i < dev->actconfig->bNumInterfaces; i++) - usb_find_interface_driver(dev, i); + if (dev->devnum > 0) + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) + usb_find_interface_driver(dev, i); } @@ -376,7 +388,7 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) { struct list_head *tmp = usb_driver_list.next; - struct usb_interface *interface; + struct usb_interface *interface; if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) { printk(KERN_ERR "usb-core: bad find_interface_driver params\n"); @@ -385,22 +397,22 @@ interface = dev->actconfig->interface + ifnum; - if (usb_interface_claimed(interface)) - return -1; + if (usb_interface_claimed(interface)) + return -1; - while (tmp != &usb_driver_list) { + while (tmp != &usb_driver_list) { void *private; - struct usb_driver *driver = list_entry(tmp, struct usb_driver, - driver_list); - - tmp = tmp->next; - if (!(private = driver->probe(dev, ifnum))) - continue; + struct usb_driver *driver = list_entry(tmp, struct usb_driver, + driver_list); + + tmp = tmp->next; + if (!(private = driver->probe(dev, ifnum))) + continue; usb_driver_claim_interface(driver, interface, private); - return 0; - } - + return 0; + } + return -1; } @@ -412,7 +424,7 @@ static void usb_find_drivers(struct usb_device *dev) { unsigned ifnum; - unsigned rejected = 0; + unsigned rejected = 0; for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) { /* if this interface hasn't already been claimed */ @@ -422,9 +434,8 @@ } } - if (rejected) { + if (rejected) printk(KERN_DEBUG "usbcore: unhandled interfaces on device.\n"); - } } /* @@ -463,7 +474,358 @@ { atomic_inc(&dev->refcnt); } +/* ------------------------------------------------------------------------------------- + * New USB Core Functions + * -------------------------------------------------------------------------------------*/ + +urb_t* usb_alloc_urb(int iso_packets) +{ + urb_t *urb; + urb=(urb_t*)kmalloc(sizeof(urb_t) + iso_packets*sizeof(iso_packet_descriptor_t), + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!urb) + { + printk(KERN_ERR MODSTR"alloc_urb: kmalloc failed\n"); + return 0; + } + memset(urb,0,sizeof(urb_t)); + return urb; +} + +/*-------------------------------------------------------------------*/ +void usb_free_urb(urb_t* urb) +{ + kfree(urb); +} +/*-------------------------------------------------------------------*/ +int usb_submit_urb(urb_t *urb) +{ + if(urb && urb->dev) + return urb->dev->bus->op->submit_urb(urb); + else + return -1; +} + +/*-------------------------------------------------------------------*/ +int usb_unlink_urb(urb_t *urb) +{ + if(urb && urb->dev) + return urb->dev->bus->op->unlink_urb(urb); + else + return -1; +} +/*-------------------------------------------------------------------* + * COMPLETION HANDLERS * + *-------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------* + * completion handler for compatibility wrappers (sync control/bulk) * + *-------------------------------------------------------------------*/ +static void usb_api_blocking_completion(urb_t *urb) +{ + api_wrapper_data *awd = (api_wrapper_data *)urb->context; + + if (waitqueue_active(awd->wakeup)) + wake_up(awd->wakeup); +#if 0 + else + dbg(KERN_DEBUG MODSTR "(blocking_completion): waitqueue empty!\n"); + // even occurs if urb was unlinked by timeout... +#endif +} + +/*-------------------------------------------------------------------* + * completion handler for compatibility wrappers (async bulk) * + *-------------------------------------------------------------------*/ +static void usb_api_async_completion(urb_t *urb) +{ + api_wrapper_data *awd=(api_wrapper_data*)urb->context; + + if (awd->handler) + awd->handler(urb->status,urb->transfer_buffer,urb->actual_length,awd->stuff); + } + +/*-------------------------------------------------------------------* + * COMPATIBILITY STUFF * + *-------------------------------------------------------------------*/ + +// Starts urb and waits for completion or timeout +static int usb_start_wait_urb(urb_t *urb, int timeout, unsigned long* rval) +{ + DECLARE_WAITQUEUE(wait, current); + DECLARE_WAIT_QUEUE_HEAD(wqh); + api_wrapper_data awd; + int status; + + awd.wakeup=&wqh; + awd.handler=0; + init_waitqueue_head(&wqh); + current->state = TASK_UNINTERRUPTIBLE; + add_wait_queue(&wqh, &wait); + urb->context=&awd; + status=usb_submit_urb(urb); + if (status) { + // something went wrong + usb_free_urb(urb); + remove_wait_queue(&wqh, &wait); + return status; + } + + if (urb->status == -EINPROGRESS) + status=schedule_timeout(timeout); // ZZzzzz.... + else + status = 1; + + remove_wait_queue(&wqh, &wait); + + if (!status) { + // timeout + printk(KERN_DEBUG MODSTR"usb_control/bulk_msg: timeout\n"); + usb_unlink_urb(urb); // remove urb safely + status=-ETIMEDOUT; + } + else + status=urb->status; + + if (rval) + *rval=urb->actual_length; + + usb_free_urb(urb); + return status; +} + +/*-------------------------------------------------------------------*/ +// returns status (negative) are length (positive) +int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, + devrequest *cmd, void *data, int len, int timeout) +{ + urb_t *urb; + int retv; + unsigned long length; + + urb=usb_alloc_urb(0); + if (!urb) + return -ENOMEM; + + FILL_CONTROL_URB(urb, usb_dev, pipe, (unsigned char*)cmd, data, len, /* build urb */ + (usb_complete_t)usb_api_blocking_completion,0); + + retv=usb_start_wait_urb(urb,timeout, &length); + if (retv < 0) + return retv; + else + return length; + +} +/*-------------------------------------------------------------------*/ +int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, + __u16 value, __u16 index, void *data, __u16 size, int timeout) +{ + devrequest dr; + + dr.requesttype = requesttype; + dr.request = request; + dr.value = cpu_to_le16p(&value); + dr.index = cpu_to_le16p(&index); + dr.length = cpu_to_le16p(&size); + //dbg(KERN_DEBUG MODSTR"usb_control_msg\n"); + return usb_internal_control_msg(dev, pipe, &dr, data, size, timeout); +} + +/*-------------------------------------------------------------------*/ +/* compatibility wrapper, builds bulk urb, and waits for completion */ +/* synchronous behavior */ + +int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, + void *data, int len, unsigned long *rval, int timeout) +{ + urb_t *urb; + + if (len < 0) + return -EINVAL; + + urb=usb_alloc_urb(0); + if (!urb) + return -ENOMEM; + + FILL_BULK_URB(urb,usb_dev,pipe,(unsigned char*)data,len, /* build urb */ + (usb_complete_t)usb_api_blocking_completion,0); + + return usb_start_wait_urb(urb,timeout,rval); +} +/*-------------------------------------------------------------------*/ + +void *usb_request_bulk(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, void *data, int len, void *dev_id) +{ + urb_t *urb; + DECLARE_WAITQUEUE(wait, current); + DECLARE_WAIT_QUEUE_HEAD(wqh); + api_wrapper_data *awd; + + if (!(urb=usb_alloc_urb(0))) + return NULL; + if (!(awd = kmalloc(sizeof(api_wrapper_data), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL))) { + kfree(urb); + return NULL; + } + + /* build urb */ + FILL_BULK_URB(urb, dev, pipe, data, len, (usb_complete_t)usb_api_async_completion, awd); + + awd->handler=handler; + awd->stuff=dev_id; + if (usb_submit_urb(urb) < 0) { + kfree(awd); + kfree(urb); + return NULL; + } + return urb; +} + +// compatibility wrapper. Remove urb only if it is called before the +// transaction's completion interrupt. If called from within the +// completion handler (urb->completed==1), it does nothing, since the +// qh is already removed + +int usb_terminate_bulk(struct usb_device *dev, void *first) +{ + urb_t *urb=(urb_t*)first; + dbg(KERN_DEBUG MODSTR"usb_terminate_bulk: urb:%p\n",urb); + if (!urb) // none found? there is nothing to remove! + return -ENODEV; + + usb_unlink_urb(urb); + kfree(urb->context); + kfree(urb); + return USB_ST_NOERROR; +} + +/* + * usb_release_bandwidth(): + * + * called to release an interrupt pipe's bandwidth (in microseconds) + */ +void usb_release_bandwidth(struct usb_device *dev, int bw_alloc) +{ + dev->bus->bandwidth_allocated -= bw_alloc; + dev->bus->bandwidth_int_reqs--; + PRINTD ("bw_alloc reduced to %d for %d requesters", + dev->bus->bandwidth_allocated, + dev->bus->bandwidth_int_reqs + + dev->bus->bandwidth_isoc_reqs); +} + +static void irq_callback(urb_t *urb) +{ + struct irq_wrapper_data *wd = (struct irq_wrapper_data *)urb->context; + + if (!wd->handler) + return; +#if 0 // verbose... + if (!wd->handler(urb->status, urb->transfer_buffer, urb->actual_length, wd->context)) + printk(KERN_ERR "usb: legacy irq callback returned 0!!!\n"); +#else + wd->handler(urb->status, urb->transfer_buffer, urb->actual_length, wd->context); +#endif +} + +int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id, void **handle) +{ + long bustime; + int ret; + struct irq_wrapper_data *wd; + urb_t *urb; + unsigned int maxsze = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + *handle = NULL; + + //printk("irq: dev:%p pipe:%08X handler:%p period:%d dev_id:%p max:%d\n", dev, pipe, handler, period, dev_id, maxsze); + + /* Check host controller's bandwidth for this int. request. */ + bustime = calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe), 0, + usb_maxpacket(dev, pipe, usb_pipeout(pipe))); + bustime = NS_TO_US(bustime); /* work in microseconds */ + if (check_bandwidth_alloc (dev->bus->bandwidth_allocated, bustime)) + return -EUSERS; // no bandwidth left + + if (!maxsze || !usb_pipeint(pipe)) + return -EINVAL; + + if (!(urb = usb_alloc_urb(0))) + return -ENOMEM; + + if (!(wd = kmalloc(sizeof(struct irq_wrapper_data), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL))) { + kfree(urb); + return -ENOMEM; + } + if (!(urb->transfer_buffer = kmalloc(maxsze, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL))) { + kfree(urb); + kfree(wd); + return -ENOMEM; + } + wd->handler=handler; + wd->context=dev_id; + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer_length = urb->actual_length = maxsze; + urb->interval = period; + urb->context = wd; + urb->complete = irq_callback; + if ((ret = usb_submit_urb(urb)) < 0) { + kfree(wd); + kfree(urb->transfer_buffer); + kfree(urb); + return ret; + } + *handle = urb; + + /* Claim the USB bandwidth if no error. */ + if (!ret) { + dev->bus->bandwidth_allocated += bustime; + dev->bus->bandwidth_int_reqs++; + PRINTD ("bw_alloc bumped to %d for %d requesters", + dev->bus->bandwidth_allocated, + dev->bus->bandwidth_int_reqs + + dev->bus->bandwidth_isoc_reqs); + } + + return ret; +} + +int usb_release_irq(struct usb_device *dev, void *handle, unsigned int pipe) +{ + long bustime; + int err; + urb_t *urb = (urb_t*)handle; + + if (!urb) + return -EBADF; + err=usb_unlink_urb(urb); + kfree(urb->context); + kfree(urb->transfer_buffer); + kfree(urb); + /* Return the USB bandwidth if no error. */ + if (!err) { + bustime = calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe), 0, + usb_maxpacket(dev, pipe, usb_pipeout(pipe))); + bustime = NS_TO_US(bustime); /* work in microseconds */ + usb_release_bandwidth(dev, bustime); + } + return err; +} + +/* + * usb_get_current_frame_number() + * + * returns the current frame number for the parent USB bus/controller + * of the given USB device. + */ +int usb_get_current_frame_number(struct usb_device *usb_dev) +{ + return usb_dev->bus->op->get_frame_number (usb_dev); +} +/*-------------------------------------------------------------------*/ static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor *endpoint, unsigned char *buffer, int size) { struct usb_descriptor_header *header; @@ -485,8 +847,12 @@ return parsed; } - memcpy(endpoint, buffer, USB_DT_ENDPOINT_SIZE); - le16_to_cpus(&endpoint->wMaxPacketSize); + if (header->bLength == USB_DT_ENDPOINT_AUDIO_SIZE) + memcpy(endpoint, buffer, USB_DT_ENDPOINT_AUDIO_SIZE); + else + memcpy(endpoint, buffer, USB_DT_ENDPOINT_SIZE); + + le16_to_cpus(&endpoint->wMaxPacketSize); buffer += header->bLength; size -= header->bLength; @@ -505,20 +871,21 @@ } /* If we find another descriptor which is at or below us */ - /* in the descriptor heirarchy then we're done */ + /* in the descriptor heirarchy then we're done */ if ((header->bDescriptorType == USB_DT_ENDPOINT) || (header->bDescriptorType == USB_DT_INTERFACE) || (header->bDescriptorType == USB_DT_CONFIG) || (header->bDescriptorType == USB_DT_DEVICE)) break; + printk(KERN_INFO "usb: skipping descriptor 0x%X\n", + header->bDescriptorType); numskipped++; buffer += header->bLength; size -= header->bLength; parsed += header->bLength; } - if (numskipped) printk(KERN_INFO "usb: skipped %d class/vendor specific endpoint descriptors\n", numskipped); @@ -532,6 +899,7 @@ } endpoint->extra = kmalloc(len, GFP_KERNEL); + if (!endpoint->extra) { printk(KERN_ERR "Couldn't allocate memory for endpoint extra descriptors\n"); endpoint->extralen = 0; @@ -556,6 +924,7 @@ interface->max_altsetting = USB_ALTSETTINGALLOC; interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL); + if (!interface->altsetting) { printk(KERN_ERR "couldn't kmalloc interface->altsetting\n"); return -1; @@ -634,6 +1003,7 @@ ifp->extralen = 0; } else { ifp->extra = kmalloc(len, GFP_KERNEL); + if (!ifp->extra) { printk(KERN_ERR "couldn't allocate memory for interface extra descriptors\n"); ifp->extralen = 0; @@ -694,7 +1064,7 @@ return parsed; } -static int usb_parse_configuration(struct usb_device *dev, struct usb_config_descriptor *config, char *buffer) +int usb_parse_configuration(struct usb_device *dev, struct usb_config_descriptor *config, char *buffer) { int i; int retval; @@ -702,7 +1072,7 @@ struct usb_descriptor_header *header; memcpy(config, buffer, USB_DT_INTERFACE_SIZE); - + usb_show_config_descriptor(config); le16_to_cpus(&config->wTotalLength); size = config->wTotalLength; @@ -714,6 +1084,7 @@ config->interface = (struct usb_interface *) kmalloc(config->bNumInterfaces * sizeof(struct usb_interface), GFP_KERNEL); + printk("kmalloc IF %p, numif %i\n",config->interface,config->bNumInterfaces); if (!config->interface) { printk(KERN_WARNING "usb: out of memory\n"); return -1; @@ -754,7 +1125,7 @@ void usb_destroy_configuration(struct usb_device *dev) { - int c, i, j; + int c, i, j, k; if (!dev->config) return; @@ -763,24 +1134,34 @@ struct usb_config_descriptor *cf = &dev->config[c]; if (!cf->interface) - break; + break; for (i = 0; i < cf->bNumInterfaces; i++) { struct usb_interface *ifp = &cf->interface[i]; - + if (!ifp->altsetting) - break; + break; for (j = 0; j < ifp->num_altsetting; j++) { struct usb_interface_descriptor *as = &ifp->altsetting[j]; + + if(as->extra) { + kfree(as->extra); + } if (!as->endpoint) break; - + + for(k = 0; k < as->bNumEndpoints; k++) { + if(as->endpoint[k].extra) { + kfree(as->endpoint[k].extra); + } + } kfree(as->endpoint); } + kfree(ifp->altsetting); } kfree(cf->interface); @@ -815,16 +1196,16 @@ printk("usbcore: USB disconnect on device %d\n", dev->devnum); - if (dev->actconfig) { - for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { - struct usb_interface *interface = &dev->actconfig->interface[i]; + if (dev->actconfig) { + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { + struct usb_interface *interface = &dev->actconfig->interface[i]; struct usb_driver *driver = interface->driver; - if (driver) { - driver->disconnect(dev, interface->private_data); + if (driver) { + driver->disconnect(dev, interface->private_data); usb_driver_release_interface(driver, interface); - } - } - } + } + } + } /* Free up all the children.. */ for (i = 0; i < USB_MAXCHILDREN; i++) { @@ -838,8 +1219,9 @@ /* Free up the device itself, including its device number */ if (dev->devnum > 0) clear_bit(dev->devnum, &dev->bus->devmap.devicemap); - + usb_free_dev(dev); + } /* @@ -851,9 +1233,8 @@ void usb_connect(struct usb_device *dev) { int devnum; - - dev->descriptor.bMaxPacketSize0 = 8; /* Start off at 8 bytes */ - + // FIXME needs locking for SMP!! + dev->descriptor.bMaxPacketSize0 = 8; /* Start off at 8 bytes */ devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1); if (devnum < 128) { set_bit(devnum, dev->bus->devmap.devicemap); @@ -865,10 +1246,14 @@ * These are the actual routines to send * and receive control messages. */ + +#define GET_TIMEOUT 3 +#define SET_TIMEOUT 3 + int usb_set_address(struct usb_device *dev) { return usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS, - 0, dev->devnum, 0, NULL, 0, HZ); + 0, dev->devnum, 0, NULL, 0, HZ * GET_TIMEOUT); } int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size) @@ -879,8 +1264,8 @@ while (i--) { if ((result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, - (type << 8) + index, 0, buf, size, HZ)) >= 0 || - result == USB_ST_STALL) + (type << 8) + index, 0, buf, size, HZ * GET_TIMEOUT)) >= 0 || + result == -EPIPE) break; } return result; @@ -890,7 +1275,7 @@ { return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, - (USB_DT_STRING << 8) + index, langid, buf, size, HZ); + (USB_DT_STRING << 8) + index, langid, buf, size, HZ * GET_TIMEOUT); } int usb_get_device_descriptor(struct usb_device *dev) @@ -909,7 +1294,7 @@ int usb_get_status(struct usb_device *dev, int type, int target, void *data) { return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, data, 2, HZ); + USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, data, 2, HZ * GET_TIMEOUT); } int usb_get_protocol(struct usb_device *dev) @@ -919,7 +1304,7 @@ if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_PROTOCOL, USB_DIR_IN | USB_RT_HIDD, - 0, 1, &type, 1, HZ)) < 0) + 0, 1, &type, 1, HZ * GET_TIMEOUT)) < 0) return ret; return type; @@ -928,7 +1313,7 @@ int usb_set_protocol(struct usb_device *dev, int protocol) { return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_SET_PROTOCOL, USB_RT_HIDD, protocol, 1, NULL, 0, HZ); + USB_REQ_SET_PROTOCOL, USB_RT_HIDD, protocol, 1, NULL, 0, HZ * SET_TIMEOUT); } /* keyboards want a nonzero duration according to HID spec, but @@ -936,7 +1321,7 @@ int usb_set_idle(struct usb_device *dev, int duration, int report_id) { return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_IDLE, - USB_RT_HIDD, (duration << 8) | report_id, 1, NULL, 0, HZ); + USB_RT_HIDD, (duration << 8) | report_id, 1, NULL, 0, HZ * SET_TIMEOUT); } static void usb_set_maxpacket(struct usb_device *dev) @@ -979,7 +1364,7 @@ */ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CLEAR_FEATURE, USB_RT_ENDPOINT, 0, endp, NULL, 0, HZ); + USB_REQ_CLEAR_FEATURE, USB_RT_ENDPOINT, 0, endp, NULL, 0, HZ * SET_TIMEOUT); /* don't clear if failed */ if (result < 0) @@ -987,12 +1372,12 @@ result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_ENDPOINT, 0, endp, - &status, sizeof(status), HZ); + &status, sizeof(status), HZ * SET_TIMEOUT); if (result < 0) return result; if (status & 1) - return USB_ST_STALL; /* still halted */ + return -EPIPE; /* still halted */ usb_endpoint_running(dev, endp & 0x0f, usb_endpoint_out(endp)); @@ -1005,14 +1390,26 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) { - int ret; + struct usb_interface *iface = NULL; + int ret, i; + + for (i=0; iactconfig->bNumInterfaces; i++) { + if (dev->actconfig->interface[i].altsetting->bInterfaceNumber == interface) { + iface = &dev->actconfig->interface[i]; + break; + } + } + if (!iface) { + printk(KERN_INFO "usb: selecting invalid interface %d\n", interface); + return -EINVAL; + } if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_INTERFACE, USB_RT_INTERFACE, alternate, - interface, NULL, 0, HZ)) < 0) + interface, NULL, 0, HZ * 5)) < 0) return ret; - dev->actconfig->interface[interface].act_altsetting = alternate; + iface->act_altsetting = alternate; usb_set_maxpacket(dev); return 0; } @@ -1034,7 +1431,7 @@ } if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, HZ)) < 0) + USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, HZ * SET_TIMEOUT)) < 0) return ret; dev->actconfig = cp; @@ -1049,11 +1446,12 @@ { return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_REPORT, USB_DIR_IN | USB_RT_HIDD, - (type << 8) + id, index, buf, size, HZ); + (type << 8) + id, index, buf, size, HZ * GET_TIMEOUT); } int usb_get_configuration(struct usb_device *dev) { + int result; unsigned int cfgno; unsigned char buffer[8]; unsigned char *bigbuffer; @@ -1076,14 +1474,14 @@ sizeof(struct usb_config_descriptor)); for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) { - int result; + /* We grab the first 8 bytes so we know how long the whole */ /* configuration is */ result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8); if (result < 0) { printk(KERN_ERR "usb: unable to get descriptor\n"); - return result; + goto err; } /* Get the full buffer */ @@ -1092,7 +1490,8 @@ bigbuffer = kmalloc(desc->wTotalLength, GFP_KERNEL); if (!bigbuffer) { printk(KERN_ERR "unable to allocate memory for configuration descriptors\n"); - return USB_ST_INTERNALERROR; + result=-ENOMEM; + goto err; } /* Now that we know the length, get the whole thing */ @@ -1100,19 +1499,24 @@ if (result < 0) { printk(KERN_ERR "couldn't get all of config descriptors\n"); kfree(bigbuffer); - return result; - } - + goto err; + } result = usb_parse_configuration(dev, &dev->config[cfgno], bigbuffer); kfree(bigbuffer); if (result > 0) printk(KERN_INFO "usb: descriptor data left\n"); else if (result < 0) - return -1; + { + result=-1; + goto err; + } } return 0; + err: + dev->descriptor.bNumConfigurations=cfgno; + return result; } char *usb_string(struct usb_device *dev, int index) @@ -1178,7 +1582,7 @@ printk(KERN_INFO "USB new device connect, assigned device number %d\n", dev->devnum); - + dev->maxpacketsize = 0; /* Default to 8 byte max packet size */ dev->epmaxpacketin [0] = 8; dev->epmaxpacketout[0] = 8; @@ -1190,6 +1594,7 @@ err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8); if (err < 0) { printk(KERN_ERR "usbcore: USB device not responding, giving up (error=%d)\n", err); + clear_bit(addr, &dev->bus->devmap.devicemap); dev->devnum = -1; return 1; } @@ -1206,8 +1611,10 @@ dev->devnum = addr; err = usb_set_address(dev); + if (err < 0) { printk(KERN_ERR "usbcore: USB device not accepting new address (error=%d)\n", err); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); dev->devnum = -1; return 1; } @@ -1216,14 +1623,17 @@ err = usb_get_device_descriptor(dev); if (err < 0) { - printk(KERN_ERR "usbcore: unable to get device descriptor (error=%d)\n", err); + printk(KERN_ERR "usbcore: unable to get device descriptor (error=%d)\n",err); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); dev->devnum = -1; return 1; } - err = usb_get_configuration(dev); + err=usb_get_configuration(dev); + if (err < 0) { printk(KERN_ERR "usbcore: unable to get configuration (error=%d)\n", err); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); dev->devnum = -1; return 1; } @@ -1245,189 +1655,11 @@ proc_usb_add_device(dev); /* find drivers willing to handle this device */ - usb_find_drivers(dev); + usb_find_drivers(dev); return 0; } -int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout) -{ - devrequest dr; - int ret; - - dr.requesttype = requesttype; - dr.request = request; - dr.value = cpu_to_le16p(&value); - dr.index = cpu_to_le16p(&index); - dr.length = cpu_to_le16p(&size); - - ret = dev->bus->op->control_msg(dev, pipe, &dr, data, size, timeout); - - if (ret < 0 && usb_debug) { - unsigned char *p = (unsigned char *)&dr; - - printk(KERN_DEBUG "Failed control msg - r:%02X rt:%02X v:%04X i:%04X s:%04X - ret: %d\n", - request, requesttype, value, index, size, ret); - printk(KERN_DEBUG " %02X %02X %02X %02X %02X %02X %02X %02X\n", - p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); - } - - return ret; -} - -int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id, void **handle) -{ - long bustime; - int ret; - - *handle = NULL; - - /* Check host controller's bandwidth for this int. request. */ - bustime = calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe), 0, - usb_maxpacket(dev, pipe, usb_pipeout(pipe))); - bustime = NS_TO_US(bustime); /* work in microseconds */ - if (check_bandwidth_alloc (dev->bus->bandwidth_allocated, bustime)) - return (USB_ST_BANDWIDTH_ERROR); - - ret = dev->bus->op->request_irq(dev, pipe, handler, period, dev_id, handle, bustime); - - /* Claim the USB bandwidth if no error. */ - if (!ret) { - dev->bus->bandwidth_allocated += bustime; - dev->bus->bandwidth_int_reqs++; - PRINTD ("bw_alloc bumped to %d for %d requesters", - dev->bus->bandwidth_allocated, - dev->bus->bandwidth_int_reqs + - dev->bus->bandwidth_isoc_reqs); - } - - return ret; -} - -int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, void *data, int len, unsigned long *rval, int timeout) -{ - return dev->bus->op->bulk_msg(dev, pipe, data, len, rval, timeout); -} - -void *usb_request_bulk(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, void *data, int len, void *dev_id) -{ - return dev->bus->op->request_bulk(dev, pipe, handler, data, len, dev_id); -} - -int usb_terminate_bulk(struct usb_device *dev, void *first) -{ - return dev->bus->op->terminate_bulk(dev, first); -} - -/* - * usb_release_bandwidth(): - * - * called to release an interrupt pipe's bandwidth (in microseconds) - */ -void usb_release_bandwidth(struct usb_device *dev, int bw_alloc) -{ - dev->bus->bandwidth_allocated -= bw_alloc; - dev->bus->bandwidth_int_reqs--; - PRINTD ("bw_alloc reduced to %d for %d requesters", - dev->bus->bandwidth_allocated, - dev->bus->bandwidth_int_reqs + - dev->bus->bandwidth_isoc_reqs); -} - -int usb_release_irq(struct usb_device *dev, void *handle, unsigned int pipe) -{ - long bustime; - int err; - - err = dev->bus->op->release_irq(dev, handle); - - /* Return the USB bandwidth if no error. */ - if (!err) { - bustime = calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe), 0, - usb_maxpacket(dev, pipe, usb_pipeout(pipe))); - bustime = NS_TO_US(bustime); /* work in microseconds */ - usb_release_bandwidth(dev, bustime); - } - - return err; -} - -/* - * usb_get_current_frame_number() - * - * returns the current frame number for the parent USB bus/controller - * of the given USB device. - */ -int usb_get_current_frame_number(struct usb_device *usb_dev) -{ - return usb_dev->bus->op->get_frame_number (usb_dev); -} - -int usb_init_isoc(struct usb_device *usb_dev, - unsigned int pipe, - int frame_count, - void *context, - struct usb_isoc_desc **isocdesc) -{ - long bustime; - int err; - - if (frame_count <= 0) - return -EINVAL; - - /* Check host controller's bandwidth for this Isoc. request. */ - /* TBD: some way to factor in frame_spacing ??? */ - bustime = calc_bus_time (0, usb_pipein(pipe), 1, - usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe))); - bustime = NS_TO_US(bustime) / frame_count; /* work in microseconds */ - if (check_bandwidth_alloc (usb_dev->bus->bandwidth_allocated, bustime)) - return USB_ST_BANDWIDTH_ERROR; - - err = usb_dev->bus->op->init_isoc (usb_dev, pipe, frame_count, context, isocdesc); - - /* Claim the USB bandwidth if no error. */ - if (!err) { - usb_dev->bus->bandwidth_allocated += bustime; - usb_dev->bus->bandwidth_isoc_reqs++; - PRINTD ("bw_alloc bumped to %d for %d requesters", - usb_dev->bus->bandwidth_allocated, - usb_dev->bus->bandwidth_int_reqs + - usb_dev->bus->bandwidth_isoc_reqs); - } - - return err; -} - -void usb_free_isoc(struct usb_isoc_desc *isocdesc) -{ - long bustime; - - /* Return the USB bandwidth. */ - bustime = calc_bus_time (0, usb_pipein(isocdesc->pipe), 1, - usb_maxpacket(isocdesc->usb_dev, isocdesc->pipe, - usb_pipeout(isocdesc->pipe))); - bustime = NS_TO_US(bustime) / isocdesc->frame_count; - isocdesc->usb_dev->bus->bandwidth_allocated -= bustime; - isocdesc->usb_dev->bus->bandwidth_isoc_reqs--; - PRINTD ("bw_alloc reduced to %d for %d requesters", - isocdesc->usb_dev->bus->bandwidth_allocated, - isocdesc->usb_dev->bus->bandwidth_int_reqs + - isocdesc->usb_dev->bus->bandwidth_isoc_reqs); - - isocdesc->usb_dev->bus->op->free_isoc (isocdesc); -} - -int usb_run_isoc(struct usb_isoc_desc *isocdesc, - struct usb_isoc_desc *pr_isocdesc) -{ - return isocdesc->usb_dev->bus->op->run_isoc (isocdesc, pr_isocdesc); -} - -int usb_kill_isoc(struct usb_isoc_desc *isocdesc) -{ - return isocdesc->usb_dev->bus->op->kill_isoc (isocdesc); -} - static int usb_open(struct inode * inode, struct file * file) { int minor = MINOR(inode->i_rdev); @@ -1442,16 +1674,16 @@ } static struct file_operations usb_fops = { - NULL, /* seek */ + NULL, /* seek */ NULL, /* read */ NULL, /* write */ NULL, /* readdir */ NULL, /* poll */ NULL, /* ioctl */ NULL, /* mmap */ - usb_open, + usb_open, NULL, /* flush */ - NULL /* release */ + NULL /* release */ }; void usb_major_init(void) @@ -1519,15 +1751,16 @@ EXPORT_SYMBOL(usb_get_configuration); EXPORT_SYMBOL(usb_set_configuration); +EXPORT_SYMBOL(usb_get_current_frame_number); + +EXPORT_SYMBOL(usb_alloc_urb); +EXPORT_SYMBOL(usb_free_urb); +EXPORT_SYMBOL(usb_submit_urb); +EXPORT_SYMBOL(usb_unlink_urb); + EXPORT_SYMBOL(usb_control_msg); EXPORT_SYMBOL(usb_request_irq); EXPORT_SYMBOL(usb_release_irq); EXPORT_SYMBOL(usb_bulk_msg); EXPORT_SYMBOL(usb_request_bulk); EXPORT_SYMBOL(usb_terminate_bulk); - -EXPORT_SYMBOL(usb_get_current_frame_number); -EXPORT_SYMBOL(usb_init_isoc); -EXPORT_SYMBOL(usb_free_isoc); -EXPORT_SYMBOL(usb_run_isoc); -EXPORT_SYMBOL(usb_kill_isoc); diff -u --recursive --new-file v2.3.33/linux/drivers/usb/usb.h linux/drivers/usb/usb.h --- v2.3.33/linux/drivers/usb/usb.h Thu Nov 18 20:25:37 1999 +++ linux/drivers/usb/usb.h Thu Dec 16 01:27:42 1999 @@ -3,6 +3,8 @@ #include #include +#include + /* USB constants */ @@ -126,14 +128,14 @@ __u16 value; __u16 index; __u16 length; - __u32 timeout; /* in milliseconds */ - void *data; + __u32 timeout; /* in milliseconds */ + void *data; }; struct usb_proc_bulktransfer { unsigned int ep; unsigned int len; - unsigned int timeout; /* in milliseconds */ + unsigned int timeout; /* in milliseconds */ void *data; }; @@ -158,13 +160,16 @@ unsigned int altsetting; }; -#define USB_PROC_CONTROL _IOWR('U', 0, struct usb_proc_ctrltransfer) -#define USB_PROC_BULK _IOWR('U', 2, struct usb_proc_bulktransfer) -#define USB_PROC_OLD_CONTROL _IOWR('U', 0, struct usb_proc_old_ctrltransfer) -#define USB_PROC_OLD_BULK _IOWR('U', 2, struct usb_proc_old_bulktransfer) -#define USB_PROC_RESETEP _IOR('U', 3, unsigned int) -#define USB_PROC_SETINTERFACE _IOR('U', 4, struct usb_proc_setinterface) -#define USB_PROC_SETCONFIGURATION _IOR('U', 5, unsigned int) +#define USB_PROC_CONTROL _IOWR('U', 0, struct usb_proc_ctrltransfer) +#define USB_PROC_BULK _IOWR('U', 2, struct usb_proc_bulktransfer) +#define USB_PROC_OLD_CONTROL _IOWR('U', 0, struct usb_proc_old_ctrltransfer) +#define USB_PROC_OLD_BULK _IOWR('U', 2, struct usb_proc_old_bulktransfer) +#define USB_PROC_RESETEP _IOR('U', 3, unsigned int) +#define USB_PROC_SETINTERFACE _IOR('U', 4, struct usb_proc_setinterface) +#define USB_PROC_SETCONFIGURATION _IOR('U', 5, unsigned int) + + + #ifdef __KERNEL__ @@ -184,6 +189,42 @@ extern void usb_hub_cleanup(void); extern void usb_mouse_cleanup(void); +/* for 2.2-kernels */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +typedef struct wait_queue wait_queue_t; + +typedef struct wait_queue *wait_queue_head_t; +#define DECLARE_WAITQUEUE(wait, current) \ + struct wait_queue wait = { current, NULL } +#define DECLARE_WAIT_QUEUE_HEAD(wait)\ + wait_queue_head_t wait + +#define init_waitqueue_head(x) *x=NULL +#define init_MUTEX(x) *(x)=MUTEX +#define DECLARE_MUTEX(name) struct semaphore name=MUTEX +#define DECLARE_MUTEX_LOCKED(name) struct semaphore name=MUTEX_LOCKED + + +#define __set_current_state(state_value) \ + do { current->state = state_value; } while (0) +#ifdef __SMP__ +#define set_current_state(state_value) \ + set_mb(current->state, state_value) +#else +#define set_current_state(state_value) \ + __set_current_state(state_value) +#endif + +#endif // 2.2.x + + static __inline__ void wait_ms(unsigned int ms) { current->state = TASK_UNINTERRUPTIBLE; @@ -198,34 +239,30 @@ __u16 length; } devrequest __attribute__ ((packed)); -/* - * Status codes (these [used to] follow OHCI controllers condition codes) - */ -#define USB_ST_NOERROR 0 -#define USB_ST_CRC -1 -#define USB_ST_BITSTUFF -2 -#define USB_ST_DTMISMATCH -3 /* data toggle mismatch */ -#define USB_ST_STALL -4 -#define USB_ST_NORESPONSE -5 /* device not responding/handshaking */ -#define USB_ST_PIDCHECK -6 /* Check bits on PID failed */ -#define USB_ST_PIDUNDEF -7 /* PID unexpected/undefined */ -#define USB_ST_DATAOVERRUN -8 -#define USB_ST_DATAUNDERRUN -9 -#define USB_ST_RESERVED1 -10 -#define USB_ST_RESERVED2 -11 -#define USB_ST_BUFFEROVERRUN -12 -#define USB_ST_BUFFERUNDERRUN -13 -#define USB_ST_RESERVED3 -14 -#define USB_ST_RESERVED4 -15 - -/* internal errors */ -#define USB_ST_REMOVED -100 -#define USB_ST_TIMEOUT -101 -#define USB_ST_INTERNALERROR -200 -#define USB_ST_NOTSUPPORTED -201 -#define USB_ST_BANDWIDTH_ERROR -202 -#define USB_ST_NOCHANGE -203 +/* USB-status codes: + * USB_ST* maps to -E* and should go away in the future +*/ +#define USB_ST_NOERROR 0 +#define USB_ST_CRC (-EILSEQ) +#define USB_ST_BITSTUFF (-EPROTO) +#define USB_ST_NORESPONSE (-ETIMEDOUT) /* device not responding/handshaking */ +#define USB_ST_DATAOVERRUN (-EOVERFLOW) +#define USB_ST_DATAUNDERRUN (-EREMOTEIO) +#define USB_ST_BUFFEROVERRUN (-ECOMM) +#define USB_ST_BUFFERUNDERRUN (-ENOSR) +#define USB_ST_INTERNALERROR (-EPROTO) /* unknown error */ +#define USB_ST_SHORT_PACKET (-EREMOTEIO) +#define USB_ST_PARTIAL_ERROR (-EXDEV) /* ISO transfer only partially completed */ +#define USB_ST_URB_KILLED (-ENOENT) /* URB canceled by user */ +#define USB_ST_URB_PENDING (-EINPROGRESS) +#define USB_ST_REMOVED (-ENODEV) /* device not existing or removed */ +#define USB_ST_TIMEOUT (-ETIMEDOUT) /* communication timed out, also in urb->status**/ +#define USB_ST_NOTSUPPORTED (-ENOSYS) +#define USB_ST_BANDWIDTH_ERROR (-ENOSPC) /* too much bandwidth used */ +#define USB_ST_URB_INVALID_ERROR (-EINVAL) /* invalid value/transfer type */ +#define USB_ST_URB_REQUEST_ERROR (-ENXIO) /* invalid endpoint */ +#define USB_ST_STALL (-EPIPE) /* pipe stalled, also in urb->status*/ /* * USB device number allocation bitmap. There's one bitmap @@ -249,8 +286,8 @@ /* Everything but the endpoint maximums are aribtrary */ #define USB_MAXCONFIG 8 -#define USB_ALTSETTINGALLOC 4 -#define USB_MAXALTSETTING 128 /* Hard limit */ +#define USB_ALTSETTINGALLOC 4 +#define USB_MAXALTSETTING 128 /* Hard limit */ #define USB_MAXINTERFACES 32 #define USB_MAXENDPOINTS 32 @@ -289,7 +326,7 @@ __u8 bRefresh; __u8 bSynchAddress; - unsigned char *extra; /* Extra descriptors */ + unsigned char *extra; /* Extra descriptors */ int extralen; } __attribute__ ((packed)); @@ -321,7 +358,7 @@ __u8 bInterfaceProtocol; __u8 iInterface; - struct usb_endpoint_descriptor *endpoint; + struct usb_endpoint_descriptor *endpoint; unsigned char *extra; int extralen; @@ -332,8 +369,8 @@ int act_altsetting; /* active alternate setting */ int num_altsetting; /* number of alternate settings */ - int max_altsetting; /* total memory allocated */ - + int max_altsetting; /* total memory allocated */ + struct usb_driver *driver; /* driver */ void *private_data; }; @@ -391,102 +428,116 @@ */ typedef int (*usb_device_irq)(int, void *, int, void *); -/* - * Isoc. support additions - */ -#define START_FRAME_FUDGE 3 +/* -------------------------------------------------------------------------------------* + * New USB Structures * + * -------------------------------------------------------------------------------------*/ -#define USB_WRAP_FRAMENR(x) ((x) & 2047) -/* for start_type: */ -enum { - START_ASAP = 0, - START_ABSOLUTE, - START_RELATIVE -}; -#define START_TYPE_MAX START_RELATIVE +#define USB_DISABLE_SPD 1 +#define USB_ISO_ASAP 2 +#define USB_URB_EARLY_COMPLETE 4 -/* - * Completion/Callback routine returns an enum, - * which tells the interrupt handler what to do - * with the completed frames (TDs). - */ -enum { - CB_CONTINUE = 0, /* OK, remove all TDs; - needs to be 0 to be consistent with - current callback function ret. values */ - CB_REUSE, /* leave descriptors as NULL, not active */ - CB_RESTART, /* leave descriptors as they are, alive */ - CB_ABORT, /* kill this USB transfer request */ - CB_CONT_RUN /* append the isoc_desc at the end of all active isoc_desc */ -}; +typedef struct +{ + unsigned int offset; + unsigned int length; // expected length + unsigned int actual_length; + unsigned int status; +} iso_packet_descriptor_t, *piso_packet_descriptor_t; -struct isoc_frame_desc { - int frame_length; /* may be 0 (i.e., allowed) */ - /* set by driver for OUTs to devices; - * set by USBD for INs from devices, - * after I/O complete */ - unsigned int frame_status; - /* set by USBD after I/O complete */ -}; +struct urb; +typedef void (*usb_complete_t)(struct urb *); -struct usb_isoc_desc { - /* - * The following fields are set by the usb_init_isoc() call. - */ - struct usb_device *usb_dev; - unsigned int pipe; - int frame_count; - void *context; /* driver context (private) ptr */ - int frame_size; - /* - * The following fields are set by the driver between the - * usb_init_isoc() and usb_run_isoc() calls - * (along with the "frames" array for OUTput). - */ - int start_type; - int start_frame; /* optional, depending on start_type */ - int frame_spacing; /* not using (yet?) */ - int callback_frames; /* every # frames + last one */ - /* 0 means no callbacks until IOC on last frame */ - usb_device_irq callback_fn; - void *data; - int buf_size; - struct usb_isoc_desc *prev_isocdesc; /* previous isoc_desc, for CB_CONT_RUN */ +typedef struct urb +{ + void *hcpriv; // private data for host controller + struct list_head urb_list; // list pointer to all active urbs + struct urb* next; // pointer to next URB + struct usb_device *dev; // pointer to associated USB device + unsigned int pipe; // pipe information + int status; // returned status + unsigned int transfer_flags; // USB_DISABLE_SPD | USB_ISO_ASAP | USB_URB_EARLY_COMPLETE + void *transfer_buffer; // associated data buffer + int transfer_buffer_length; // data buffer length + int actual_length; // actual data buffer length + unsigned char* setup_packet; // setup packet (control only) + // + int start_frame; // start frame (iso/irq only) + int number_of_packets; // number of packets in this request (iso/irq only) + int interval; // polling interval (irq only) + int error_count; // number of errors in this transfer (iso only) + // + void *context; // context for completion routine + usb_complete_t complete; // pointer to completion routine + // + iso_packet_descriptor_t iso_frame_desc[0]; +} urb_t, *purb_t; + +#define FILL_CONTROL_URB(a,aa,b,c,d,e,f,g) \ + do {\ + a->dev=aa;\ + a->pipe=b;\ + a->setup_packet=c;\ + a->transfer_buffer=d;\ + a->transfer_buffer_length=e;\ + a->complete=f;\ + a->context=g;\ + } while (0) + +#define FILL_BULK_URB(a,aa,b,c,d,e,f) \ + do {\ + a->dev=aa;\ + a->pipe=b;\ + a->transfer_buffer=c;\ + a->transfer_buffer_length=d;\ + a->complete=e;\ + a->context=f;\ + } while (0) + +#define FILL_INT_URB(a,aa,b,c,d,e,f,g) \ + do {\ + a->dev=aa;\ + a->pipe=b;\ + a->transfer_buffer=c;\ + a->transfer_buffer_length=d;\ + a->complete=e;\ + a->context=f;\ + a->interval=g;\ + a->start_frame=-1;\ + } while (0) + +purb_t usb_alloc_urb(int iso_packets); +void usb_free_urb (purb_t purb); +int usb_submit_urb(purb_t purb); +int usb_unlink_urb(purb_t purb); +int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, void *data, int len, int timeout); +int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *rval, int timeout); + +/*-------------------------------------------------------------------* + * COMPATIBILITY STUFF * + *-------------------------------------------------------------------*/ +typedef struct +{ + wait_queue_head_t *wakeup; - /* - * The following fields are set by the usb_run_isoc() call. - */ - int end_frame; - void *td; /* UHCI or OHCI TD ptr */ - /* - * The following fields are set by the USB HCD interrupt handler - * before calling the driver's callback function. - */ - int total_completed_frames; - int prev_completed_frame; /* from the previous callback */ - int cur_completed_frame; /* from this callback */ - int total_length; /* accumulated */ - int error_count; /* accumulated */ - struct isoc_frame_desc frames [0]; /* variable size: [frame_count] */ + usb_device_irq handler; + void* stuff; + /* more to follow */ +} api_wrapper_data; + +struct irq_wrapper_data { + void *context; + usb_device_irq handler; }; +/* ------------------------------------------------------------------------------------- */ + struct usb_operations { int (*allocate)(struct usb_device *); int (*deallocate)(struct usb_device *); - int (*control_msg)(struct usb_device *, unsigned int, devrequest *, void *, int, int); - int (*bulk_msg)(struct usb_device *, unsigned int, void *, int, unsigned long *, int); - int (*request_irq)(struct usb_device *, unsigned int, usb_device_irq, int, void *, void **, long); - int (*release_irq)(struct usb_device *, void *); - void *(*request_bulk)(struct usb_device *, unsigned int, usb_device_irq, - void *, int, void *); - int (*terminate_bulk)(struct usb_device *, void *); int (*get_frame_number) (struct usb_device *usb_dev); - int (*init_isoc) (struct usb_device *usb_dev, unsigned int pipe, - int frame_count, void *context, struct usb_isoc_desc **isocdesc); - void (*free_isoc) (struct usb_isoc_desc *isocdesc); - int (*run_isoc) (struct usb_isoc_desc *isocdesc, struct usb_isoc_desc *pr_isocdesc); - int (*kill_isoc) (struct usb_isoc_desc *isocdesc); + int (*submit_urb) (struct urb* purb); + int (*unlink_urb) (struct urb* purb); }; /* @@ -510,6 +561,7 @@ int bandwidth_isoc_reqs; /* number of Isoc. requesters */ /* procfs entry */ + int proc_busnum; struct proc_dir_entry *proc_entry; }; @@ -539,7 +591,7 @@ int string_langid; /* language ID for strings */ void *hcpriv; /* Host Controller private data */ - + /* procfs entry */ struct proc_dir_entry *proc_entry; @@ -579,7 +631,6 @@ extern int usb_request_irq(struct usb_device *, unsigned int, usb_device_irq, int, void *, void **); extern int usb_release_irq(struct usb_device *dev, void *handle, unsigned int pipe); -extern int usb_bulk_msg(struct usb_device *, unsigned int, void *, int, unsigned long *, int); extern void *usb_request_bulk(struct usb_device *, unsigned int, usb_device_irq, void *, int, void *); extern int usb_terminate_bulk(struct usb_device *, void *); @@ -590,19 +641,6 @@ extern void usb_destroy_configuration(struct usb_device *dev); int usb_get_current_frame_number (struct usb_device *usb_dev); - -int usb_init_isoc (struct usb_device *usb_dev, - unsigned int pipe, - int frame_count, - void *context, - struct usb_isoc_desc **isocdesc); - -void usb_free_isoc (struct usb_isoc_desc *isocdesc); - -int usb_run_isoc (struct usb_isoc_desc *isocdesc, - struct usb_isoc_desc *pr_isocdesc); - -int usb_kill_isoc (struct usb_isoc_desc *isocdesc); /* * Calling this entity a "pipe" is glorifying it. A USB pipe diff -u --recursive --new-file v2.3.33/linux/drivers/usb/usb_scsi.c linux/drivers/usb/usb_scsi.c --- v2.3.33/linux/drivers/usb/usb_scsi.c Wed Dec 8 14:11:27 1999 +++ linux/drivers/usb/usb_scsi.c Mon Dec 20 14:16:59 1999 @@ -26,7 +26,6 @@ * */ -#include #include #include #include @@ -144,9 +143,8 @@ result = usb_bulk_msg(us->pusb_dev, pipe, buf, this_xfer, &partial, HZ*5); - if (result != 0 || partial != this_xfer) - US_DEBUGP("bulk_msg returned %d xferred %lu/%d\n", - result, partial, this_xfer); + US_DEBUGP("bulk_msg returned %d xferred %lu/%d\n", + result, partial, this_xfer); if (result == USB_ST_STALL) { US_DEBUGP("clearing endpoint halt for pipe %x\n", pipe); @@ -157,7 +155,7 @@ /* we want to retry if the device reported NAK */ if (result == USB_ST_TIMEOUT) { if (partial != this_xfer) { - return 0; /* I do not like this */ + return 0; /* I do not like this */ } if (!maxtry--) break; @@ -244,10 +242,13 @@ { struct us_data *us = (struct us_data *)dev_id; + US_DEBUGP("pop_CBI_irq() called!!\n"); + if (state != USB_ST_REMOVED) { us->ip_data = le16_to_cpup((__u16 *)buffer); - /* US_DEBUGP("Interrupt Status %x\n", us->ip_data); */ + US_DEBUGP("Interrupt Status %x\n", us->ip_data); } + if (us->ip_wanted) { us->ip_wanted = 0; wake_up(&us->ip_waitq); @@ -291,13 +292,13 @@ int retry = 5; int done_start = 0; + /* we'll try this up to 5 times? */ while (retry--) { - if (us->flags & US_FL_FIXED_COMMAND) { memset(cmd, 0, us->fixedlength); - + /* fix some commands */ - + switch (srb->cmnd[0]) { case WRITE_6: case READ_6: @@ -323,37 +324,52 @@ us->mode_xlate = 0; memcpy(cmd, srb->cmnd, srb->cmd_len); break; - } + } /* switch */ + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, USB_TYPE_CLASS | USB_RT_INTERFACE, 0, us->ifnum, cmd, us->fixedlength, HZ*5); - if (!done_start && (us->subclass == US_SC_UFI /*|| us->subclass == US_SC_8070*/) - && cmd[0] == TEST_UNIT_READY && result) { + US_DEBUGP("First usb_control_msg returns %d\n", result); + + /* For UFI, if this is the first time we've sent this TEST_UNIT_READY + * command, we can try again + */ + if (!done_start && (us->subclass == US_SC_UFI) + && (cmd[0] == TEST_UNIT_READY) && (result < 0)) { + /* as per spec try a start command, wait and retry */ + wait_ms(100); done_start++; memset(cmd, 0, sizeof(cmd)); cmd[0] = START_STOP; cmd[4] = 1; /* start */ + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - US_CBI_ADSC, USB_TYPE_CLASS | USB_RT_INTERFACE, + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RT_INTERFACE, 0, us->ifnum, cmd, us->fixedlength, HZ*5); - wait_ms(100); + US_DEBUGP("Next usb_control_msg returns %d\n", result); + + /* allow another retry */ retry++; continue; } - } else { + } else { /* !US_FL_FIXED_COMMAND */ result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, USB_TYPE_CLASS | USB_RT_INTERFACE, 0, us->ifnum, srb->cmnd, srb->cmd_len, HZ*5); } + + /* return an answer if we've got one */ if (/*result != USB_ST_STALL &&*/ result != USB_ST_TIMEOUT) return result; } + /* all done -- return our status */ return result; } @@ -375,9 +391,9 @@ while (retry--) { result = usb_control_msg(us->pusb_dev, usb_rcvctrlpipe(us->pusb_dev,0), - USB_REQ_GET_STATUS, USB_DIR_IN | USB_TYPE_STANDARD | USB_RT_DEVICE, - 0, us->ifnum, - status, sizeof(status), HZ*5); + USB_REQ_GET_STATUS, USB_DIR_IN | + USB_TYPE_STANDARD | USB_RT_DEVICE, + 0, us->ifnum, status, sizeof(status), HZ*5); if (result != USB_ST_TIMEOUT) break; } @@ -398,20 +414,14 @@ /* add interrupt transfer, marked for removal */ us->ip_wanted = 1; - us->irqpipe = usb_rcvintpipe(us->pusb_dev, us->ep_int); - result = usb_request_irq(us->pusb_dev, us->irqpipe, pop_CBI_irq, - IRQ_PERIOD, (void *)us, &us->irq_handle); - if (result) { - US_DEBUGP("usb_request_irq failed (0x%x), No interrupt for CBI\n", - result); - return DID_ABORT << 16; - } + /* go to sleep until we get this interrup */ sleep_on(&us->ip_waitq); -#ifdef REWRITE_PROJECT - usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe); - us->irq_handle = NULL; -#endif + + /* NO! We don't release this IRQ. We just re-use the handler + usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe); + us->irq_handle = NULL; + */ if (us->ip_wanted) { US_DEBUGP("Did not get interrupt on CBI\n"); @@ -447,10 +457,12 @@ { int result; - /* run the command */ + US_DEBUGP("CBI gets a command:\n"); + us_show_command(srb); - if ((result = pop_CB_command(srb))) { - US_DEBUGP("CBI command %x\n", result); + /* run the command */ + if ((result = pop_CB_command(srb)) < 0) { + US_DEBUGP("Call to pop_CB_command returned %d\n", result); if (result == USB_ST_STALL || result == USB_ST_TIMEOUT) { return (DID_OK << 16) | 2; } @@ -458,11 +470,12 @@ } /* transfer the data */ - if (us_transfer_length(srb)) { result = us_transfer(srb, US_DIRECTION(srb->cmnd[0])); - if (result && result != USB_ST_DATAUNDERRUN && result != USB_ST_STALL) { - US_DEBUGP("CBI transfer %x\n", result); + if ((result < 0) && + (result != USB_ST_DATAUNDERRUN) && + (result != USB_ST_STALL)) { + US_DEBUGP("CBI attempted to transfer data, result is %x\n", result); return DID_ERROR << 16; } #if 0 @@ -578,6 +591,8 @@ US_DEBUGP("Bulk logical error\n"); return DID_ABORT << 16; } + + /* We need to fix some of this status handling. */ switch (bcs.Status) { case US_BULK_STAT_OK: return DID_OK << 16; @@ -593,7 +608,7 @@ return DID_ERROR << 16; } - return (DID_OK << 16) | 2; /* check sense required */ + return (DID_OK << 16) | 2; /* check sense required */ } /* Host functions */ @@ -605,20 +620,28 @@ struct us_data *us = (struct us_data *)sht->proc_dir; char name[32]; + /* set up our name */ sprintf(name, "usbscsi%d", us->host_number); sht->name = sht->proc_name = kmalloc(strlen(name)+1, GFP_KERNEL); if (!sht->proc_name) return 0; strcpy(sht->proc_name, name); + + /* we start with no /proc directory entry */ sht->proc_dir = NULL; + + /* register the host */ us->host = scsi_register(sht, sizeof(us)); if (us->host) { us->host->hostdata[0] = (unsigned long)us; us->host_no = us->host->host_no; return 1; } + + /* odd... didn't register properly. Abort and free pointers */ kfree(sht->proc_name); - sht->proc_name = sht->name = NULL; + sht->proc_name = NULL; + sht->name = NULL; return 0; } @@ -695,55 +718,80 @@ return 0; } +/*********************************************************************** + * /proc/scsi/ functions + ***********************************************************************/ + +/* we use this macro to help us write into the buffer */ #undef SPRINTF -#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); } +#define SPRINTF(args...) do { if (pos < (buffer + length)) pos += sprintf (pos, ## args); } while (0) -int usb_scsi_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout) +int usb_scsi_proc_info (char *buffer, char **start, off_t offset, + int length, int hostno, int inout) { struct us_data *us = us_list; char *pos = buffer; - char *vendor; - char *product; - char *style = ""; + char *tmp_ptr; /* find our data from hostno */ - while (us) { if (us->host_no == hostno) break; us = us->next; } + /* if we couldn't find it, we return an error */ if (!us) return -ESRCH; - /* null on outward */ - + /* if someone is sending us data, just throw it away */ if (inout) return length; - if (!us->pusb_dev || !(vendor = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer))) - vendor = "?"; - if (!us->pusb_dev || !(product = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct))) - product = "?"; + /* print the controler name */ + SPRINTF ("Host scsi%d: usb-scsi\n", hostno); + + /* print product and vendor strings */ + if (!us->pusb_dev) { + SPRINTF("Vendor: Unknown Vendor\n"); + SPRINTF("Product: Unknown Product\n"); + } else { + SPRINTF("Vendor: "); + tmp_ptr = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer); + if (!tmp_ptr) + SPRINTF("Unknown Vendor\n"); + else + SPRINTF("%s\n", tmp_ptr); + + SPRINTF("Product: "); + tmp_ptr = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct); + if (!tmp_ptr) + SPRINTF("Unknown Vendor\n"); + else + SPRINTF("%s\n", tmp_ptr); + } + SPRINTF("Protocol: "); switch (us->protocol) { case US_PR_CB: - style = "Control/Bulk"; + SPRINTF("Control/Bulk\n"); break; - + case US_PR_CBI: - style = "Control/Bulk/Interrupt"; + SPRINTF("Control/Bulk/Interrupt\n"); break; - - case US_PR_ZIP: - style = "Bulk only"; + + case US_PR_BULK: + SPRINTF("Bulk only\n"); + break; + + default: + SPRINTF("Unknown Protocol\n"); break; - } - SPRINTF ("Host scsi%d: usb-scsi\n", hostno); - SPRINTF ("Device: %s %s - GUID " GUID_FORMAT "\n", vendor, product, GUID_ARGS(us->guid) ); - SPRINTF ("Style: %s\n", style); + + /* show the GUID of the device */ + SPRINTF("GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid)); /* * Calculate start of next buffer, and return value. @@ -763,50 +811,50 @@ */ static Scsi_Host_Template my_host_template = { - NULL, /* next */ - NULL, /* module */ - NULL, /* proc_dir */ + NULL, /* next */ + NULL, /* module */ + NULL, /* proc_dir */ usb_scsi_proc_info, - NULL, /* name - points to unique */ + NULL, /* name - points to unique */ us_detect, us_release, - NULL, /* info */ - NULL, /* ioctl */ + NULL, /* info */ + NULL, /* ioctl */ us_command, us_queuecommand, - NULL, /* eh_strategy */ + NULL, /* eh_strategy */ us_abort, us_bus_reset, us_bus_reset, us_host_reset, - NULL, /* abort */ - NULL, /* reset */ - NULL, /* slave_attach */ - NULL, /* bios_param */ - 1, /* can_queue */ - -1, /* this_id */ - SG_ALL, /* sg_tablesize */ - 1, /* cmd_per_lun */ - 0, /* present */ - FALSE, /* unchecked_isa_dma */ - FALSE, /* use_clustering */ - TRUE, /* use_new_eh_code */ - TRUE /* emulated */ + NULL, /* abort */ + NULL, /* reset */ + NULL, /* slave_attach */ + NULL, /* bios_param */ + 1, /* can_queue */ + -1, /* this_id */ + SG_ALL, /* sg_tablesize */ + 1, /* cmd_per_lun */ + 0, /* present */ + FALSE, /* unchecked_isa_dma */ + FALSE, /* use_clustering */ + TRUE, /* use_new_eh_code */ + TRUE /* emulated */ }; static unsigned char sense_notready[] = { - 0x70, /* current error */ + 0x70, /* current error */ 0x00, - 0x02, /* not ready */ + 0x02, /* not ready */ 0x00, 0x00, - 10, /* additional length */ + 10, /* additional length */ 0x00, 0x00, 0x00, 0x00, - 0x04, /* not ready */ - 0x03, /* manual intervention */ + 0x04, /* not ready */ + 0x03, /* manual intervention */ 0x00, 0x00, 0x00, @@ -863,8 +911,6 @@ } else { US_DEBUG(us_show_command(us->srb)); - /* check for variable length - do properly if so */ - if (us->filter && us->filter->command) us->srb->result = us->filter->command(us->fdata, us->srb); else if (us->srb->cmnd[0] == START_STOP && @@ -875,6 +921,7 @@ unsigned int savelen = us->srb->request_bufflen; unsigned int saveallocation = 0; + /* check for variable length - do properly if so */ switch (us->srb->cmnd[0]) { case REQUEST_SENSE: if (us->srb->request_bufflen > 18) @@ -917,6 +964,66 @@ default: break; } /* end switch on cmnd[0] */ + +#if 0 + /* translate READ_6 to READ_10 */ + if (us->srb->cmnd[0] == 0x08) { + + /* get the control */ + us->srb->cmnd[9] = us->srb->cmnd[5]; + + /* get the length */ + us->srb->cmnd[8] = us->srb->cmnd[6]; + us->srb->cmnd[7] = 0; + + /* set the reserved area to 0 */ + us->srb->cmnd[6] = 0; + + /* get LBA */ + us->srb->cmnd[5] = us->srb->cmnd[3]; + us->srb->cmnd[4] = us->srb->cmnd[2]; + us->srb->cmnd[3] = 0; + us->srb->cmnd[2] = 0; + + /* LUN and other info in cmnd[1] can stay */ + + /* fix command code */ + us->srb->cmnd[0] = 0x28; + + US_DEBUGP("Changing READ_6 to READ_10\n"); + US_DEBUG(us_show_command(us->srb)); + } + + /* translate WRITE_6 to WRITE_10 */ + if (us->srb->cmnd[0] == 0x0A) { + + /* get the control */ + us->srb->cmnd[9] = us->srb->cmnd[5]; + + /* get the length */ + us->srb->cmnd[8] = us->srb->cmnd[4]; + us->srb->cmnd[7] = 0; + + /* set the reserved area to 0 */ + us->srb->cmnd[6] = 0; + + /* get LBA */ + us->srb->cmnd[5] = us->srb->cmnd[3]; + us->srb->cmnd[4] = us->srb->cmnd[2]; + us->srb->cmnd[3] = 0; + us->srb->cmnd[2] = 0; + + /* LUN and other info in cmnd[1] can stay */ + + /* fix command code */ + us->srb->cmnd[0] = 0x2A; + + US_DEBUGP("Changing WRITE_6 to WRITE_10\n"); + US_DEBUG(us_show_command(us->srb)); + } +#endif + + /* let's do the command */ us->srb->result = us->pop(us->srb); if (savelen != us->srb->request_bufflen && @@ -927,9 +1034,9 @@ /* set correct length and retry */ switch (us->srb->cmnd[0]) { case REQUEST_SENSE: - /* simply return 18 bytes */ + /* simply return 18 bytes */ p[7] = 10; - length = us->srb->request_bufflen;; + length = us->srb->request_bufflen; break; case INQUIRY: @@ -938,8 +1045,9 @@ break; case MODE_SENSE: - length = p[0] + 4 > savelen ? savelen : p[0] + 4; - us->srb->cmnd[4] = 4; + US_DEBUGP("MODE_SENSE Mode data length is %d\n", p[0]); + length = p[0] + 1 > savelen ? savelen : p[0] + 1; + us->srb->cmnd[4] = length; break; case LOG_SENSE: @@ -949,7 +1057,9 @@ break; case MODE_SENSE_10: - length = ((p[0] << 8) + p[1]) + 8 > savelen ? savelen : ((p[0] << 8) + p[1]) + 8; + US_DEBUGP("MODE_SENSE_10 Mode data length is %d\n", + (p[0] << 8) + p[1]); + length = ((p[0] << 8) + p[1]) + 6 > savelen ? savelen : ((p[0] << 8) + p[1]) + 6; us->srb->cmnd[7] = length >> 8; us->srb->cmnd[8] = length; break; @@ -967,8 +1077,13 @@ us->srb->request_bufflen = savelen; switch (us->srb->cmnd[0]) { - case REQUEST_SENSE: case INQUIRY: + if ((((unsigned char*)us->srb->request_buffer)[2] & 0x7) == 0) { + US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n"); + ((unsigned char*)us->srb->request_buffer)[2] |= 2; + } + /* FALL THROUGH */ + case REQUEST_SENSE: case MODE_SENSE: if (us->srb->use_sg == 0 && length > 0) { int i; @@ -1050,42 +1165,51 @@ usbscsi_debug = !usbscsi_debug; printk(USB_SCSI "debug toggle = %d\n", usbscsi_debug); } else { - break; /* exit the loop on any other signal */ + break; /* exit the loop on any other signal */ } } } - MOD_DEC_USE_COUNT; + // MOD_DEC_USE_COUNT; printk("usbscsi_control_thread exiting\n"); return 0; } + +/* Probe to see if a new device is actually a SCSI device */ static void * scsi_probe(struct usb_device *dev, unsigned int ifnum) { struct usb_interface_descriptor *interface; int i; - char *mf; /* manufacturer */ - char *prod; /* product */ - char *serial; /* serial number */ + char *mf; /* manufacturer */ + char *prod; /* product */ + char *serial; /* serial number */ struct us_data *ss = NULL; struct usb_scsi_filter *filter = filters; void *fdata = NULL; unsigned int flags = 0; - GUID(guid); + GUID(guid); /* Global Unique Identifier */ struct us_data *prev; Scsi_Host_Template *htmplt; int protocol = 0; int subclass = 0; + struct usb_interface_descriptor *altsetting = + &(dev->actconfig->interface[ifnum].altsetting[0]); + /* clear the GUID and fetch the strings */ GUID_CLEAR(guid); mf = usb_string(dev, dev->descriptor.iManufacturer); prod = usb_string(dev, dev->descriptor.iProduct); serial = usb_string(dev, dev->descriptor.iSerialNumber); /* probe with filters first */ - + /* MDD: What are filters? What do they do? + * They look like some way to catch certain specific devices and set + * flags for them. Probably a good idea if we have lots of different + * types of devices. + */ if (mf && prod) { while (filter) { if ((fdata = filter->probe(dev, mf, prod, serial)) != NULL) { @@ -1098,45 +1222,56 @@ } /* generic devices next */ - + + /* MDD: Isn't this always true? */ if (fdata == NULL) { - /* some exceptions */ + /* We make an exception for the shuttle E-USB */ if (dev->descriptor.idVendor == 0x04e6 && dev->descriptor.idProduct == 0x0001) { - /* shuttle E-USB */ protocol = US_PR_CB; - subclass = US_SC_8070; /* an assumption */ + subclass = US_SC_8070; /* an assumption */ } else if (dev->descriptor.bDeviceClass != 0 || - dev->actconfig->interface[ifnum].altsetting[0].bInterfaceClass != - USB_CLASS_MASS_STORAGE || - dev->actconfig->interface[ifnum].altsetting[0].bInterfaceSubClass < US_SC_MIN || - dev->actconfig->interface[ifnum].altsetting[0].bInterfaceSubClass > US_SC_MAX) { + altsetting->bInterfaceClass != USB_CLASS_MASS_STORAGE || + altsetting->bInterfaceSubClass < US_SC_MIN || + altsetting->bInterfaceSubClass > US_SC_MAX) { + /* if it's not a mass storage, we go no further */ return NULL; } - /* now check if we have seen it before */ + /* At this point, we know we've got a live one */ + US_DEBUGP("USB Mass Storage device detected\n"); + /* Create a GUID for this device */ if (dev->descriptor.iSerialNumber && usb_string(dev, dev->descriptor.iSerialNumber) ) { - make_guid(guid, dev->descriptor.idVendor, dev->descriptor.idProduct, + /* If we have a serial number, and it's a non-NULL string */ + make_guid(guid, dev->descriptor.idVendor, + dev->descriptor.idProduct, usb_string(dev, dev->descriptor.iSerialNumber)); } else { - make_guid(guid, dev->descriptor.idVendor, dev->descriptor.idProduct, - "0"); + /* We don't have a serial number, so we use 0 */ + make_guid(guid, dev->descriptor.idVendor, + dev->descriptor.idProduct, "0"); } - for (ss = us_list; ss; ss = ss->next) { - if (!ss->pusb_dev && GUID_EQUAL(guid, ss->guid)) { + + /* Now check if we have seen this GUID before, and restore + * the flags if we find it + */ + for (ss = us_list; ss != NULL; ss = ss->next) { + if (!ss->pusb_dev && GUID_EQUAL(guid, ss->guid)) { US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); flags = ss->flags; break; } } - } + } /* if (fdata == NULL) */ + /* If ss == NULL, then this is a new device. Allocate memory for it */ if (!ss) { - if ((ss = (struct us_data *)kmalloc(sizeof(*ss), GFP_KERNEL)) == NULL) { + if ((ss = (struct us_data *)kmalloc(sizeof(*ss), + GFP_KERNEL)) == NULL) { printk(KERN_WARNING USB_SCSI "Out of memory\n"); if (filter) filter->release(fdata); @@ -1145,10 +1280,18 @@ memset(ss, 0, sizeof(struct us_data)); } - interface = &dev->actconfig->interface[ifnum].altsetting[0]; + /* Initialize the us_data structure with some useful info */ + interface = altsetting; ss->filter = filter; ss->fdata = fdata; ss->flags = flags; + ss->ifnum = ifnum; + ss->pusb_dev = dev; + ss->attention_done = 0; + + /* If the device has subclass and protocol, then use that. Otherwise, + * take data from the specific interface. + */ if (subclass) { ss->subclass = subclass; ss->protocol = protocol; @@ -1156,11 +1299,9 @@ ss->subclass = interface->bInterfaceSubClass; ss->protocol = interface->bInterfaceProtocol; } - ss->attention_done = 0; - - /* set the protocol op */ - US_DEBUGP("Protocol "); + /* set the handler pointers based on the protocol */ + US_DEBUGP("Protocol: "); switch (ss->protocol) { case US_PR_CB: US_DEBUGPX("Control/Bulk\n"); @@ -1174,11 +1315,17 @@ ss->pop_reset = pop_CB_reset; break; - default: + case US_PR_BULK: US_DEBUGPX("Bulk\n"); ss->pop = pop_Bulk; ss->pop_reset = pop_Bulk_reset; break; + + default: + US_DEBUGPX("Unknown\n"); + kfree(ss); + return NULL; + break; } /* @@ -1186,9 +1333,9 @@ * An optional interrupt is OK (necessary for CBI protocol). * We will ignore any others. */ - for (i = 0; i < interface->bNumEndpoints; i++) { - if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + /* is it an BULK endpoint? */ + if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { if (interface->endpoint[i].bEndpointAddress & USB_DIR_IN) ss->ep_in = interface->endpoint[i].bEndpointAddress & @@ -1196,8 +1343,11 @@ else ss->ep_out = interface->endpoint[i].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - } else if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_INT) { + } + + /* is it an interrupt endpoint? */ + if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_INT) { ss->ep_int = interface->endpoint[i].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; } @@ -1205,13 +1355,10 @@ US_DEBUGP("Endpoints In %d Out %d Int %d\n", ss->ep_in, ss->ep_out, ss->ep_int); - /* save the interface number */ - ss->ifnum = ifnum; - - /* exit if strange looking */ - + /* Do some basic sanity checks, and bail if we find a problem */ if (usb_set_interface(dev, interface->bInterfaceNumber, 0) || - !ss->ep_in || !ss->ep_out || (ss->protocol == US_PR_CBI && ss->ep_int == 0)) { + !ss->ep_in || !ss->ep_out || + (ss->protocol == US_PR_CBI && ss->ep_int == 0)) { US_DEBUGP("Problems with device\n"); if (ss->host) { scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt); @@ -1221,78 +1368,82 @@ if (filter) filter->release(fdata); kfree(ss); - return NULL; /* no endpoints */ + return NULL; } - if (dev->actconfig->iConfiguration && usb_string(dev, dev->actconfig->iConfiguration)) - US_DEBUGP("Configuration %s\n", - usb_string(dev, dev->actconfig->iConfiguration)); - if (interface->iInterface && usb_string(dev, interface->iInterface)) - US_DEBUGP("Interface %s\n", - usb_string(dev, interface->iInterface)); - - ss->pusb_dev = dev; - - /* Now generate a scsi host definition, and register with scsi above us */ - + /* If this is a new device (i.e. we haven't seen it before), we need to + * generate a scsi host definition, and register with scsi above us + */ if (!ss->host) { - - /* make unique id if possible */ - + /* copy the GUID we created before */ US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); memcpy(ss->guid, guid, sizeof(guid)); /* set class specific stuff */ - - US_DEBUGP("SubClass "); + US_DEBUGP("SubClass: "); switch (ss->subclass) { case US_SC_RBC: US_DEBUGPX("Reduced Block Commands\n"); break; + case US_SC_8020: US_DEBUGPX("8020\n"); break; + case US_SC_QIC: US_DEBUGPX("QIC157\n"); break; + case US_SC_8070: US_DEBUGPX("8070\n"); ss->flags |= US_FL_FIXED_COMMAND; ss->fixedlength = 12; break; + case US_SC_SCSI: US_DEBUGPX("Transparent SCSI\n"); break; + case US_SC_UFI: - US_DEBUGPX(" UFF\n"); + US_DEBUGPX("UFI\n"); ss->flags |= US_FL_FIXED_COMMAND; ss->fixedlength = 12; break; default: + US_DEBUGPX("Unknown\n"); break; } - /* create unique host template */ + /* Allocate memory for the SCSI Host Template */ + if ((htmplt = (Scsi_Host_Template *) + kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) { - if ((htmplt = (Scsi_Host_Template *)kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) { printk(KERN_WARNING USB_SCSI "Out of memory\n"); if (filter) filter->release(fdata); kfree(ss); return NULL; } + + /* Initialize the host template based on the default one */ memcpy(htmplt, &my_host_template, sizeof(my_host_template)); + + /* Grab the next host number */ ss->host_number = my_host_number++; - (struct us_data *)htmplt->proc_dir = ss; + /* MDD: FIXME: this is bad. We abuse this pointer so we + * can pass the ss pointer to the host controler thread + * in us_detect + */ + (struct us_data *)htmplt->proc_dir = ss; + /* shuttle E-USB */ if (dev->descriptor.idVendor == 0x04e6 && dev->descriptor.idProduct == 0x0001) { __u8 qstat[2]; int result; - - /* shuttle E-USB */ + result = usb_control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0), 1, 0xC0, 0, ss->ifnum, @@ -1314,10 +1465,24 @@ #endif } else if (ss->protocol == US_PR_CBI) + { + int result; + init_waitqueue_head(&ss->ip_waitq); - /* start up our thread */ + /* set up the IRQ pipe and handler */ + /* FIXME: This needs to get the period from the device */ + ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); + result = usb_request_irq(ss->pusb_dev, ss->irqpipe, pop_CBI_irq, + 255, (void *)ss, &ss->irq_handle); + if (result) { + US_DEBUGP("usb_request_irq failed (0x%x), No interrupt for CBI\n", + result); + } + } + + /* start up our thread */ { DECLARE_MUTEX_LOCKED(sem); @@ -1336,7 +1501,6 @@ } /* wait for it to start */ - down(&sem); } @@ -1352,12 +1516,13 @@ prev->next = ss; } - printk(KERN_WARNING "DANGEROUS: USB SCSI driver data integrity not assured !!!\n"); + printk(KERN_WARNING "WARNING: USB SCSI data integrity not assured\n"); printk(KERN_INFO "USB SCSI device found at address %d\n", dev->devnum); return ss; } +/* Handle a disconnect event from the USB core */ static void scsi_disconnect(struct usb_device *dev, void *ptr) { struct us_data *ss = ptr; @@ -1367,19 +1532,17 @@ if (ss->filter) ss->filter->release(ss->fdata); ss->pusb_dev = NULL; - MOD_DEC_USE_COUNT; + // MOD_DEC_USE_COUNT; } + +/*********************************************************************** + * Initialization and registration + ***********************************************************************/ + int usb_scsi_init(void) { - MOD_INC_USE_COUNT; - -#ifdef CONFIG_USB_HP4100 - hp4100_init(); -#endif -#ifdef CONFIG_USB_ZIP - usb_zip_init(); -#endif + // MOD_INC_USE_COUNT; if (usb_register(&scsi_driver) < 0) return -1; @@ -1388,6 +1551,9 @@ return 0; } +/* Functions to handle filters. These are designed to allow us to handle + * certain odd devices + */ int usb_scsi_register(struct usb_scsi_filter *filter) { struct usb_scsi_filter *prev = (struct usb_scsi_filter *)&filters; @@ -1411,6 +1577,7 @@ #ifdef MODULE int init_module(void) { + /* MDD: Perhaps we should register the host here */ return usb_scsi_init(); } diff -u --recursive --new-file v2.3.33/linux/drivers/usb/usb_scsi.h linux/drivers/usb/usb_scsi.h --- v2.3.33/linux/drivers/usb/usb_scsi.h Thu Nov 18 20:25:37 1999 +++ linux/drivers/usb/usb_scsi.h Mon Dec 20 14:16:59 1999 @@ -47,8 +47,7 @@ #define US_PR_CB 1 /* Control/Bulk w/o interrupt */ #define US_PR_CBI 0 /* Control/Bulk/Interrupt */ -#define US_PR_ZIP 0x50 /* bulk only */ -/* #define US_PR_BULK ?? */ +#define US_PR_BULK 0x50 /* bulk only */ /* * Bulk only data structures (Zip 100, for example) diff -u --recursive --new-file v2.3.33/linux/drivers/usb/uss720.c linux/drivers/usb/uss720.c --- v2.3.33/linux/drivers/usb/uss720.c Sun Nov 7 16:37:34 1999 +++ linux/drivers/usb/uss720.c Mon Dec 20 14:17:26 1999 @@ -4,7 +4,7 @@ * uss720.c -- USS720 USB Parport Cable. * * Copyright (C) 1999 - * Thomas Sailer (sailer@ife.ee.ethz.ch) + * Thomas Sailer (sailer@ife.ee.ethz.ch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,10 +25,10 @@ * History: * 0.1 04.08.99 Created * 0.2 07.08.99 Some fixes mainly suggested by Tim Waugh - * Interrupt handling currently disabled because - * usb_request_irq crashes somewhere within ohci.c - * for no apparent reason (that is for me, anyway) - * ECP currently untested + * Interrupt handling currently disabled because + * usb_request_irq crashes somewhere within ohci.c + * for no apparent reason (that is for me, anyway) + * ECP currently untested * 0.3 10.08.99 fixing merge errors * 0.4 13.08.99 Added Vendor/Product ID of Brad Hard's cable * 0.5 20.09.99 usb_control_msg wrapper used @@ -50,7 +50,7 @@ struct usb_device *usbdev; void *irqhandle; unsigned int irqpipe; - unsigned char reg[7]; /* USB registers */ + unsigned char reg[7]; /* USB registers */ }; /* --------------------------------------------------------------------- */ @@ -62,17 +62,17 @@ static const unsigned char regindex[9] = { 4, 0, 1, 5, 5, 0, 2, 3, 6 }; - int ret; + int ret; if (!usbdev) return -1; - ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev,0), 3, 0xc0, ((unsigned int)reg) << 8, 0, priv->reg, 7, HZ); - if (ret) { - printk(KERN_DEBUG "uss720: get_1284_register(%d) failed, status 0x%x\n", + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev,0), 3, 0xc0, ((unsigned int)reg) << 8, 0, priv->reg, 7, HZ); + if (ret) { + printk(KERN_DEBUG "uss720: get_1284_register(%d) failed, status 0x%x\n", (unsigned int)reg, ret); - } else { + } else { #if 0 - printk(KERN_DEBUG "uss720: get_1284_register(%d) return %02x %02x %02x %02x %02x %02x %02x\n", + printk(KERN_DEBUG "uss720: get_1284_register(%d) return %02x %02x %02x %02x %02x %02x %02x\n", (unsigned int)reg, (unsigned int)priv->reg[0], (unsigned int)priv->reg[1], (unsigned int)priv->reg[2], (unsigned int)priv->reg[3], (unsigned int)priv->reg[4], (unsigned int)priv->reg[5], (unsigned int)priv->reg[6]); @@ -90,20 +90,20 @@ { struct parport_uss720_private *priv = pp->private_data; struct usb_device *usbdev = priv->usbdev; - int ret; + int ret; if (!usbdev) return -1; - ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev,0), 4, 0x40, (((unsigned int)reg) << 8) | val, 0, NULL, 0, HZ); - if (ret) { - printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x) failed, status 0x%x\n", + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev,0), 4, 0x40, (((unsigned int)reg) << 8) | val, 0, NULL, 0, HZ); + if (ret) { + printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x) failed, status 0x%x\n", (unsigned int)reg, (unsigned int)val, ret); - } else { + } else { #if 0 - printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x)\n", + printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x)\n", (unsigned int)reg, (unsigned int)val); #endif - } + } return ret; } @@ -363,7 +363,7 @@ return 0; if (change_mode(pp, ECR_EPP)) return 0; - i = usbdev->bus->op->bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), buf, length, &rlen, HZ*20); + i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), buf, length, &rlen, HZ*20); if (i) printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %u rlen %lu\n", buf, length, rlen); change_mode(pp, ECR_PS2); @@ -424,7 +424,7 @@ return 0; if (change_mode(pp, ECR_ECP)) return 0; - i = usbdev->bus->op->bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), buffer, len, &rlen, HZ*20); + i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), buffer, len, &rlen, HZ*20); if (i) printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %u rlen %lu\n", buffer, len, rlen); change_mode(pp, ECR_PS2); @@ -442,7 +442,7 @@ return 0; if (change_mode(pp, ECR_ECP)) return 0; - i = usbdev->bus->op->bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), buffer, len, &rlen, HZ*20); + i = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), buffer, len, &rlen, HZ*20); if (i) printk(KERN_ERR "uss720: recvbulk ep 2 buf %p len %u rlen %lu\n", buffer, len, rlen); change_mode(pp, ECR_PS2); @@ -475,7 +475,7 @@ return 0; if (change_mode(pp, ECR_PPF)) return 0; - i = usbdev->bus->op->bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), buffer, len, &rlen, HZ*20); + i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), buffer, len, &rlen, HZ*20); if (i) printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %u rlen %lu\n", buffer, len, rlen); change_mode(pp, ECR_PS2); @@ -540,28 +540,28 @@ static void * uss720_probe(struct usb_device *usbdev, unsigned int ifnum) { - struct usb_interface_descriptor *interface; - struct usb_endpoint_descriptor *endpoint; + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; struct parport_uss720_private *priv; struct parport *pp; - int i; + int i; - printk(KERN_DEBUG "uss720: probe: vendor id 0x%x, device id 0x%x\n", - usbdev->descriptor.idVendor, usbdev->descriptor.idProduct); + printk(KERN_DEBUG "uss720: probe: vendor id 0x%x, device id 0x%x\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct); - if ((usbdev->descriptor.idVendor != 0x047e || usbdev->descriptor.idProduct != 0x1001) && + if ((usbdev->descriptor.idVendor != 0x047e || usbdev->descriptor.idProduct != 0x1001) && (usbdev->descriptor.idVendor != 0x0557 || usbdev->descriptor.idProduct != 0x2001) && (usbdev->descriptor.idVendor != 0x0729 || usbdev->descriptor.idProduct != 0x1284)) - return NULL; + return NULL; - /* our known interfaces have 3 alternate settings */ - if (usbdev->actconfig->interface[ifnum].num_altsetting != 3) - return NULL; + /* our known interfaces have 3 alternate settings */ + if (usbdev->actconfig->interface[ifnum].num_altsetting != 3) + return NULL; - i = usb_set_interface(usbdev, ifnum, 2); - printk(KERN_DEBUG "uss720: set inteface result %d\n", i); + i = usb_set_interface(usbdev, ifnum, 2); + printk(KERN_DEBUG "uss720: set inteface result %d\n", i); - interface = &usbdev->actconfig->interface[ifnum].altsetting[2]; + interface = &usbdev->actconfig->interface[ifnum].altsetting[2]; /* * Allocate parport interface @@ -579,16 +579,16 @@ priv->usbdev = usbdev; pp->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP | PARPORT_MODE_ECP | PARPORT_MODE_COMPAT; - /* set the USS720 control register to manual mode, no ECP compression, enable all ints */ - set_1284_register(pp, 7, 0x00); + /* set the USS720 control register to manual mode, no ECP compression, enable all ints */ + set_1284_register(pp, 7, 0x00); set_1284_register(pp, 6, 0x30); /* PS/2 mode */ set_1284_register(pp, 2, 0x0c); - /* debugging */ - get_1284_register(pp, 0, NULL); - printk("uss720: reg: %02x %02x %02x %02x %02x %02x %02x\n", - priv->reg[0], priv->reg[1], priv->reg[2], priv->reg[3], priv->reg[4], priv->reg[5], priv->reg[6]); + /* debugging */ + get_1284_register(pp, 0, NULL); + printk("uss720: reg: %02x %02x %02x %02x %02x %02x %02x\n", + priv->reg[0], priv->reg[1], priv->reg[2], priv->reg[3], priv->reg[4], priv->reg[5], priv->reg[6]); - endpoint = &interface->endpoint[2]; + endpoint = &interface->endpoint[2]; printk(KERN_DEBUG "uss720: epaddr %d interval %d\n", endpoint->bEndpointAddress, endpoint->bInterval); #if 0 priv->irqpipe = usb_rcvctrlpipe(usbdev, endpoint->bEndpointAddress); @@ -600,11 +600,11 @@ goto probe_abort_port; } #endif - parport_proc_register(pp); - parport_announce_port(pp); + parport_proc_register(pp); + parport_announce_port(pp); MOD_INC_USE_COUNT; - return pp; + return pp; probe_abort_port: parport_unregister_port(pp); @@ -619,7 +619,7 @@ struct parport_uss720_private *priv = pp->private_data; usb_release_irq(usbdev, priv->irqhandle, priv->irqpipe); - priv->usbdev = NULL; + priv->usbdev = NULL; parport_proc_unregister(pp); parport_unregister_port(pp); kfree(priv); @@ -627,10 +627,10 @@ } static struct usb_driver uss720_driver = { - "uss720", - uss720_probe, - uss720_disconnect, - { NULL, NULL } + "uss720", + uss720_probe, + uss720_disconnect, + { NULL, NULL } }; /* --------------------------------------------------------------------- */ @@ -643,7 +643,7 @@ if (usb_register(&uss720_driver) < 0) return -1; - printk(KERN_INFO "uss720: USB<->IEEE1284 cable driver v0.4 registered.\n" + printk(KERN_INFO "uss720: USB<->IEEE1284 cable driver v0.4 registered.\n" KERN_INFO "uss720: (C) 1999 by Thomas Sailer, \n"); return 0; } diff -u --recursive --new-file v2.3.33/linux/fs/buffer.c linux/fs/buffer.c --- v2.3.33/linux/fs/buffer.c Tue Dec 14 01:27:24 1999 +++ linux/fs/buffer.c Mon Dec 20 14:52:57 1999 @@ -727,8 +727,7 @@ atomic_dec(&bh->b_count); tmp = bh->b_this_page; while (tmp != bh) { - if (atomic_read(&tmp->b_count) && - (tmp->b_end_io == end_buffer_io_async)) + if (tmp->b_end_io == end_buffer_io_async && buffer_locked(tmp)) goto still_busy; tmp = tmp->b_this_page; } @@ -1089,7 +1088,7 @@ return NULL; } -void set_bh_page (struct buffer_head *bh, struct page *page, unsigned int offset) +void set_bh_page (struct buffer_head *bh, struct page *page, unsigned long offset) { bh->b_page = page; if (offset >= PAGE_SIZE) diff -u --recursive --new-file v2.3.33/linux/fs/exec.c linux/fs/exec.c --- v2.3.33/linux/fs/exec.c Tue Dec 7 09:32:47 1999 +++ linux/fs/exec.c Sat Dec 18 15:29:41 1999 @@ -42,14 +42,6 @@ #include #endif -/* - * Here are the actual binaries that will be accepted: - * add more with "register_binfmt()" if using modules... - * - * These are defined again for the 'real' modules if you are using a - * module definition for these routines. - */ - static struct linux_binfmt *formats = (struct linux_binfmt *) NULL; int register_binfmt(struct linux_binfmt * fmt) diff -u --recursive --new-file v2.3.33/linux/fs/ext2/balloc.c linux/fs/ext2/balloc.c --- v2.3.33/linux/fs/ext2/balloc.c Tue Dec 7 09:32:47 1999 +++ linux/fs/ext2/balloc.c Mon Dec 20 16:06:56 1999 @@ -118,8 +118,8 @@ * * Return the slot used to store the bitmap, or a -ve error code. */ -static int load__block_bitmap (struct super_block * sb, - unsigned int block_group) +static int __load_block_bitmap (struct super_block * sb, + unsigned int block_group) { int i, j, retval = 0; unsigned long block_bitmap_number; @@ -136,7 +136,7 @@ if (sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group) return block_group; - ext2_error (sb, "load_block_bitmap", + ext2_error (sb, "__load_block_bitmap", "block_group != block_bitmap_number"); } retval = read_block_bitmap (sb, block_group, block_group); @@ -192,7 +192,7 @@ * Return the slot number of the group in the superblock bitmap cache's on * success, or a -ve error code. * - * There is still one inconsistancy here --- if the number of groups in this + * There is still one inconsistency here --- if the number of groups in this * filesystems is <= EXT2_MAX_GROUP_LOADED, then we have no way of * differentiating between a group for which we have never performed a bitmap * IO request, and a group for which the last bitmap read request failed. @@ -224,7 +224,7 @@ * If not, then do a full lookup for this block group. */ else { - slot = load__block_bitmap (sb, block_group); + slot = __load_block_bitmap (sb, block_group); } /* diff -u --recursive --new-file v2.3.33/linux/fs/fat/dir.c linux/fs/fat/dir.c --- v2.3.33/linux/fs/fat/dir.c Mon Aug 9 11:43:49 1999 +++ linux/fs/fat/dir.c Mon Dec 20 14:41:47 1999 @@ -10,6 +10,7 @@ * VFAT extensions by Gordon Chaffee * Merged with msdos fs by Henrik Storner * Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV + * Short name translation 1999 by Wolfram Pienkoss */ #define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S)) @@ -55,22 +56,21 @@ /* * Convert Unicode 16 to UTF8, translated Unicode, or ASCII. - * If uni_xlate is enabled and we - * can't get a 1:1 conversion, use a colon as an escape character since - * it is normally invalid on the vfat filesystem. The following three - * characters are a sort of uuencoded 16 bit Unicode value. This lets - * us do a full dump and restore of Unicode filenames. We could get - * into some trouble with long Unicode names, but ignore that right now. + * If uni_xlate is enabled and we can't get a 1:1 conversion, use a + * colon as an escape character since it is normally invalid on the vfat + * filesystem. The following four characters are the hexadecimal digits + * of Unicode value. This lets us do a full dump and restore of Unicode + * filenames. We could get into some trouble with long Unicode names, + * but ignore that right now. * Ahem... Stack smashing in ring 0 isn't fun. Fixed. */ static int uni16_to_x8(unsigned char *ascii, unsigned char *uni, int uni_xlate, struct nls_table *nls) { - unsigned char *ip, *op; - unsigned char ch, cl; - unsigned char *uni_page; - unsigned short val; + unsigned char *ip, *op, *uni_page, ch, cl, nc; + unsigned int ec; + int k; ip = uni; op = ascii; @@ -84,14 +84,15 @@ *op++ = uni_page[cl]; } else { if (uni_xlate == 1) { - *op++ = ':'; - val = (cl << 8) + ch; - op[2] = fat_uni2esc[val & 0x3f]; - val >>= 6; - op[1] = fat_uni2esc[val & 0x3f]; - val >>= 6; - *op = fat_uni2esc[val & 0x3f]; - op += 3; + *op = ':'; + ec = (ch << 8) + cl; + for (k = 4; k > 0; k--) { + nc = ec & 0xF; + op[k] = nc > 9 ? nc + ('a' - 10) + : nc + '0'; + ec >>= 4; + } + op += 5; } else { *op++ = '?'; } @@ -119,8 +120,31 @@ printk("]\n"); } #endif -static int memicmp(const char *s1, const char *s2, int len) { - while(len--) if (tolower(*s1++)!=tolower(*s2++)) return 1; + +static inline unsigned char +fat_tolower(struct nls_table *t, unsigned char c) +{ + unsigned char nc = t->charset2lower[c]; + + return nc ? nc : c; +} + +static inline struct nls_unicode +fat_short2lower_uni(struct nls_table *t, unsigned char c) +{ + unsigned char nc = t->charset2lower[c]; + + return nc ? t->charset2uni[nc] : t->charset2uni[c]; +} + +static int +fat_strnicmp(struct nls_table *t, const unsigned char *s1, + const unsigned char *s2, int len) +{ + while(len--) + if (fat_tolower(t, *s1++) != fat_tolower(t, *s2++)) + return 1; + return 0; } @@ -128,23 +152,21 @@ * Return values: negative -> error, 0 -> not found, positive -> found, * value is the total amount of slots, including the shortname entry. */ -int fat_search_long( - struct inode *inode, const char *name, int name_len, int anycase, - loff_t *spos, loff_t *lpos) +int fat_search_long(struct inode *inode, const char *name, int name_len, + int anycase, loff_t *spos, loff_t *lpos) { struct super_block *sb = inode->i_sb; - int ino,i,i2,last; - char c; struct buffer_head *bh = NULL; struct msdos_dir_entry *de; - loff_t cpos = 0; - char bufname[14]; - unsigned char long_slots; + struct nls_table *nls_io = MSDOS_SB(sb)->nls_io; + struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk; + struct nls_unicode bufuname[14]; + unsigned char xlate_len, long_slots, *unicode = NULL; + char c, bufname[260]; /* 256 + 4 */ int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate; int utf8 = MSDOS_SB(sb)->options.utf8; - unsigned char *unicode = NULL; - struct nls_table *nls = MSDOS_SB(sb)->nls_io; - int res = 0; + int ino, i, i2, last, res = 0; + loff_t cpos = 0; while(1) { if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1) @@ -157,7 +179,7 @@ continue; if (de->attr != ATTR_EXT && IS_FREE(de->name)) continue; - if (de->attr == ATTR_EXT) { + if (de->attr == ATTR_EXT) { struct msdos_dir_slot *ds; int offset; unsigned char id; @@ -226,36 +248,42 @@ for (i = 0, last = 0; i < 8;) { if (!(c = de->name[i])) break; - if (c >= 'A' && c <= 'Z') c += 32; if (c == 0x05) c = 0xE5; - if ((bufname[i++] = c) != ' ') + bufuname[i++] = fat_short2lower_uni(nls_disk, c); + if (c != ' ') last = i; } i = last; - bufname[i++] = '.'; + bufuname[i++] = fat_short2lower_uni(nls_disk, '.'); for (i2 = 0; i2 < 3; i2++) { if (!(c = de->ext[i2])) break; - if (c >= 'A' && c <= 'Z') c += 32; - if ((bufname[i++] = c) != ' ') + bufuname[i++] = fat_short2lower_uni(nls_disk, c); + if (c != ' ') last = i; } if (!last) continue; - if (last==name_len) - if ((!anycase && !memcmp(name, bufname, last)) || - (anycase && !memicmp(name, bufname, last))) - goto Found; + memset(&bufuname[last], 0, sizeof(struct nls_unicode)); + xlate_len = utf8 + ?utf8_wcstombs(bufname, (__u16 *) &bufuname, 260) + :uni16_to_x8(bufname, (unsigned char *) &bufuname, + uni_xlate, nls_io); + if (xlate_len == name_len) + if ((!anycase && !memcmp(name, bufname, xlate_len)) || + (anycase && !fat_strnicmp(nls_io, name, bufname, + xlate_len))) + goto Found; + if (long_slots) { - char longname[260]; /* 256 + 4 */ - unsigned char long_len; - long_len = utf8 - ?utf8_wcstombs(longname, (__u16 *) unicode, 260) - :uni16_to_x8(longname, unicode, uni_xlate, nls); - if (long_len != name_len) + xlate_len = utf8 + ?utf8_wcstombs(bufname, (__u16 *) unicode, 260) + :uni16_to_x8(bufname, unicode, uni_xlate, nls_io); + if (xlate_len != name_len) continue; - if ((!anycase && !memcmp(name, longname, long_len)) || - (anycase && !memicmp(name, longname, long_len))) + if ((!anycase && !memcmp(name, bufname, xlate_len)) || + (anycase && !fat_strnicmp(nls_io, name, bufname, + xlate_len))) goto Found; } } @@ -272,31 +300,23 @@ return res; } -static int fat_readdirx( - struct inode *inode, - struct file *filp, - void *dirent, - filldir_t filldir, - int shortnames, - int both) +static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, + filldir_t filldir, int shortnames, int both) { struct super_block *sb = inode->i_sb; - int ino,inum,i,i2,last; - char c; struct buffer_head *bh; struct msdos_dir_entry *de; - unsigned long lpos; - loff_t cpos; - unsigned char long_slots; + struct nls_table *nls_io = MSDOS_SB(sb)->nls_io; + struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk; + struct nls_unicode bufuname[14], *ptuname = &bufuname[0]; + unsigned char long_slots, *unicode = NULL; + char c, bufname[56], *ptname = bufname; + unsigned long lpos, dummy, *furrfu = &lpos; int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate; + int isvfat = MSDOS_SB(sb)->options.isvfat; int utf8 = MSDOS_SB(sb)->options.utf8; - unsigned char *unicode = NULL; - struct nls_table *nls = MSDOS_SB(sb)->nls_io; - char bufname[14]; - char *ptname = bufname; - int dotoffset = 0; - unsigned long *furrfu = &lpos; - unsigned long dummy; + int ino,inum,i,i2,last, dotoffset = 0; + loff_t cpos; cpos = filp->f_pos; /* Fake . and .. for the root directory. */ @@ -322,7 +342,7 @@ if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1) goto EODir; /* Check for long filename entry */ - if (MSDOS_SB(sb)->options.isvfat) { + if (isvfat) { if (de->name[0] == (__s8) DELETED_FLAG) goto RecEnd; if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME)) @@ -334,7 +354,7 @@ goto RecEnd; } - if (MSDOS_SB(sb)->options.isvfat && de->attr == ATTR_EXT) { + if (isvfat && de->attr == ATTR_EXT) { struct msdos_dir_slot *ds; int offset; unsigned char id; @@ -403,23 +423,27 @@ } if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) { + *ptuname = fat_short2lower_uni(nls_disk, '.'); *ptname++ = '.'; dotoffset = 1; } for (i = 0, last = 0; i < 8;) { if (!(c = de->name[i])) break; - if (c >= 'A' && c <= 'Z') c += 32; /* see namei.c, msdos_format_name */ if (c == 0x05) c = 0xE5; - if ((ptname[i++] = c) != ' ') + ptuname[i] = fat_short2lower_uni(nls_disk, c); + ptname[i++] = (c>='A' && c<='Z') ? c+32 : c; + if (c != ' ') last = i; } i = last; + ptuname[i] = fat_short2lower_uni(nls_disk, '.'); ptname[i++] = '.'; for (i2 = 0; i2 < 3; i2++) { if (!(c = de->ext[i2])) break; - if (c >= 'A' && c <= 'Z') c += 32; - if ((ptname[i++] = c) != ' ') + ptuname[i] = fat_short2lower_uni(nls_disk, c); + ptname[i++] = (c>='A' && c<='Z') ? c+32 : c; + if (c != ' ') last = i; } if (!last) @@ -442,6 +466,13 @@ inum = iunique(sb, MSDOS_ROOT_INO); } + if (isvfat) { + memset(&bufuname[i], 0, sizeof(struct nls_unicode)); + i = utf8 ? utf8_wcstombs(bufname, (__u16 *) &bufuname, 56) + : uni16_to_x8(bufname, (unsigned char *) &bufuname, + uni_xlate, nls_io); + } + if (!long_slots||shortnames) { if (both) bufname[i] = '\0'; @@ -451,7 +482,7 @@ char longname[275]; unsigned char long_len = utf8 ? utf8_wcstombs(longname, (__u16 *) unicode, 275) - : uni16_to_x8(longname, unicode, uni_xlate, nls); + : uni16_to_x8(longname, unicode, uni_xlate, nls_io); if (both) { memcpy(&longname[long_len+1], bufname, i); long_len += i; diff -u --recursive --new-file v2.3.33/linux/fs/fat/inode.c linux/fs/fat/inode.c --- v2.3.33/linux/fs/fat/inode.c Sun Nov 7 16:37:34 1999 +++ linux/fs/fat/inode.c Mon Dec 20 14:41:47 1999 @@ -653,13 +653,13 @@ if (sbi->options.isvfat && !opts.utf8) { p = opts.iocharset ? opts.iocharset : "iso8859-1"; sbi->nls_io = load_nls(p); - if (! sbi->nls_io) { + if (! sbi->nls_io) /* Fail only if explicit charset specified */ if (opts.iocharset) goto out_unload_nls; - sbi->nls_io = load_nls_default(); - } } + if (! sbi->nls_io) + sbi->nls_io = load_nls_default(); root_inode=get_empty_inode(); if (!root_inode) @@ -681,8 +681,7 @@ out_no_root: printk("get root inode failed\n"); iput(root_inode); - if (sbi->nls_io) - unload_nls(sbi->nls_io); + unload_nls(sbi->nls_io); out_unload_nls: unload_nls(sbi->nls_disk); goto out_fail; diff -u --recursive --new-file v2.3.33/linux/fs/isofs/inode.c linux/fs/isofs/inode.c --- v2.3.33/linux/fs/isofs/inode.c Thu Nov 18 20:25:37 1999 +++ linux/fs/isofs/inode.c Mon Dec 20 14:53:53 1999 @@ -927,7 +927,8 @@ int isofs_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create) { - off_t b_off, offset, sect_size; + unsigned long b_off; + unsigned offset, sect_size; unsigned int firstext; unsigned long nextino; int i, err; @@ -942,30 +943,21 @@ if (iblock < 0) goto abort_negative; - b_off = iblock << ISOFS_BUFFER_BITS(inode); + b_off = iblock; - /* If we are beyond the end of this file, don't give out any - * blocks. + /* If we are *way* beyond the end of the file, print a message. + * Access beyond the end of the file up to the next page boundary + * is normal, however because of the way the page cache works. + * In this case, we just return 0 so that we can properly fill + * the page with useless information without generating any + * I/O errors. */ - if (b_off > inode->i_size) { - off_t max_legal_read_offset; - - /* If we are *way* beyond the end of the file, print a message. - * Access beyond the end of the file up to the next page boundary - * is normal, however because of the way the page cache works. - * In this case, we just return 0 so that we can properly fill - * the page with useless information without generating any - * I/O errors. - */ - max_legal_read_offset = (inode->i_size + PAGE_SIZE - 1) - & ~(PAGE_SIZE - 1); - if (b_off >= max_legal_read_offset) - goto abort_beyond_end; - } + if (b_off > ((inode->i_size + PAGE_SIZE - 1) >> ISOFS_BUFFER_BITS(inode))) + goto abort_beyond_end; offset = 0; firstext = inode->u.isofs_i.i_first_extent; - sect_size = inode->u.isofs_i.i_section_size; + sect_size = inode->u.isofs_i.i_section_size >> ISOFS_BUFFER_BITS(inode); nextino = inode->u.isofs_i.i_next_section_ino; i = 0; @@ -990,8 +982,7 @@ } bh_result->b_dev = inode->i_dev; - bh_result->b_blocknr = firstext + - ((b_off - offset) >> ISOFS_BUFFER_BITS(inode)); + bh_result->b_blocknr = firstext + b_off - offset; bh_result->b_state |= (1UL << BH_Mapped); err = 0; diff -u --recursive --new-file v2.3.33/linux/fs/minix/inode.c linux/fs/minix/inode.c --- v2.3.33/linux/fs/minix/inode.c Tue Aug 31 17:29:14 1999 +++ linux/fs/minix/inode.c Mon Dec 20 15:07:08 1999 @@ -177,6 +177,7 @@ kdev_t dev = s->s_dev; const char * errmsg; struct inode *root_inode; + unsigned int hblock; /* N.B. These should be compile-time tests. Unfortunately that is impossible. */ @@ -186,6 +187,11 @@ panic("bad V2 i-node size"); MOD_INC_USE_COUNT; + + hblock = get_hardblocksize(dev); + if (hblock && hblock > BLOCK_SIZE) + goto out_bad_hblock; + lock_super(s); set_blocksize(dev, BLOCK_SIZE); if (!(bh = bread(dev,1,BLOCK_SIZE))) @@ -322,11 +328,16 @@ brelse(bh); goto out_unlock; +out_bad_hblock: + printk("MINIX-fs: blocksize too small for device.\n"); + goto out; + out_bad_sb: printk("MINIX-fs: unable to read superblock\n"); out_unlock: - s->s_dev = 0; unlock_super(s); + out: + s->s_dev = 0; MOD_DEC_USE_COUNT; return NULL; } diff -u --recursive --new-file v2.3.33/linux/fs/partitions/Config.in linux/fs/partitions/Config.in --- v2.3.33/linux/fs/partitions/Config.in Tue Dec 14 01:27:24 1999 +++ linux/fs/partitions/Config.in Tue Dec 14 20:43:57 1999 @@ -3,61 +3,57 @@ # bool 'Advanced partition selection' CONFIG_PARTITION_ADVANCED if [ "$CONFIG_PARTITION_ADVANCED" = "y" ]; then - bool 'Alpha OSF partition support' CONFIG_OSF_PARTITION - bool 'Macintosh partition map support' CONFIG_MAC_PARTITION - bool 'PC BIOS (MSDOS partition tables) support' CONFIG_MSDOS_PARTITION + bool ' Acorn partition support' CONFIG_ACORN_PARTITION + if [ "$CONFIG_ACORN_PARTITION" != "n" ]; then +# bool ' Cumana partition support' CONFIG_ACORN_PARTITION_CUMANA + bool ' ICS partition support' CONFIG_ACORN_PARTITION_ICS + bool ' Native filecore partition support' CONFIG_ACORN_PARTITION_ADFS + bool ' PowerTec partition support' CONFIG_ACORN_PARTITION_POWERTEC + bool ' RISCiX partition support' CONFIG_ACORN_PARTITION_RISCIX + fi + bool ' Alpha OSF partition support' CONFIG_OSF_PARTITION + bool ' Amiga partition table support' CONFIG_AMIGA_PARTITION + bool ' Atari partition table support' CONFIG_ATARI_PARTITION + bool ' Macintosh partition map support' CONFIG_MAC_PARTITION + bool ' PC BIOS (MSDOS partition tables) support' CONFIG_MSDOS_PARTITION + if [ "$CONFIG_MSDOS_PARTITION" = "y" ]; then + bool ' BSD disklabel (FreeBSD partition tables) support' CONFIG_BSD_DISKLABEL + bool ' Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION + bool ' Unixware slices support' CONFIG_UNIXWARE_DISKLABEL + fi else - if [ "$ARCH" = "alpha" ]; then - define_bool CONFIG_OSF_PARTITION y - fi - if [ "$ARCH" = "ppc" -o "$CONFIG_MAC" = "y" ]; then - define_bool CONFIG_MAC_PARTITION y - fi - if [ "$CONFIG_AMIGA" != "y" -a "$CONFIG_ATARI" != "y" -a \ - "$CONFIG_MAC" != "y" ]; then - define_bool CONFIG_MSDOS_PARTITION y - fi -fi -if [ "$CONFIG_PARTITION_ADVANCED" = "y" -a \ - "$CONFIG_MSDOS_PARTITION" = "y" ]; then - bool ' BSD disklabel (FreeBSD partition tables) support' CONFIG_BSD_DISKLABEL - bool ' Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION - bool ' Unixware slices support' CONFIG_UNIXWARE_DISKLABEL + if [ "$ARCH" = "alpha" ]; then + define_bool CONFIG_OSF_PARTITION y + fi + if [ "$ARCH" = "ppc" -o "$CONFIG_MAC" = "y" ]; then + define_bool CONFIG_MAC_PARTITION y + fi + if [ "$CONFIG_AMIGA" != "y" -a "$CONFIG_ATARI" != "y" -a \ + "$CONFIG_MAC" != "y" ]; then + define_bool CONFIG_MSDOS_PARTITION y + fi + if [ "$CONFIG_AMIGA" = "y" ]; then + define_bool CONFIG_AMIGA_PARTITION y + fi + if [ "$CONFIG_ARCH_ACORN" = "y" ]; then + define_bool CONFIG_ACORN_PARTITION y + define_bool CONFIG_ACORN_PARTITION_ADFS y +# define_bool CONFIG_ACORN_PARTITION_CUMANA y + define_bool CONFIG_ACORN_PARTITION_ICS y + define_bool CONFIG_ACORN_PARTITION_POWERTEC y + define_bool CONFIG_ACORN_PARTITION_RISCIX y + fi + if [ "$CONFIG_ATARI" = "y" ]; then + define_bool CONFIG_ATARI_PARTITION y + fi fi if [ "$CONFIG_SGI" != "y" ]; then - bool 'SGI partition support' CONFIG_SGI_PARTITION + bool 'SGI partition support' CONFIG_SGI_PARTITION else - define_bool CONFIG_SGI_PARTITION y + define_bool CONFIG_SGI_PARTITION y fi if [ "$ARCH" != "sparc" -a "$ARCH" != "sparc64" ]; then - bool 'Sun partition tables support' CONFIG_SUN_PARTITION -else - define_bool CONFIG_SUN_PARTITION y -fi -if [ "$CONFIG_PARTITION_ADVANCED" = "y" ]; then - bool 'Amiga partition table support' CONFIG_AMIGA_PARTITION - bool 'Atari partition table support' CONFIG_ATARI_PARTITION - bool 'Acorn partition support' CONFIG_ACORN_PARTITION - if [ "$CONFIG_ACORN_PARTITION" != "n" ]; then - bool ' Native filecore partition support' CONFIG_ACORN_PARTITION_ADFS -# bool ' Cumana partition support' CONFIG_ACORN_PARTITION_CUMANA - bool ' ICS partition support' CONFIG_ACORN_PARTITION_ICS - bool ' PowerTec partition support' CONFIG_ACORN_PARTITION_POWERTEC - bool ' RISCiX partition support' CONFIG_ACORN_PARTITION_RISCIX - fi + bool 'Sun partition tables support' CONFIG_SUN_PARTITION else - if [ "$CONFIG_AMIGA" = "y" ]; then - define_bool CONFIG_AMIGA_PARTITION y - fi - if [ "$CONFIG_ARCH_ACORN" = "y" ]; then - define_bool CONFIG_ACORN_PARTITION y - define_bool CONFIG_ACORN_PARTITION_ADFS y -# define_bool CONFIG_ACORN_PARTITION_CUMANA y - define_bool CONFIG_ACORN_PARTITION_ICS y - define_bool CONFIG_ACORN_PARTITION_POWERTEC y - define_bool CONFIG_ACORN_PARTITION_RISCIX y - fi -fi -if [ "$CONFIG_ATARI" = "y" ]; then - define_bool CONFIG_ATARI_PARTITION y + define_bool CONFIG_SUN_PARTITION y fi diff -u --recursive --new-file v2.3.33/linux/fs/qnx4/file.c linux/fs/qnx4/file.c --- v2.3.33/linux/fs/qnx4/file.c Tue Dec 7 09:32:48 1999 +++ linux/fs/qnx4/file.c Thu Dec 16 13:57:05 1999 @@ -249,7 +249,7 @@ } else { set_bit(PG_uptodate, &page->flags); } - Unlock_Page(page); + UnlockPage(page); /* free_page(buf); */ return res; diff -u --recursive --new-file v2.3.33/linux/fs/read_write.c linux/fs/read_write.c --- v2.3.33/linux/fs/read_write.c Sun Nov 7 16:37:34 1999 +++ linux/fs/read_write.c Sat Dec 18 15:31:35 1999 @@ -39,11 +39,15 @@ static inline loff_t llseek(struct file *file, loff_t offset, int origin) { loff_t (*fn)(struct file *, loff_t, int); + loff_t retval; fn = default_llseek; if (file->f_op && file->f_op->llseek) fn = file->f_op->llseek; - return fn(file, offset, origin); + lock_kernel(); + retval = fn(file, offset, origin); + unlock_kernel(); + return retval; } asmlinkage off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin) @@ -53,7 +57,6 @@ struct dentry * dentry; struct inode * inode; - lock_kernel(); retval = -EBADF; file = fget(fd); if (!file) @@ -72,7 +75,6 @@ out_putf: fput(file); bad: - unlock_kernel(); return retval; } @@ -87,7 +89,6 @@ struct inode * inode; loff_t offset; - lock_kernel(); retval = -EBADF; file = fget(fd); if (!file) @@ -112,7 +113,6 @@ out_putf: fput(file); bad: - unlock_kernel(); return retval; } #endif @@ -264,7 +264,6 @@ struct file * file; ssize_t ret; - lock_kernel(); ret = -EBADF; file = fget(fd); @@ -275,7 +274,6 @@ fput(file); bad_file: - unlock_kernel(); return ret; } @@ -285,19 +283,16 @@ struct file * file; ssize_t ret; - lock_kernel(); ret = -EBADF; file = fget(fd); if (!file) goto bad_file; - if (file->f_op && file->f_op->write && (file->f_mode & FMODE_WRITE)) { + if (file->f_op && file->f_op->write && (file->f_mode & FMODE_WRITE)) ret = do_readv_writev(VERIFY_READ, file, vector, count); - } fput(file); bad_file: - unlock_kernel(); return ret; } @@ -312,8 +307,6 @@ struct file * file; ssize_t (*read)(struct file *, char *, size_t, loff_t *); - lock_kernel(); - ret = -EBADF; file = fget(fd); if (!file) @@ -333,7 +326,6 @@ out: fput(file); bad_file: - unlock_kernel(); return ret; } @@ -344,8 +336,6 @@ struct file * file; ssize_t (*write)(struct file *, const char *, size_t, loff_t *); - lock_kernel(); - ret = -EBADF; file = fget(fd); if (!file) @@ -366,6 +356,5 @@ out: fput(file); bad_file: - unlock_kernel(); return ret; } diff -u --recursive --new-file v2.3.33/linux/fs/stat.c linux/fs/stat.c --- v2.3.33/linux/fs/stat.c Thu Aug 26 13:05:40 1999 +++ linux/fs/stat.c Thu Dec 16 16:25:37 1999 @@ -280,3 +280,124 @@ unlock_kernel(); return error; } + + +/* ---------- LFS-64 ----------- */ +#if BITS_PER_LONG == 32 + +static long cp_new_stat64(struct inode * inode, struct stat64 * statbuf) +{ + struct stat64 tmp; + unsigned int blocks, indirect; + + memset(&tmp, 0, sizeof(tmp)); + tmp.st_dev = kdev_t_to_nr(inode->i_dev); + tmp.st_ino = inode->i_ino; + tmp.st_mode = inode->i_mode; + tmp.st_nlink = inode->i_nlink; + tmp.st_uid = inode->i_uid; + tmp.st_gid = inode->i_gid; + tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); + tmp.st_atime = inode->i_atime; + tmp.st_mtime = inode->i_mtime; + tmp.st_ctime = inode->i_ctime; + tmp.st_size = inode->i_size; +/* + * st_blocks and st_blksize are approximated with a simple algorithm if + * they aren't supported directly by the filesystem. The minix and msdos + * filesystems don't keep track of blocks, so they would either have to + * be counted explicitly (by delving into the file itself), or by using + * this simple algorithm to get a reasonable (although not 100% accurate) + * value. + */ + +/* + * Use minix fs values for the number of direct and indirect blocks. The + * count is now exact for the minix fs except that it counts zero blocks. + * Everything is in units of BLOCK_SIZE until the assignment to + * tmp.st_blksize. + */ +#define D_B 7 +#define I_B (BLOCK_SIZE / sizeof(unsigned short)) + + if (!inode->i_blksize) { + blocks = (tmp.st_size + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS; + if (blocks > D_B) { + indirect = (blocks - D_B + I_B - 1) / I_B; + blocks += indirect; + if (indirect > 1) { + indirect = (indirect - 1 + I_B - 1) / I_B; + blocks += indirect; + if (indirect > 1) + blocks++; + } + } + tmp.st_blocks = (BLOCK_SIZE / 512) * blocks; + tmp.st_blksize = BLOCK_SIZE; + } else { + tmp.st_blocks = inode->i_blocks; + tmp.st_blksize = inode->i_blksize; + } + return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; +} + +asmlinkage long sys_stat64(char * filename, struct stat64 * statbuf, long flags) +{ + struct dentry * dentry; + int error; + + lock_kernel(); + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + error = do_revalidate(dentry); + if (!error) + error = cp_new_stat64(dentry->d_inode, statbuf); + + dput(dentry); + } + unlock_kernel(); + return error; +} + +asmlinkage long sys_lstat64(char * filename, struct stat64 * statbuf, long flags) +{ + struct dentry * dentry; + int error; + + lock_kernel(); + dentry = lnamei(filename); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + error = do_revalidate(dentry); + if (!error) + error = cp_new_stat64(dentry->d_inode, statbuf); + + dput(dentry); + } + unlock_kernel(); + return error; +} + +asmlinkage long sys_fstat64(unsigned long fd, struct stat64 * statbuf, long flags) +{ + struct file * f; + int err = -EBADF; + + lock_kernel(); + f = fget(fd); + if (f) { + struct dentry * dentry = f->f_dentry; + + err = do_revalidate(dentry); + if (!err) + err = cp_new_stat64(dentry->d_inode, statbuf); + fput(f); + } + unlock_kernel(); + return err; +} + +#endif /* BITS_PER_LONG == 32 */ diff -u --recursive --new-file v2.3.33/linux/fs/udf/balloc.c linux/fs/udf/balloc.c --- v2.3.33/linux/fs/udf/balloc.c Tue Nov 23 22:42:21 1999 +++ linux/fs/udf/balloc.c Mon Dec 20 16:07:09 1999 @@ -104,7 +104,7 @@ return retval; } -static int load__block_bitmap(struct super_block * sb, unsigned int block_group) +static int __load_block_bitmap(struct super_block * sb, unsigned int block_group) { int i, j, retval = 0; unsigned long block_bitmap_number; @@ -188,7 +188,7 @@ } else { - slot = load__block_bitmap(sb, block_group); + slot = __load_block_bitmap(sb, block_group); } if (slot < 0) diff -u --recursive --new-file v2.3.33/linux/fs/vfat/namei.c linux/fs/vfat/namei.c --- v2.3.33/linux/fs/vfat/namei.c Tue Dec 7 09:32:49 1999 +++ linux/fs/vfat/namei.c Mon Dec 20 14:41:47 1999 @@ -8,6 +8,8 @@ * VFAT filesystem to . Specify * what file operation caused you trouble and if you can duplicate * the problem, send a script that demonstrates it. + * + * Short name translation 1999 by Wolfram Pienkoss */ #define __NO_VERSION__ @@ -161,6 +163,69 @@ return 1; } +static inline unsigned char +vfat_getlower(struct nls_table *t, unsigned char c) +{ + return t->charset2lower[c]; +} + +static inline unsigned char +vfat_tolower(struct nls_table *t, unsigned char c) +{ + unsigned char nc = t->charset2lower[c]; + + return nc ? nc : c; +} + +static inline unsigned char +vfat_getupper(struct nls_table *t, unsigned char c) +{ + return t->charset2upper[c]; +} + +static inline unsigned char +vfat_toupper(struct nls_table *t, unsigned char c) +{ + unsigned char nc = t->charset2upper[c]; + + return nc ? nc : c; +} + +static int +vfat_strnicmp(struct nls_table *t, const unsigned char *s1, + const unsigned char *s2, int len) +{ + while(len--) + if (vfat_tolower(t, *s1++) != vfat_tolower(t, *s2++)) + return 1; + + return 0; +} + +static inline unsigned char +vfat_uni2short(struct nls_table *t, struct nls_unicode uc) +{ + unsigned char *up; + + up = t->page_uni2charset[uc.uni2]; + if (up) + return up[uc.uni1]; + + return 0; +} + +static inline unsigned char +vfat_uni2upper_short(struct nls_table *t, struct nls_unicode uc) +{ + unsigned char *up; + + up = t->page_uni2charset[uc.uni2]; + if (up) + return vfat_toupper(t, up[uc.uni1]); + + return 0; +} + /* * Compute the hash for the vfat name corresponding to the dentry. * Note: if the name is invalid, we leave the hash code unchanged so @@ -190,9 +255,9 @@ */ static int vfat_hashi(struct dentry *dentry, struct qstr *qstr) { + struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io; const char *name; int len; - char c; unsigned long hash; len = qstr->len; @@ -201,10 +266,8 @@ len--; hash = init_name_hash(); - while (len--) { - c = tolower(*name++); - hash = partial_name_hash(tolower(c), hash); - } + while (len--) + hash = partial_name_hash(vfat_tolower(t, *name++), hash); qstr->hash = end_name_hash(hash); return 0; @@ -215,6 +278,7 @@ */ static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b) { + struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io; int alen, blen; /* A filename cannot end in '.' or we treat it like it has none */ @@ -225,7 +289,7 @@ while (blen && b->name[blen-1] == '.') blen--; if (alen == blen) { - if (strnicmp(a->name, b->name, alen) == 0) + if (vfat_strnicmp(t, a->name, b->name, alen) == 0) return 0; } return 1; @@ -339,8 +403,6 @@ unsigned char c; int i, baselen; - if (IS_FREE(name)) return -EINVAL; - if (len && name[len-1] == ' ') return -EINVAL; if (len >= 256) return -EINVAL; for (i = 0; i < len; i++) { @@ -369,44 +431,47 @@ return 0; } -static int vfat_valid_shortname(const char *name,int len,int utf8) +static int vfat_valid_shortname(struct nls_table *nls, struct nls_unicode *name, + int len) { - const char *walk; - unsigned char c; + struct nls_unicode *walk; + unsigned char c, l; int space; - int baselen; + + c = vfat_uni2upper_short(nls, *name); + if (IS_FREE(&c)) + return -EINVAL; space = 1; /* disallow names starting with a dot */ - c = 0; for (walk = name; len && walk-name < 8;) { - c = *walk++; len--; - if (utf8 && (c & 0x80)) return -EINVAL; + l = vfat_uni2short(nls, *walk++); + c = vfat_getupper(nls, l); + if (!c) return -EINVAL; + if (l != vfat_tolower(nls, c)) return -EINVAL; if (strchr(replace_chars,c)) return -EINVAL; - if (c >= 'A' && c <= 'Z') return -EINVAL; if (c < ' '|| c==':') return -EINVAL; if (c == '.') break; space = c == ' '; } if (space) return -EINVAL; if (len && c != '.') { - c = *walk++; len--; + c = vfat_uni2upper_short(nls, *walk++); if (c != '.') return -EINVAL; } - baselen = walk - name; if (c == '.') { - baselen--; if (len >= 4) return -EINVAL; while (len > 0) { - c = *walk++; len--; - if (utf8 && (c & 0x80)) return -EINVAL; + l = vfat_uni2short(nls, *walk++); + c = vfat_getupper(nls, l); + if (!c) return -EINVAL; + if (l != vfat_tolower(nls, c)) return -EINVAL; if (strchr(replace_chars,c)) return -EINVAL; if (c < ' ' || c == '.'|| c==':') return -EINVAL; - if (c >= 'A' && c <= 'Z') return -EINVAL; space = c == ' '; } if (space) return -EINVAL; @@ -428,36 +493,41 @@ return 0; } -static int vfat_format_name(const char *name,int len,char *res,int utf8) +static int vfat_format_name(struct nls_table *nls, struct nls_unicode *name, + int len, char *res) { char *walk; unsigned char c; int space; + c = vfat_uni2upper_short(nls, *name); + if (IS_FREE(&c)) + return -EINVAL; + space = 1; /* disallow names starting with a dot */ - for (walk = res; len-- && (c=*name++)!='.' ; walk++) { + for (walk = res; len--; walk++) { + c = vfat_uni2upper_short(nls, *name++); + if (c == '.') break; + if (!c) return -EINVAL; if (walk-res == 8) return -EINVAL; - if (utf8 && (c & 0x80)) return -EINVAL; if (strchr(replace_chars,c)) return -EINVAL; - if (c >= 'A' && c <= 'Z') return -EINVAL; if (c < ' '|| c==':') return -EINVAL; space = c == ' '; - *walk = c >= 'a' && c <= 'z' ? c-32 : c; + *walk = c; } if (space) return -EINVAL; if (len >= 0) { while (walk-res < 8) *walk++ = ' '; while (len > 0 && walk-res < MSDOS_NAME) { - c = *name++; + c = vfat_uni2upper_short(nls, *name++); len--; - if (utf8 && (c & 0x80)) return -EINVAL; + if (!c) return -EINVAL; if (strchr(replace_chars,c)) return -EINVAL; if (c < ' ' || c == '.'|| c==':') return -EINVAL; - if (c >= 'A' && c <= 'Z') return -EINVAL; space = c == ' '; - *walk++ = c >= 'a' && c <= 'z' ? c-32 : c; + *walk++ = c; } if (space) return -EINVAL; if (len) return -EINVAL; @@ -472,44 +542,35 @@ /* Given a valid longname, create a unique shortname. Make sure the * shortname does not exist */ -static int vfat_create_shortname(struct inode *dir, const char *name, - int len, char *name_res, int utf8) -{ - const char *ip, *ext_start, *end; - char *p; - int sz, extlen, baselen; - char msdos_name[13]; - char base[9], ext[4]; - int i; - char buf[8]; - const char *name_start; +static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, + struct nls_unicode *name, int len, + char *name_res) +{ + struct nls_unicode *ip, *op, *ext_start, *end, *name_start; + struct nls_unicode msdos_name[13]; + char base[9], ext[4], buf[8], *p; + int sz, extlen, baselen, i; - PRINTK2(("Entering vfat_create_shortname: name=%s, len=%d\n", name, len)); + PRINTK2(("Entering vfat_create_shortname\n")); sz = 0; /* Make compiler happy */ - if (len && name[len-1]==' ') return -EINVAL; if (len <= 12) { /* Do a case insensitive search if the name would be a valid * shortname if is were all capitalized. However, do not * allow spaces in short names because Win95 scandisk does * not like that */ - for (i = 0, p = msdos_name, ip = name; ; i++, p++, ip++) { + for (i = 0, op = &msdos_name[0], ip = name; ; i++, ip++, op++) { if (i == len) { - if (vfat_format_name(msdos_name, - len, name_res, utf8) < 0) + if (vfat_format_name(nls, &msdos_name[0], len, + name_res) < 0) break; PRINTK3(("vfat_create_shortname 1\n")); if (vfat_find_form(dir, name_res) < 0) return 0; return -EEXIST; } - - if (*ip == ' ') + if (vfat_uni2upper_short(nls, *ip) == ' ') break; - if (*ip >= 'A' && *ip <= 'Z') { - *p = *ip + 32; - } else { - *p = *ip; - } + *op = *ip; } } @@ -517,7 +578,7 @@ /* Now, we need to create a shortname from the long name */ ext_start = end = &name[len]; while (--ext_start >= name) { - if (*ext_start == '.') { + if (vfat_uni2upper_short(nls, *ext_start) == '.') { if (ext_start == end - 1) { sz = len; ext_start = NULL; @@ -537,7 +598,11 @@ name_start = &name[0]; while (name_start < ext_start) { - if (!strchr(skip_chars,*name_start)) break; + unsigned char c = vfat_uni2upper_short(nls, *name_start); + if (!c) + break; + if (!strchr(skip_chars, c)) + break; name_start++; } if (name_start != ext_start) { @@ -551,16 +616,15 @@ for (baselen = i = 0, p = base, ip = name; i < sz && baselen < 8; i++) { - if (utf8 && (*ip & 0x80)) { - *p++ = '_'; + unsigned char c = vfat_uni2upper_short(nls, *ip); + if (!c) { + *p++ = c = '_'; baselen++; - } else if (!strchr(skip_chars, *ip)) { - if (*ip >= 'a' && *ip <= 'z') { - *p = *ip - 32; - } else { - *p = *ip; - } - if (strchr(replace_chars, *p)) *p='_'; + } else if (!strchr(skip_chars, c)) { + if (strchr(replace_chars, c)) + *p = '_'; + else + *p = c; p++; baselen++; } ip++; @@ -572,18 +636,16 @@ extlen = 0; if (ext_start) { for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) { - if (utf8 && (*ip & 0x80)) { - *p++ = '_'; + unsigned char c = vfat_uni2upper_short(nls, *ip); + if (!c) { + *p++ = c = '_'; extlen++; - } else if (!strchr(skip_chars, *ip)) { - if (*ip >= 'a' && *ip <= 'z') { - *p = *ip - 32; - } else { - *p = *ip; - } - if (strchr(replace_chars, *p)) *p='_'; - extlen++; - p++; + } else if (!strchr(skip_chars, c)) { + if (strchr(replace_chars, c)) + *p = '_'; + else + *p = c; + p++; extlen++; } } } @@ -641,14 +703,14 @@ /* Translate a string, including coded sequences into Unicode */ static int -xlate_to_uni(const char *name, int len, char *outname, int *outlen, +xlate_to_uni(const char *name, int len, char *outname, int *longlen, int *outlen, int escape, int utf8, struct nls_table *nls) { - int i; const unsigned char *ip; + unsigned char nc; char *op; - int fill; - unsigned char c1, c2, c3; + unsigned int ec; + int i, k, fill; if (utf8) { *outlen = utf8_mbstowcs((__u16 *) outname, name, PAGE_SIZE); @@ -658,25 +720,40 @@ } else { if (name[len-1] == '.') len--; - op = outname; if (nls) { for (i = 0, ip = name, op = outname, *outlen = 0; - i < len && *outlen <= 260; i++, *outlen += 1) + i < len && *outlen <= 260; *outlen += 1) { if (escape && (*ip == ':')) { - if (i > len - 4) return -EINVAL; - c1 = fat_esc2uni[ip[1]]; - c2 = fat_esc2uni[ip[2]]; - c3 = fat_esc2uni[ip[3]]; - if (c1 == 255 || c2 == 255 || c3 == 255) + if (i > len - 5) + return -EINVAL; + ec = 0; + for (k = 1; k < 5; k++) { + nc = ip[k]; + ec <<= 4; + if (nc >= '0' && nc <= '9') { + ec |= nc - '0'; + continue; + } + if (nc >= 'a' && nc <= 'f') { + ec |= nc - ('a' - 10); + continue; + } + if (nc >= 'A' && nc <= 'F') { + ec |= nc - ('A' - 10); + continue; + } return -EINVAL; - *op++ = (c1 << 4) + (c2 >> 2); - *op++ = ((c2 & 0x3) << 6) + c3; - ip += 4; + } + *op++ = ec & 0xFF; + *op++ = ec >> 8; + ip += 5; + i += 5; } else { *op++ = nls->charset2uni[*ip].uni1; *op++ = nls->charset2uni[*ip].uni2; ip++; + i++; } } } else { @@ -691,6 +768,7 @@ if (*outlen > 260) return -ENAMETOOLONG; + *longlen = *outlen; if (*outlen % 13) { *op++ = 0; *op++ = 0; @@ -709,37 +787,49 @@ } static int -vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len, - char *msdos_name, int *slots, - int uni_xlate, int utf8, struct nls_table *nls) +vfat_fill_slots(struct inode *dir, struct msdos_dir_slot *ds, const char *name, + int len, int *slots, int uni_xlate) { + struct nls_table *nls_io, *nls_disk; + struct nls_unicode *uname; struct msdos_dir_slot *ps; struct msdos_dir_entry *de; - int res; - int slot; + unsigned long page; unsigned char cksum; - char *uniname; const char *ip; - unsigned long page; - int unilen; - int i; + char *uniname, msdos_name[MSDOS_NAME]; + int res, utf8, slot, ulen, unilen, i; loff_t offset; + de = (struct msdos_dir_entry *) ds; + utf8 = MSDOS_SB(dir->i_sb)->options.utf8; + nls_io = MSDOS_SB(dir->i_sb)->nls_io; + nls_disk = MSDOS_SB(dir->i_sb)->nls_disk; + if (name[len-1] == '.') len--; if(!(page = __get_free_page(GFP_KERNEL))) return -ENOMEM; uniname = (char *) page; - res = xlate_to_uni(name, len, uniname, &unilen, uni_xlate, utf8, nls); - if (res < 0) { - free_page(page); - return res; - } + res = xlate_to_uni(name, len, uniname, &ulen, &unilen, uni_xlate, + utf8, nls_io); + if (res < 0) + goto out_free; + + uname = (struct nls_unicode *) page; + if (vfat_valid_shortname(nls_disk, uname, ulen) >= 0) { + res = vfat_format_name(nls_disk, uname, ulen, de->name); + if (!res) + goto out_free; + } + res = vfat_create_shortname(dir, nls_disk, uname, ulen, msdos_name); + if (res) + goto out_free; *slots = unilen / 13; for (cksum = i = 0; i < 11; i++) { cksum = (((cksum&1)<<7)|((cksum&0xfe)>>1)) + msdos_name[i]; } - PRINTK3(("vfat_fill_long_slots 3: slots=%d\n",*slots)); + PRINTK3(("vfat_fill_slots 3: slots=%d\n",*slots)); for (ps = ds, slot = *slots; slot > 0; slot--, ps++) { ps->id = slot; @@ -756,12 +846,13 @@ ds[0].id |= 0x40; de = (struct msdos_dir_entry *) ps; - PRINTK3(("vfat_fill_long_slots 9\n")); + PRINTK3(("vfat_fill_slots 9\n")); strncpy(de->name, msdos_name, MSDOS_NAME); (*slots)++; +out_free: free_page(page); - return 0; + return res; } /* We can't get "." or ".." here - VFS takes care of those cases */ @@ -769,29 +860,14 @@ static int vfat_build_slots(struct inode *dir,const char *name,int len, struct msdos_dir_slot *ds, int *slots) { - struct msdos_dir_entry *de; - char msdos_name[MSDOS_NAME]; - int res, xlate, utf8; - struct nls_table *nls; + int res, xlate; - de = (struct msdos_dir_entry *) ds; xlate = MSDOS_SB(dir->i_sb)->options.unicode_xlate; - utf8 = MSDOS_SB(dir->i_sb)->options.utf8; - nls = MSDOS_SB(dir->i_sb)->nls_io; - *slots = 1; res = vfat_valid_longname(name, len, xlate); if (res < 0) return res; - if (vfat_valid_shortname(name, len, utf8) >= 0) { - vfat_format_name(name, len, de->name, utf8); - return 0; - } - res = vfat_create_shortname(dir, name, len, msdos_name, utf8); - if (res < 0) - return res; - return vfat_fill_long_slots(ds, name, len, msdos_name, slots, xlate, - utf8, nls); + return vfat_fill_slots(dir, ds, name, len, slots, xlate); } static int vfat_add_entry(struct inode *dir,struct qstr* qname, diff -u --recursive --new-file v2.3.33/linux/include/asm-alpha/pgtable.h linux/include/asm-alpha/pgtable.h --- v2.3.33/linux/include/asm-alpha/pgtable.h Wed Dec 15 10:43:17 1999 +++ linux/include/asm-alpha/pgtable.h Wed Dec 15 10:32:47 1999 @@ -296,7 +296,7 @@ #define kern_addr_valid(addr) (1) #define io_remap_page_range(start, busaddr, size, prot) \ - remap_page_range(start, virt_to_phys(__ioremap(busaddr, 0)), size, prot) + remap_page_range(start, virt_to_phys(__ioremap(busaddr)), size, prot) #define pte_ERROR(e) \ printk("%s:%d: bad pte %016lx.\n", __FILE__, __LINE__, pte_val(e)) diff -u --recursive --new-file v2.3.33/linux/include/asm-alpha/unistd.h linux/include/asm-alpha/unistd.h --- v2.3.33/linux/include/asm-alpha/unistd.h Tue Aug 31 17:29:14 1999 +++ linux/include/asm-alpha/unistd.h Mon Dec 20 14:41:47 1999 @@ -310,6 +310,7 @@ #define __NR_sendfile 370 #define __NR_setresgid 371 #define __NR_getresgid 372 +#define __NR_dipc 373 #if defined(__LIBRARY__) && defined(__GNUC__) diff -u --recursive --new-file v2.3.33/linux/include/asm-arm/stat.h linux/include/asm-arm/stat.h --- v2.3.33/linux/include/asm-arm/stat.h Tue Aug 4 16:07:17 1998 +++ linux/include/asm-arm/stat.h Thu Dec 16 16:25:37 1999 @@ -38,4 +38,5 @@ unsigned long __unused5; }; +/* Someone please add a glibc/arm compatible stat64 struct here. */ #endif diff -u --recursive --new-file v2.3.33/linux/include/asm-arm/unistd.h linux/include/asm-arm/unistd.h --- v2.3.33/linux/include/asm-arm/unistd.h Fri Oct 22 13:21:53 1999 +++ linux/include/asm-arm/unistd.h Thu Dec 16 16:25:37 1999 @@ -83,7 +83,7 @@ #define __NR_sigpending (__NR_SYSCALL_BASE+ 73) #define __NR_sethostname (__NR_SYSCALL_BASE+ 74) #define __NR_setrlimit (__NR_SYSCALL_BASE+ 75) -#define __NR_getrlimit (__NR_SYSCALL_BASE+ 76) +#define __NR_old_getrlimit (__NR_SYSCALL_BASE+ 76) #define __NR_getrusage (__NR_SYSCALL_BASE+ 77) #define __NR_gettimeofday (__NR_SYSCALL_BASE+ 78) #define __NR_settimeofday (__NR_SYSCALL_BASE+ 79) @@ -198,6 +198,13 @@ /* 188 reserved */ /* 189 reserved */ #define __NR_vfork (__NR_SYSCALL_BASE+190) +#define __NR_getrlimit (__NR_SYSCALL_BASE+191) +#define __NR_mmap2 (__NR_SYSCALL_BASE+192) +#define __NR_truncate64 (__NR_SYSCALL_BASE+193) +#define __NR_ftruncate64 (__NR_SYSCALL_BASE+194) +#define __NR_stat64 (__NR_SYSCALL_BASE+195) +#define __NR_lstat64 (__NR_SYSCALL_BASE+196) +#define __NR_fstat64 (__NR_SYSCALL_BASE+197) #define __sys2(x) #x #define __sys1(x) __sys2(x) diff -u --recursive --new-file v2.3.33/linux/include/asm-i386/hw_irq.h linux/include/asm-i386/hw_irq.h --- v2.3.33/linux/include/asm-i386/hw_irq.h Tue Dec 7 09:32:49 1999 +++ linux/include/asm-i386/hw_irq.h Mon Dec 20 16:03:12 1999 @@ -124,8 +124,8 @@ "pushl %ecx\n\t" \ "pushl %ebx\n\t" \ "movl $" STR(__KERNEL_DS) ",%edx\n\t" \ - "movl %dx,%ds\n\t" \ - "movl %dx,%es\n\t" + "movl %edx,%ds\n\t" \ + "movl %edx,%es\n\t" #define IRQ_NAME2(nr) nr##_interrupt(void) #define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr) diff -u --recursive --new-file v2.3.33/linux/include/asm-i386/page.h linux/include/asm-i386/page.h --- v2.3.33/linux/include/asm-i386/page.h Tue Dec 7 09:32:49 1999 +++ linux/include/asm-i386/page.h Wed Dec 15 10:32:47 1999 @@ -69,8 +69,8 @@ * a virtual address space of one gigabyte, which limits the * amount of physical memory you can use to about 950MB. * - * If you want more physical memory than this then see the CONFIG_BIGMEM - * option in the kernel configuration. + * If you want more physical memory than this then see the CONFIG_HIGHMEM4G + * amd CONFIG_HIGHMEM64G options in the kernel configuration. */ #define __PAGE_OFFSET (0xC0000000) diff -u --recursive --new-file v2.3.33/linux/include/asm-i386/stat.h linux/include/asm-i386/stat.h --- v2.3.33/linux/include/asm-i386/stat.h Mon Sep 30 07:42:31 1996 +++ linux/include/asm-i386/stat.h Thu Dec 16 16:25:37 1999 @@ -38,4 +38,40 @@ unsigned long __unused5; }; +/* This matches struct stat64 in glibc2.1, hence the absolutely + * insane amounts of padding around dev_t's. + */ +struct stat64 { + unsigned short st_dev; + unsigned char __pad0[10]; + + unsigned long st_ino; + unsigned int st_mode; + unsigned int st_nlink; + + unsigned long st_uid; + unsigned long st_gid; + + unsigned short st_rdev; + unsigned char __pad3[10]; + + long long st_size; + unsigned long st_blksize; + + unsigned long st_blocks; /* Number 512-byte blocks allocated. */ + unsigned long __pad4; /* future possible st_blocks high bits */ + + unsigned long st_atime; + unsigned long __pad5; + + unsigned long st_mtime; + unsigned long __pad6; + + unsigned long st_ctime; + unsigned long __pad7; /* will be high 32 bits of ctime someday */ + + unsigned long __unused1; + unsigned long __unused2; +}; + #endif diff -u --recursive --new-file v2.3.33/linux/include/asm-i386/unistd.h linux/include/asm-i386/unistd.h --- v2.3.33/linux/include/asm-i386/unistd.h Wed Dec 8 14:11:28 1999 +++ linux/include/asm-i386/unistd.h Thu Dec 16 16:25:37 1999 @@ -199,6 +199,9 @@ #define __NR_mmap2 192 #define __NR_truncate64 193 #define __NR_ftruncate64 194 +#define __NR_stat64 195 +#define __NR_lstat64 196 +#define __NR_fstat64 197 /* user-visible error numbers are in the range -1 - -124: see */ diff -u --recursive --new-file v2.3.33/linux/include/asm-m68k/stat.h linux/include/asm-m68k/stat.h --- v2.3.33/linux/include/asm-m68k/stat.h Mon Oct 5 12:33:40 1998 +++ linux/include/asm-m68k/stat.h Thu Dec 16 16:25:37 1999 @@ -38,4 +38,8 @@ unsigned long __unused5; }; +/* stat64 struct goes here -- someone please make + * it mesh with whatever glibc does in userland on + * m68k's. + */ #endif /* _M68K_STAT_H */ diff -u --recursive --new-file v2.3.33/linux/include/asm-m68k/unistd.h linux/include/asm-m68k/unistd.h --- v2.3.33/linux/include/asm-m68k/unistd.h Mon Aug 9 12:27:31 1999 +++ linux/include/asm-m68k/unistd.h Thu Dec 16 16:25:37 1999 @@ -80,7 +80,7 @@ #define __NR_sigpending 73 #define __NR_sethostname 74 #define __NR_setrlimit 75 -#define __NR_getrlimit 76 +#define __NR_old_getrlimit 76 #define __NR_getrusage 77 #define __NR_gettimeofday 78 #define __NR_settimeofday 79 @@ -194,6 +194,13 @@ #define __NR_getpmsg 188 /* some people actually want streams */ #define __NR_putpmsg 189 /* some people actually want streams */ #define __NR_vfork 190 +#define __NR_getrlimit 191 +#define __NR_mmap2 192 +#define __NR_truncate64 193 +#define __NR_ftruncate64 194 +#define __NR_stat64 195 +#define __NR_lstat64 196 +#define __NR_fstat64 197 /* user-visible error numbers are in the range -1 - -122: see */ diff -u --recursive --new-file v2.3.33/linux/include/asm-ppc/cache.h linux/include/asm-ppc/cache.h --- v2.3.33/linux/include/asm-ppc/cache.h Tue Aug 31 17:29:14 1999 +++ linux/include/asm-ppc/cache.h Mon Dec 20 14:27:34 1999 @@ -6,7 +6,6 @@ #include #include -/*#include */ /* bytes per L1 cache line */ #define L1_CACHE_BYTES 32 @@ -25,37 +24,6 @@ #if defined(__KERNEL__) && !defined(__ASSEMBLY__) extern void flush_dcache_range(unsigned long start, unsigned long stop); - -static inline unsigned long unlock_dcache(void) -{ -#ifndef CONFIG_8xx - ulong hid0 = 0; - /* 601 doesn't do this */ - if ( (ulong) _get_PVR() == 1 ) - return 0; - asm("mfspr %0,1008 \n\t" : "=r" (hid0) ); - if ( !(hid0 & HID0_DLOCK) ) - return 0; - asm("mtspr 1008,%0 \n\t" :: "r" (hid0 & ~(HID0_DLOCK))); - return (hid0 & HID0_DLOCK) ? 1 : 0; -#else /* ndef CONFIG_8xx */ - return 0; -#endif -} - -static inline void lock_dcache(unsigned long lockit) -{ -#ifndef CONFIG_8xx - /* 601 doesn't do this */ - if ( !lockit || ((ulong) _get_PVR() == 1) ) - return; - asm("mfspr %0,1008 \n\t" - "ori %0,%0,%2 \n\t" - "mtspr 1008,%0 \n\t" - "sync \n\t isync \n\t" - : "=r" (lockit) : "0" (lockit), "i" (HID0_DLOCK)); -#endif /* ndef CONFIG_8xx */ -} #endif /* __ASSEMBLY__ */ diff -u --recursive --new-file v2.3.33/linux/include/asm-ppc/smp.h linux/include/asm-ppc/smp.h --- v2.3.33/linux/include/asm-ppc/smp.h Sat Oct 9 11:47:50 1999 +++ linux/include/asm-ppc/smp.h Mon Dec 20 14:27:34 1999 @@ -25,7 +25,8 @@ extern void smp_message_pass(int target, int msg, unsigned long data, int wait); extern void smp_store_cpu_info(int id); -extern void smp_message_recv(void); +extern void smp_message_recv(int); +void smp_send_tlb_invalidate(int); #define NO_PROC_ID 0xFF /* No processor magic marker */ #define PROC_CHANGE_PENALTY 20 diff -u --recursive --new-file v2.3.33/linux/include/asm-ppc/stat.h linux/include/asm-ppc/stat.h --- v2.3.33/linux/include/asm-ppc/stat.h Tue Aug 4 16:06:36 1998 +++ linux/include/asm-ppc/stat.h Mon Dec 20 14:27:34 1999 @@ -16,6 +16,7 @@ unsigned long st_mtime; unsigned long st_ctime; }; + struct stat { dev_t st_dev; ino_t st_ino; @@ -37,4 +38,39 @@ unsigned long __unused5; }; +/* This matches struct stat64 in glibc2.1, hence the absolutely + * insane amounts of padding around dev_t's. + */ +struct stat64 { + unsigned short st_dev; + unsigned char __pad0[10]; + + unsigned long st_ino; + unsigned int st_mode; + unsigned int st_nlink; + + unsigned long st_uid; + unsigned long st_gid; + + unsigned short st_rdev; + unsigned char __pad3[10]; + + long long st_size; + unsigned long st_blksize; + + unsigned long st_blocks; /* Number 512-byte blocks allocated. */ + unsigned long __pad4; /* future possible st_blocks high bits */ + + unsigned long st_atime; + unsigned long __pad5; + + unsigned long st_mtime; + unsigned long __pad6; + + unsigned long st_ctime; + unsigned long __pad7; /* will be high 32 bits of ctime someday */ + + unsigned long __unused1; + unsigned long __unused2; +}; #endif diff -u --recursive --new-file v2.3.33/linux/include/linux/apm_bios.h linux/include/linux/apm_bios.h --- v2.3.33/linux/include/linux/apm_bios.h Sun Nov 7 16:37:34 1999 +++ linux/include/linux/apm_bios.h Mon Dec 20 15:43:01 1999 @@ -124,9 +124,6 @@ extern int apm_register_callback(int (*callback)(apm_event_t)); extern void apm_unregister_callback(int (*callback)(apm_event_t)); -extern int apm_display_blank(void); -extern int apm_display_unblank(void); - #endif /* __KERNEL__ */ /* diff -u --recursive --new-file v2.3.33/linux/include/linux/fs.h linux/include/linux/fs.h --- v2.3.33/linux/include/linux/fs.h Tue Dec 14 01:27:24 1999 +++ linux/include/linux/fs.h Mon Dec 20 16:01:13 1999 @@ -250,7 +250,7 @@ #define bh_offset(bh) ((unsigned long)(bh)->b_data & ~PAGE_MASK) -extern void set_bh_page(struct buffer_head *bh, struct page *page, unsigned int offset); +extern void set_bh_page(struct buffer_head *bh, struct page *page, unsigned long offset); #define touch_buffer(bh) set_bit(PG_referenced, &bh->b_page->flags) diff -u --recursive --new-file v2.3.33/linux/include/linux/hdreg.h linux/include/linux/hdreg.h --- v2.3.33/linux/include/linux/hdreg.h Fri Oct 22 13:21:55 1999 +++ linux/include/linux/hdreg.h Mon Dec 20 16:01:12 1999 @@ -99,6 +99,8 @@ #define SETFEATURES_EN_WCACHE 0x02 /* Enable write cache */ #define SETFEATURES_XFER 0x03 /* Set transfer mode */ +# define XFER_UDMA_6 0x46 /* 0100|0110 */ +# define XFER_UDMA_5 0x45 /* 0100|0101 */ # define XFER_UDMA_4 0x44 /* 0100|0100 */ # define XFER_UDMA_3 0x43 /* 0100|0011 */ # define XFER_UDMA_2 0x42 /* 0100|0010 */ diff -u --recursive --new-file v2.3.33/linux/include/linux/i2c-algo-bit.h linux/include/linux/i2c-algo-bit.h --- v2.3.33/linux/include/linux/i2c-algo-bit.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/i2c-algo-bit.h Thu Dec 16 13:59:38 1999 @@ -0,0 +1,53 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-algo-bit.h i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +#ifndef I2C_ALGO_BIT_H +#define I2C_ALGO_BIT_H 1 + +#include + +/* --- Defines for bit-adapters --------------------------------------- */ +/* + * This struct contains the hw-dependent functions of bit-style adapters to + * manipulate the line states, and to init any hw-specific features. This is + * only used if you have more than one hw-type of adapter running. + */ +struct i2c_algo_bit_data { + void *data; /* private data for lowlevel routines */ + void (*setsda) (void *data, int state); + void (*setscl) (void *data, int state); + int (*getsda) (void *data); + int (*getscl) (void *data); + + /* local settings */ + int udelay; + int mdelay; + int timeout; +}; + +#define I2C_BIT_ADAP_MAX 16 + +int i2c_bit_add_bus(struct i2c_adapter *); +int i2c_bit_del_bus(struct i2c_adapter *); + +#endif /* I2C_ALGO_BIT_H */ diff -u --recursive --new-file v2.3.33/linux/include/linux/i2c-algo-pcf.h linux/include/linux/i2c-algo-pcf.h --- v2.3.33/linux/include/linux/i2c-algo-pcf.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/i2c-algo-pcf.h Thu Dec 16 13:59:38 1999 @@ -0,0 +1,50 @@ +/* ------------------------------------------------------------------------- */ +/* adap-pcf.h i2c driver algorithms for PCF8584 adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +#ifndef I2C_ALGO_PCF_H +#define I2C_AGLO_PCF_H 1 + +/* --- Defines for pcf-adapters --------------------------------------- */ +#include + +struct i2c_algo_pcf_data { + void *data; /* private data for lolevel routines */ + void (*setpcf) (void *data, int ctl, int val); + int (*getpcf) (void *data, int ctl); + int (*getown) (void *data); + int (*getclock) (void *data); + void (*waitforpin) (void); + + /* local settings */ + int udelay; + int mdelay; + int timeout; +}; + +#define I2C_PCF_ADAP_MAX 16 + +int i2c_pcf_add_bus(struct i2c_adapter *); +int i2c_pcf_del_bus(struct i2c_adapter *); + +#endif /* I2C_ALGO_PCF_H */ diff -u --recursive --new-file v2.3.33/linux/include/linux/i2c-dev.h linux/include/linux/i2c-dev.h --- v2.3.33/linux/include/linux/i2c-dev.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/i2c-dev.h Thu Dec 16 13:59:38 1999 @@ -0,0 +1,154 @@ +/* + i2c-dev.h - i2c-bus driver, char device interface + + Copyright (C) 1995-97 Simon G. Vogl + Copyright (C) 1998-99 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef I2C_DEV_H +#define I2C_DEV_H + + +#include +#include + +/* Some IOCTL commands are defined in */ +/* Note: 10-bit addresses are NOT supported! */ + +/* This is the structure as used in the I2C_SMBUS ioctl call */ +struct i2c_smbus_ioctl_data { + char read_write; + __u8 command; + int size; + union i2c_smbus_data *data; +}; + +#ifndef __KERNEL__ + +#include + +extern inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_smbus_ioctl_data args; + + args.read_write = read_write; + args.command = command; + args.size = size; + args.data = data; + return ioctl(file,I2C_SMBUS,&args); +} + + +extern inline __s32 i2c_smbus_write_quick(int file, __u8 value) +{ + return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL); +} + +extern inline __s32 i2c_smbus_read_byte(int file) +{ + union i2c_smbus_data data; + if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern inline __s32 i2c_smbus_write_byte(int file, __u8 value) +{ + return i2c_smbus_access(file,I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,NULL); +} + +extern inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_access(file,I2C_SMBUS_READ,command,I2C_SMBUS_BYTE_DATA,&data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, + __u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,I2C_SMBUS_BYTE_DATA, + &data); +} + +extern inline __s32 i2c_smbus_read_word_data(int file, __u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_access(file,I2C_SMBUS_READ,command,I2C_SMBUS_WORD_DATA,&data)) + return -1; + else + return 0x0FFFF & data.word; +} + +extern inline __s32 i2c_smbus_write_word_data(int file, __u8 command, + __u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,I2C_SMBUS_WORD_DATA, + &data); +} + +extern inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value) +{ + union i2c_smbus_data data; + data.word = value; + if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,I2C_SMBUS_PROC_CALL,&data)) + return -1; + else + return 0x0FFFF & data.word; +} + + +/* Returns the number of read bytes */ +extern inline __s32 i2c_smbus_read_block_data(int file, __u8 command, + __u8 *values) +{ + union i2c_smbus_data data; + int i; + if (i2c_smbus_access(file,I2C_SMBUS_READ,command,I2C_SMBUS_BLOCK_DATA,&data)) + return -1; + else { + for (i = 1; i <= data.block[0]; i++) + values[i-1] = data.block[i]; + return data.block[0]; + } +} + +extern inline __s32 i2c_smbus_write_block_data(int file, __u8 command, + __u8 length, __u8 *values) +{ + union i2c_smbus_data data; + int i; + if (length > 32) + length = 32; + for (i = 1; i <= length; i++) + data.block[i] = values[i-1]; + data.block[0] = length; + return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,I2C_SMBUS_BLOCK_DATA, + &data); +} + +#endif /* ndef __KERNEL__ */ + +#endif diff -u --recursive --new-file v2.3.33/linux/include/linux/i2c-elektor.h linux/include/linux/i2c-elektor.h --- v2.3.33/linux/include/linux/i2c-elektor.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/i2c-elektor.h Thu Dec 16 13:59:38 1999 @@ -0,0 +1,41 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +#ifndef I2C_PCF_ELEKTOR_H +#define I2C_PCF_ELEKTOR_H 1 + +/* + * This struct contains the hw-dependent functions of PCF8584 adapters to + * manipulate the registers, and to init any hw-specific features. + */ + +struct i2c_pcf_isa { + int pi_base; + int pi_irq; + int pi_clock; + int pi_own; +}; + + +#endif /* PCF_ELEKTOR_H */ diff -u --recursive --new-file v2.3.33/linux/include/linux/i2c-id.h linux/include/linux/i2c-id.h --- v2.3.33/linux/include/linux/i2c-id.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/i2c-id.h Thu Dec 16 13:59:38 1999 @@ -0,0 +1,134 @@ +/* ------------------------------------------------------------------------- */ +/* */ +/* i2c.h - definitions for the i2c-bus interface */ +/* */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-1999 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- */ +/* $Revision: 1.4 $ $Date: 1999/12/02 02:05:34 $*/ +/* ------------------------------------------------------------------------- */ + +#ifndef I2C_ID_H +#define I2C_ID_H +/* + * This file is part of the i2c-bus package and contains the identifier + * values for drivers, adapters and other folk populating these serial + * worlds. + * + * These will change often (i.e. additions) , therefore this has been + * separated from the functional interface definitions of the i2c api. + * + */ + +/* + * ---- Driver types ----------------------------------------------------- + * device id name + number function description, i2c address(es) + * + * Range 1000-1999 range is defined in sensors/sensors.h + * Range 0x100 - 0x1ff is for V4L2 Common Components + * Range 0xf000 - 0xffff is reserved for local experimentation, and should + * never be used in official drivers + */ + +#define I2C_DRIVERID_MSP3400 1 +#define I2C_DRIVERID_TUNER 2 +#define I2C_DRIVERID_VIDEOTEXT 3 /* please rename */ +#define I2C_DRIVERID_TDA8425 4 /* stereo sound processor */ +#define I2C_DRIVERID_TEA6420 5 /* audio matrix switch */ +#define I2C_DRIVERID_TEA6415C 6 /* video matrix switch */ +#define I2C_DRIVERID_TDA9840 7 /* stereo sound processor */ +#define I2C_DRIVERID_SAA7111A 8 /* video input processor */ +#define I2C_DRIVERID_SAA5281 9 /* videotext decoder */ +#define I2C_DRIVERID_SAA7112 10 /* video decoder, image scaler */ +#define I2C_DRIVERID_SAA7120 11 /* video encoder */ +#define I2C_DRIVERID_SAA7121 12 /* video encoder */ +#define I2C_DRIVERID_SAA7185B 13 /* video encoder */ +#define I2C_DRIVERID_CH7003 14 /* digital pc to tv encoder */ +#define I2C_DRIVERID_PCF8574A 15 /* i2c expander - 8 bit in/out */ +#define I2C_DRIVERID_PCF8582C 16 /* eeprom */ +#define I2C_DRIVERID_AT24Cxx 17 /* eeprom 1/2/4/8/16 K */ +#define I2C_DRIVERID_TEA6300 18 /* audio mixer */ +#define I2C_DRIVERID_BT829 19 /* pc to tv encoder */ +#define I2C_DRIVERID_TDA9850 20 /* audio mixer */ +#define I2C_DRIVERID_TDA9855 21 /* audio mixer */ + +#define I2C_DRIVERID_EXP0 0xF0 /* experimental use id's */ +#define I2C_DRIVERID_EXP1 0xF1 +#define I2C_DRIVERID_EXP2 0xF2 +#define I2C_DRIVERID_EXP3 0xF3 + +#define I2C_DRIVERID_I2CDEV 900 +#define I2C_DRIVERID_I2CPROC 901 + +/* + * ---- Adapter types ---------------------------------------------------- + * + * First, we distinguish between several algorithms to access the hardware + * interface types, as a PCF 8584 needs other care than a bit adapter. + */ + +#define I2C_ALGO_NONE 0x000000 +#define I2C_ALGO_BIT 0x010000 /* bit style adapters */ +#define I2C_ALGO_PCF 0x020000 /* PCF 8584 style adapters */ +#define I2C_ALGO_ATI 0x030000 /* ATI video card */ +#define I2C_ALGO_SMBUS 0x040000 +#define I2C_ALGO_ISA 0x050000 /* lm_sensors ISA pseudo-adapter */ +#define I2C_ALGO_SAA7146 0x060000 /* SAA 7146 video decoder bus */ +#define I2C_ALGO_SAA7146A 0x060001 /* SAA 7146A - enhanced version */ + + +#define I2C_ALGO_EXP 0x800000 /* experimental */ + +#define I2C_ALGO_MASK 0xff0000 /* Mask for algorithms */ +#define I2C_ALGO_SHIFT 0x10 /* right shift to get index values */ + +#define I2C_HW_ADAPS 0x10000 /* # adapter types */ +#define I2C_HW_MASK 0xffff + + +/* hw specific modules that are defined per algorithm layer + */ + +/* --- Bit algorithm adapters */ +#define I2C_HW_B_LP 0x00 /* Parallel port Philips style adapter */ +#define I2C_HW_B_LPC 0x01 /* Parallel port, over control reg. */ +#define I2C_HW_B_SER 0x02 /* Serial line interface */ +#define I2C_HW_B_ELV 0x03 /* ELV Card */ +#define I2C_HW_B_VELLE 0x04 /* Vellemann K8000 */ +#define I2C_HW_B_BT848 0x05 /* BT848 video boards */ +#define I2C_HW_B_WNV 0x06 /* Winnov Videums */ +#define I2C_HW_B_VIA 0x07 /* Via vt82c586b */ +#define I2C_HW_B_HYDRA 0x08 /* Apple Hydra Mac I/O */ + +/* --- PCF 8584 based algorithms */ +#define I2C_HW_P_LP 0x00 /* Parallel port interface */ +#define I2C_HW_P_ISA 0x01 /* generic ISA Bus inteface card */ +#define I2C_HW_P_ELEK 0x02 /* Elektor ISA Bus inteface card */ + +/* --- SMBus only adapters */ +#define I2C_HW_SMBUS_PIIX4 0x00 +#define I2C_HW_SMBUS_ALI15X3 0x01 +#define I2C_HW_SMBUS_VIA2 0x02 +#define I2C_HW_SMBUS_VOODOO3 0x03 +#define I2C_HW_SMBUS_I801 0x04 +#define I2C_HW_SMBUS_AMD756 0x05 +#define I2C_HW_SMBUS_SIS5595 0x06 + +/* --- ISA pseudo-adapter */ +#define I2C_HW_ISA 0x00 + +#endif /* I2C_ID_H */ diff -u --recursive --new-file v2.3.33/linux/include/linux/i2c-old.h linux/include/linux/i2c-old.h --- v2.3.33/linux/include/linux/i2c-old.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/i2c-old.h Thu Dec 16 13:59:38 1999 @@ -0,0 +1,189 @@ +#ifndef I2C_H +#define I2C_H + +/* + * linux i2c interface. Works a little bit like the scsi subsystem. + * There are: + * + * i2c the basic control module (like scsi_mod) + * bus driver a driver with a i2c bus (hostadapter driver) + * chip driver a driver for a chip connected + * to a i2c bus (cdrom/hd driver) + * + * A device will be attached to one bus and one chip driver. Every chip + * driver gets a unique ID. + * + * A chip driver can provide a ioctl-like callback for the + * communication with other parts of the kernel (not every i2c chip is + * useful without other devices, a TV card tuner for example). + * + * "i2c internal" parts of the structs: only the i2c module is allowed to + * write to them, for others they are read-only. + * + */ + +#include + +#define I2C_BUS_MAX 4 /* max # of bus drivers */ +#define I2C_DRIVER_MAX 8 /* max # of chip drivers */ +#define I2C_DEVICE_MAX 8 /* max # if devices per bus/driver */ + +struct i2c_bus; +struct i2c_driver; +struct i2c_device; + +#define I2C_DRIVERID_MSP3400 1 +#define I2C_DRIVERID_TUNER 2 +#define I2C_DRIVERID_VIDEOTEXT 3 +#define I2C_DRIVERID_VIDEODECODER 4 +#define I2C_DRIVERID_VIDEOENCODER 5 + +#define I2C_BUSID_BT848 1 /* I2C bus on a BT848 */ +#define I2C_BUSID_PARPORT 2 /* Bit banging on a parallel port */ +#define I2C_BUSID_BUZ 3 +#define I2C_BUSID_ZORAN 4 + +/* + * struct for a driver for a i2c chip (tuner, soundprocessor, + * videotext, ... ). + * + * a driver will register within the i2c module. The i2c module will + * callback the driver (i2c_attach) for every device it finds on a i2c + * bus at the specified address. If the driver decides to "accept" + * the, device, it must return a struct i2c_device, and NULL + * otherwise. + * + * i2c_detach = i2c_attach ** -1 + * + * i2c_command will be used to pass commands to the driver in a + * ioctl-line manner. + * + */ + +struct i2c_driver +{ + char name[32]; /* some useful label */ + int id; /* device type ID */ + unsigned char addr_l, addr_h; /* address range of the chip */ + + int (*attach)(struct i2c_device *device); + int (*detach)(struct i2c_device *device); + int (*command)(struct i2c_device *device,unsigned int cmd, void *arg); + + /* i2c internal */ + struct i2c_device *devices[I2C_DEVICE_MAX]; + int devcount; +}; + + +/* + * this holds the informations about a i2c bus available in the system. + * + * a chip with a i2c bus interface (like bt848) registers the bus within + * the i2c module. This struct provides functions to access the i2c bus. + * + * One must hold the spinlock to access the i2c bus (XXX: is the irqsave + * required? Maybe better use a semaphore?). + * [-AC-] having a spinlock_irqsave is only needed if we have drivers wishing + * to bang their i2c bus from an interrupt. + * + * attach/detach_inform is a callback to inform the bus driver about + * attached chip drivers. + * + */ + +/* needed: unsigned long flags */ + +#if LINUX_VERSION_CODE >= 0x020100 +# if 0 +# define LOCK_FLAGS unsigned long flags; +# define LOCK_I2C_BUS(bus) spin_lock_irqsave(&(bus->bus_lock),flags); +# define UNLOCK_I2C_BUS(bus) spin_unlock_irqrestore(&(bus->bus_lock),flags); +# else +# define LOCK_FLAGS +# define LOCK_I2C_BUS(bus) spin_lock(&(bus->bus_lock)); +# define UNLOCK_I2C_BUS(bus) spin_unlock(&(bus->bus_lock)); +# endif +#else +# define LOCK_FLAGS unsigned long flags; +# define LOCK_I2C_BUS(bus) { save_flags(flags); cli(); } +# define UNLOCK_I2C_BUS(bus) { restore_flags(flags); } +#endif + +struct i2c_bus +{ + char name[32]; /* some useful label */ + int id; + void *data; /* free for use by the bus driver */ + +#if LINUX_VERSION_CODE >= 0x020100 + spinlock_t bus_lock; +#endif + + /* attach/detach inform callbacks */ + void (*attach_inform)(struct i2c_bus *bus, int id); + void (*detach_inform)(struct i2c_bus *bus, int id); + + /* Software I2C */ + void (*i2c_setlines)(struct i2c_bus *bus, int ctrl, int data); + int (*i2c_getdataline)(struct i2c_bus *bus); + + /* Hardware I2C */ + int (*i2c_read)(struct i2c_bus *bus, unsigned char addr); + int (*i2c_write)(struct i2c_bus *bus, unsigned char addr, + unsigned char b1, unsigned char b2, int both); + + /* internal data for i2c module */ + struct i2c_device *devices[I2C_DEVICE_MAX]; + int devcount; +}; + + +/* + * This holds per-device data for a i2c device + */ + +struct i2c_device +{ + char name[32]; /* some useful label */ + void *data; /* free for use by the chip driver */ + unsigned char addr; /* chip addr */ + + /* i2c internal */ + struct i2c_bus *bus; + struct i2c_driver *driver; +}; + + +/* ------------------------------------------------------------------- */ +/* i2c module functions */ + +/* register/unregister a i2c bus */ +int i2c_register_bus(struct i2c_bus *bus); +int i2c_unregister_bus(struct i2c_bus *bus); + +/* register/unregister a chip driver */ +int i2c_register_driver(struct i2c_driver *driver); +int i2c_unregister_driver(struct i2c_driver *driver); + +/* send a command to a chip using the ioctl-like callback interface */ +int i2c_control_device(struct i2c_bus *bus, int id, + unsigned int cmd, void *arg); + +/* i2c bus access functions */ +void i2c_start(struct i2c_bus *bus); +void i2c_stop(struct i2c_bus *bus); +void i2c_one(struct i2c_bus *bus); +void i2c_zero(struct i2c_bus *bus); +int i2c_ack(struct i2c_bus *bus); + +int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack); +unsigned char i2c_readbyte(struct i2c_bus *bus,int last); + +/* i2c (maybe) hardware functions */ +int i2c_read(struct i2c_bus *bus, unsigned char addr); +int i2c_write(struct i2c_bus *bus, unsigned char addr, + unsigned char b1, unsigned char b2, int both); + +int i2c_init(void); +#endif /* I2C_H */ diff -u --recursive --new-file v2.3.33/linux/include/linux/i2c.h linux/include/linux/i2c.h --- v2.3.33/linux/include/linux/i2c.h Tue Dec 14 01:27:24 1999 +++ linux/include/linux/i2c.h Thu Dec 16 13:59:38 1999 @@ -1,190 +1,514 @@ +/* ------------------------------------------------------------------------- */ +/* */ +/* i2c.h - definitions for the i2c-bus interface */ +/* */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-1999 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- */ +/* $Revision: 1.30 $ $Date: 1999/11/16 08:12:38 $*/ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and + Frodo Looijaard */ + #ifndef I2C_H #define I2C_H -/* - * linux i2c interface. Works a little bit like the scsi subsystem. - * There are: - * - * i2c the basic control module (like scsi_mod) - * bus driver a driver with a i2c bus (hostadapter driver) - * chip driver a driver for a chip connected - * to a i2c bus (cdrom/hd driver) - * - * A device will be attached to one bus and one chip driver. Every chip - * driver gets a unique ID. - * - * A chip driver can provide a ioctl-like callback for the - * communication with other parts of the kernel (not every i2c chip is - * useful without other devices, a TV card tuner for example). - * - * "i2c internal" parts of the structs: only the i2c module is allowed to - * write to them, for others they are read-only. - * - */ +#include /* id values of adapters et. al. */ -#include +#ifdef __KERNEL__ -#define I2C_BUS_MAX 4 /* max # of bus drivers */ -#define I2C_DRIVER_MAX 8 /* max # of chip drivers */ -#define I2C_DEVICE_MAX 8 /* max # if devices per bus/driver */ +/* --- Includes and compatibility declarations ------------------------ */ -struct i2c_bus; -struct i2c_driver; -struct i2c_device; +#include +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c)) +#endif -#define I2C_DRIVERID_MSP3400 1 -#define I2C_DRIVERID_TUNER 2 -#define I2C_DRIVERID_VIDEOTEXT 3 -#define I2C_DRIVERID_VIDEODECODER 4 -#define I2C_DRIVERID_VIDEOENCODER 5 - -#define I2C_BUSID_BT848 1 /* I2C bus on a BT848 */ -#define I2C_BUSID_PARPORT 2 /* Bit banging on a parallel port */ -#define I2C_BUSID_BUZ 3 -#define I2C_BUSID_ZORAN 4 -#define I2C_BUSID_CYBER2000 5 /* I2C bus on a Cyber2000 */ - -/* - * struct for a driver for a i2c chip (tuner, soundprocessor, - * videotext, ... ). - * - * a driver will register within the i2c module. The i2c module will - * callback the driver (i2c_attach) for every device it finds on a i2c - * bus at the specified address. If the driver decides to "accept" - * the, device, it must return a struct i2c_device, and NULL - * otherwise. - * - * i2c_detach = i2c_attach ** -1 - * - * i2c_command will be used to pass commands to the driver in a - * ioctl-line manner. - * - */ - -struct i2c_driver -{ - char name[32]; /* some useful label */ - int id; /* device type ID */ - unsigned char addr_l, addr_h; /* address range of the chip */ - - int (*attach)(struct i2c_device *device); - int (*detach)(struct i2c_device *device); - int (*command)(struct i2c_device *device,unsigned int cmd, void *arg); - - /* i2c internal */ - struct i2c_device *devices[I2C_DEVICE_MAX]; - int devcount; -}; - - -/* - * this holds the informations about a i2c bus available in the system. - * - * a chip with a i2c bus interface (like bt848) registers the bus within - * the i2c module. This struct provides functions to access the i2c bus. - * - * One must hold the spinlock to access the i2c bus (XXX: is the irqsave - * required? Maybe better use a semaphore?). - * [-AC-] having a spinlock_irqsave is only needed if we have drivers wishing - * to bang their i2c bus from an interrupt. - * - * attach/detach_inform is a callback to inform the bus driver about - * attached chip drivers. - * - */ - -/* needed: unsigned long flags */ - -#if LINUX_VERSION_CODE >= 0x020100 -# if 0 -# define LOCK_FLAGS unsigned long flags; -# define LOCK_I2C_BUS(bus) spin_lock_irqsave(&(bus->bus_lock),flags); -# define UNLOCK_I2C_BUS(bus) spin_unlock_irqrestore(&(bus->bus_lock),flags); -# else -# define LOCK_FLAGS -# define LOCK_I2C_BUS(bus) spin_lock(&(bus->bus_lock)); -# define UNLOCK_I2C_BUS(bus) spin_unlock(&(bus->bus_lock)); -# endif +#include /* for 2.2.xx */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,25) +#include #else -# define LOCK_FLAGS unsigned long flags; -# define LOCK_I2C_BUS(bus) { save_flags(flags); cli(); } -# define UNLOCK_I2C_BUS(bus) { restore_flags(flags); } +#include #endif +#include +#include + +/* --- General options ------------------------------------------------ */ + +#define I2C_ALGO_MAX 4 /* control memory consumption */ +#define I2C_ADAP_MAX 16 +#define I2C_DRIVER_MAX 16 +#define I2C_CLIENT_MAX 32 +#define I2C_DUMMY_MAX 4 + +struct i2c_msg; +struct i2c_algorithm; +struct i2c_adapter; +struct i2c_client; +struct i2c_driver; +struct i2c_client_address_data; +union i2c_smbus_data; + + +/* + * The master routines are the ones normally used to transmit data to devices + * on a bus (or read from them). Apart from two basic transfer functions to + * transmit one message at a time, a more complex version can be used to + * transmit an arbitrary number of messages without interruption. + */ +extern int i2c_master_send(struct i2c_client *,const char* ,int); +extern int i2c_master_recv(struct i2c_client *,char* ,int); + +/* Transfer num messages. + */ +extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],int num); + +/* + * Some adapter types (i.e. PCF 8584 based ones) may support slave behaviuor. + * This is not tested/implemented yet and will change in the future. + */ +extern int i2c_slave_send(struct i2c_client *,char*,int); +extern int i2c_slave_recv(struct i2c_client *,char*,int); -struct i2c_bus -{ - char name[32]; /* some useful label */ - int id; - void *data; /* free for use by the bus driver */ -#if LINUX_VERSION_CODE >= 0x020100 - spinlock_t bus_lock; +/* + * I2C Message - could be used in the current interface to + */ +struct i2c_msg { + u16 addr; /* slave address */ + unsigned short flags; +#define I2C_M_TEN 0x10 /* we have a ten bit chip address */ +#define I2C_M_RD 0x01 +#if 0 +#define I2C_M_PROBE 0x20 #endif + short len; /* msg length */ + char *buf; /* pointer to msg data */ +}; + - /* attach/detach inform callbacks */ - void (*attach_inform)(struct i2c_bus *bus, int id); - void (*detach_inform)(struct i2c_bus *bus, int id); +/* This is the very generalized SMBus access routine. You probably do not + want to use this, though; one of the functions below may be much easier, + and probably just as fast. + Note that we use i2c_adapter here, because you do not need a specific + smbus adapter to call this function. */ +extern s32 i2c_smbus_xfer (struct i2c_adapter * adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data); + +/* Now follow the 'nice' access routines. These also document the calling + conventions of smbus_access. */ + +extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value); +extern s32 i2c_smbus_read_byte(struct i2c_client * client); +extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value); +extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command); +extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, + u8 command, u8 value); +extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command); +extern s32 i2c_smbus_write_word_data(struct i2c_client * client, + u8 command, u16 value); +extern s32 i2c_smbus_process_call(struct i2c_client * client, + u8 command, u16 value); +/* Returns the number of read bytes */ +extern s32 i2c_smbus_read_block_data(struct i2c_client * client, + u8 command, u8 *values); +extern s32 i2c_smbus_write_block_data(struct i2c_client * client, + u8 command, u8 length, + u8 *values); - /* Software I2C */ - void (*i2c_setlines)(struct i2c_bus *bus, int ctrl, int data); - int (*i2c_getdataline)(struct i2c_bus *bus); - /* Hardware I2C */ - int (*i2c_read)(struct i2c_bus *bus, unsigned char addr); - int (*i2c_write)(struct i2c_bus *bus, unsigned char addr, - unsigned char b1, unsigned char b2, int both); +/* + * A driver is capable of handling one or more physical devices present on + * I2C adapters. This information is used to inform the driver of adapter + * events. + */ + +struct i2c_driver { + char name[32]; + int id; + unsigned int flags; /* div., see below */ + + /* Notifies the driver that a new bus has appeared. This routine + * can be used by the driver to test if the bus meets its conditions + * & seek for the presence of the chip(s) it supports. If found, it + * registers the client(s) that are on the bus to the i2c admin. via + * i2c_attach_client. + */ + int (*attach_adapter)(struct i2c_adapter *); + + /* tells the driver that a client is about to be deleted & gives it + * the chance to remove its private data. Also, if the client struct + * has been dynamically allocated by the driver in the function above, + * it must be freed here. + */ + int (*detach_client)(struct i2c_client *); + + /* a ioctl like command that can be used to perform specific functions + * with the device. + */ + int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); + + /* These two are mainly used for bookkeeping & dynamic unloading of + * kernel modules. inc_use tells the driver that a client is being + * used by another module & that it should increase its ref. counter. + * dec_use is the inverse operation. + * NB: Make sure you have no circular dependencies, or else you get a + * deadlock when trying to unload the modules. + * You should use the i2c_{inc,dec}_use_client functions instead of + * calling this function directly. + */ + void (*inc_use)(struct i2c_client *client); + void (*dec_use)(struct i2c_client *client); +}; - /* internal data for i2c module */ - struct i2c_device *devices[I2C_DEVICE_MAX]; - int devcount; +/* + * i2c_client identifies a single device (i.e. chip) that is connected to an + * i2c bus. The behaviour is defined by the routines of the driver. This + * function is mainly used for lookup & other admin. functions. + */ +struct i2c_client { + char name[32]; + int id; + unsigned int flags; /* div., see below */ + unsigned int addr; /* chip address - NOTE: 7bit */ + /* addresses are stored in the */ + /* _LOWER_ 7 bits of this char */ + /* addr: unsigned int to make lm_sensors i2c-isa adapter work + more cleanly. It does not take any more memory space, due to + alignment considerations */ + struct i2c_adapter *adapter; /* the adapter we sit on */ + struct i2c_driver *driver; /* and our access routines */ + void *data; /* for the clients */ }; /* - * This holds per-device data for a i2c device + * The following structs are for those who like to implement new bus drivers: + * i2c_algorithm is the interface to a class of hardware solutions which can + * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 + * to name two of the most common. */ +struct i2c_algorithm { + char name[32]; /* textual description */ + unsigned int id; + + /* If a adapter algorithm can't to I2C-level access, set master_xfer + to NULL. If an adapter algorithm can do SMBus access, set + smbus_xfer. If set to NULL, the SMBus protocol is simulated + using common I2C messages */ + int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg msgs[], + int num); + int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data); + + /* --- these optional/future use for some adapter types.*/ + int (*slave_send)(struct i2c_adapter *,char*,int); + int (*slave_recv)(struct i2c_adapter *,char*,int); + + /* --- ioctl like call to set div. parameters. */ + int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long); + + /* To determine what the adapter supports */ + u32 (*functionality) (struct i2c_adapter *); +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29) +struct proc_dir_entry; +#endif -struct i2c_device -{ - char name[32]; /* some useful label */ - void *data; /* free for use by the chip driver */ - unsigned char addr; /* chip addr */ +/* + * i2c_adapter is the structure used to identify a physical i2c bus along + * with the access algorithms necessary to access it. + */ +struct i2c_adapter { + char name[32]; /* some useful name to identify the adapter */ + unsigned int id;/* == is algo->id | hwdep.struct->id, */ + /* for registered values see below */ + struct i2c_algorithm *algo;/* the algorithm to access the bus */ + void *algo_data; + + /* --- These may be NULL, but should increase the module use count */ + void (*inc_use)(struct i2c_adapter *); + void (*dec_use)(struct i2c_adapter *); + + /* --- administration stuff. */ + int (*client_register)(struct i2c_client *); + int (*client_unregister)(struct i2c_client *); + + void *data; /* private data for the adapter */ + /* some data fields that are used by all types */ + /* these data fields are readonly to the public */ + /* and can be set via the i2c_ioctl call */ + + /* data fields that are valid for all devices */ + struct semaphore lock; + unsigned int flags;/* flags specifying div. data */ + + struct i2c_client *clients[I2C_CLIENT_MAX]; + int client_count; + + int timeout; + int retries; + +#ifdef CONFIG_PROC_FS + /* No need to set this when you initialize the adapter */ + int inode; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29) + struct proc_dir_entry *proc_entry; +#endif +#endif /* def CONFIG_PROC_FS */ +}; - /* i2c internal */ - struct i2c_bus *bus; - struct i2c_driver *driver; +/*flags for the driver struct: */ +#define I2C_DF_NOTIFY 0x01 /* notify on bus (de/a)ttaches */ +#define I2C_DF_DUMMY 0x02 /* do not connect any clients */ + +/* i2c_client_address_data is the struct for holding default client + * addresses for a driver and for the parameters supplied on the + * command line + */ +struct i2c_client_address_data { + unsigned short *normal_i2c; + unsigned short *normal_i2c_range; + unsigned short *probe; + unsigned short *probe_range; + unsigned short *ignore; + unsigned short *ignore_range; + unsigned short *force; }; +/* Internal numbers to terminate lists */ +#define I2C_CLIENT_END 0xfffe + +/* The numbers to use to set I2C bus address */ +#define ANY_I2C_BUS 0xffff + +/* The length of the option lists */ +#define I2C_CLIENT_MAX_OPTS 48 + + +/* ----- functions exported by i2c.o */ + +/* administration... + */ +extern int i2c_add_adapter(struct i2c_adapter *); +extern int i2c_del_adapter(struct i2c_adapter *); + +extern int i2c_add_driver(struct i2c_driver *); +extern int i2c_del_driver(struct i2c_driver *); + +extern int i2c_attach_client(struct i2c_client *); +extern int i2c_detach_client(struct i2c_client *); -/* ------------------------------------------------------------------- */ -/* i2c module functions */ +/* Only call these if you grab a resource that makes unloading the + client and the adapter it is on completely impossible. Like when a + /proc directory is entered. */ +extern void i2c_inc_use_client(struct i2c_client *); +extern void i2c_dec_use_client(struct i2c_client *); + + +/* Detect function. It itterates over all possible addresses itself. + * It will only call found_proc if some client is connected at the + * specific address (unless a 'force' matched); + */ +typedef int i2c_client_found_addr_proc (struct i2c_adapter *adapter, + int addr, unsigned short flags,int kind); + +extern int i2c_probe(struct i2c_adapter *adapter, + struct i2c_client_address_data *address_data, + i2c_client_found_addr_proc *found_proc); + +/* An ioctl like call to set div. parameters of the adapter. + */ +extern int i2c_control(struct i2c_client *,unsigned int, unsigned long); + +/* This call returns a unique low identifier for each registered adapter, + * or -1 if the adapter was not regisitered. + */ +extern int i2c_adapter_id(struct i2c_adapter *adap); -/* register/unregister a i2c bus */ -int i2c_register_bus(struct i2c_bus *bus); -int i2c_unregister_bus(struct i2c_bus *bus); -/* register/unregister a chip driver */ -int i2c_register_driver(struct i2c_driver *driver); -int i2c_unregister_driver(struct i2c_driver *driver); -/* send a command to a chip using the ioctl-like callback interface */ -int i2c_control_device(struct i2c_bus *bus, int id, - unsigned int cmd, void *arg); +/* Return the functionality mask */ +extern u32 i2c_get_functionality (struct i2c_adapter *adap); -/* i2c bus access functions */ -void i2c_start(struct i2c_bus *bus); -void i2c_stop(struct i2c_bus *bus); -void i2c_one(struct i2c_bus *bus); -void i2c_zero(struct i2c_bus *bus); -int i2c_ack(struct i2c_bus *bus); +/* Return 1 if adapter supports everything we need, 0 if not. */ +extern int i2c_check_functionality (struct i2c_adapter *adap, u32 func); + +#endif /* __KERNEL__ */ + +/* To determine what functionality is present */ + +#define I2C_FUNC_I2C 0x00000001 +#define I2C_FUNC_10BIT_ADDR 0x00000002 +#define I2C_FUNC_SMBUS_QUICK 0x00010000 +#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000 +#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000 +#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000 +#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000 +#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000 +#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000 +#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000 +#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 +#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 +#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* New I2C-like block */ +#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* transfers */ + +#define I2C_FUNC_SMBUS_BYTE I2C_FUNC_SMBUS_READ_BYTE | \ + I2C_FUNC_SMBUS_WRITE_BYTE +#define I2C_FUNC_SMBUS_BYTE_DATA I2C_FUNC_SMBUS_READ_BYTE_DATA | \ + I2C_FUNC_SMBUS_WRITE_BYTE_DATA +#define I2C_FUNC_SMBUS_WORD_DATA I2C_FUNC_SMBUS_READ_WORD_DATA | \ + I2C_FUNC_SMBUS_WRITE_WORD_DATA +#define I2C_FUNC_SMBUS_BLOCK_DATA I2C_FUNC_SMBUS_READ_BLOCK_DATA | \ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA +#define I2C_FUNC_SMBUS_I2C_BLOCK I2C_FUNC_SMBUS_READ_I2C_BLOCK | \ + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK + +#define I2C_FUNC_SMBUS_EMUL I2C_FUNC_SMBUS_QUICK | \ + I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_BYTE_DATA | \ + I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_PROC_CALL | \ + I2C_FUNC_SMBUS_READ_BLOCK_DATA + +/* + * Data for SMBus Messages + */ +union i2c_smbus_data { + __u8 byte; + __u16 word; + __u8 block[33]; /* block[0] is used for length */ +}; + +/* smbus_access read or write markers */ +#define I2C_SMBUS_READ 1 +#define I2C_SMBUS_WRITE 0 + +/* SMBus transaction types (size parameter in the above functions) + Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */ +#define I2C_SMBUS_QUICK 0 +#define I2C_SMBUS_BYTE 1 +#define I2C_SMBUS_BYTE_DATA 2 +#define I2C_SMBUS_WORD_DATA 3 +#define I2C_SMBUS_PROC_CALL 4 +#define I2C_SMBUS_BLOCK_DATA 5 + + +/* ----- commands for the ioctl like i2c_command call: + * note that additional calls are defined in the algorithm and hw + * dependent layers - these can be listed here, or see the + * corresponding header files. + */ + /* -> bit-adapter specific ioctls */ +#define I2C_RETRIES 0x0701 /* number times a device adress should */ + /* be polled when not acknowledging */ +#define I2C_TIMEOUT 0x0702 /* set timeout - call with int */ + + +/* this is for i2c-dev.c */ +#define I2C_SLAVE 0x0703 /* Change slave address */ + /* Attn.: Slave address is 7 or 10 bits */ +#define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */ + +#define I2C_FUNCS 0x0705 /* Get the adapter functionality */ +#if 0 +#define I2C_ACK_TEST 0x0710 /* See if a slave is at a specific adress */ +#endif -int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack); -unsigned char i2c_readbyte(struct i2c_bus *bus,int last); +#define I2C_SMBUS 0x0720 /* SMBus-level access */ -/* i2c (maybe) hardware functions */ -int i2c_read(struct i2c_bus *bus, unsigned char addr); -int i2c_write(struct i2c_bus *bus, unsigned char addr, - unsigned char b1, unsigned char b2, int both); +/* ... algo-bit.c recognizes */ +#define I2C_UDELAY 0x0705 /* set delay in microsecs between each */ + /* written byte (except address) */ +#define I2C_MDELAY 0x0706 /* millisec delay between written bytes */ + + +/* ----- I2C-DEV: char device interface stuff ------------------------- */ + +#define I2C_MAJOR 89 /* Device major number */ + +#ifdef __KERNEL__ + +# ifndef NULL +# define NULL ( (void *) 0 ) +# endif + +# ifndef ENODEV +# include +# endif + +/* These defines are used for probing i2c client addresses */ +/* Default fill of many variables */ +#define I2C_CLIENT_DEFAULTS {I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END} + +/* This is ugly. We need to evaluate I2C_CLIENT_MAX_OPTS before it is + stringified */ +#define I2C_CLIENT_MODPARM_AUX1(x) "1-" #x "h" +#define I2C_CLIENT_MODPARM_AUX(x) I2C_CLIENT_MODPARM_AUX1(x) +#define I2C_CLIENT_MODPARM I2C_CLIENT_MODPARM_AUX(I2C_CLIENT_MAX_OPTS) + +/* I2C_CLIENT_MODULE_PARM creates a module parameter, and puts it in the + module header */ + +#define I2C_CLIENT_MODULE_PARM(var,desc) \ + static unsigned short var[I2C_CLIENT_MAX_OPTS] = I2C_CLIENT_DEFAULTS; \ + MODULE_PARM(var,I2C_CLIENT_MODPARM); \ + MODULE_PARM_DESC(var,desc) + +/* This is the one you want to use in your own modules */ +#define I2C_CLIENT_INSMOD \ + I2C_CLIENT_MODULE_PARM(probe, \ + "List of adapter,address pairs to scan additionally"); \ + I2C_CLIENT_MODULE_PARM(probe_range, \ + "List of adapter,start-addr,end-addr triples to scan " \ + "additionally"); \ + I2C_CLIENT_MODULE_PARM(ignore, \ + "List of adapter,address pairs not to scan"); \ + I2C_CLIENT_MODULE_PARM(ignore_range, \ + "List of adapter,start-addr,end-addr triples not to " \ + "scan"); \ + I2C_CLIENT_MODULE_PARM(force, \ + "List of adapter,address pairs to boldly assume " \ + "to be present"); \ + static struct i2c_client_address_data addr_data = \ + {normal_i2c, normal_i2c_range, \ + probe, probe_range, \ + ignore, ignore_range, \ + force} -int i2c_init(void); +#endif /* def __KERNEL__ */ #endif /* I2C_H */ diff -u --recursive --new-file v2.3.33/linux/include/linux/ide.h linux/include/linux/ide.h --- v2.3.33/linux/include/linux/ide.h Tue Dec 14 01:27:24 1999 +++ linux/include/linux/ide.h Mon Dec 20 16:26:30 1999 @@ -262,6 +262,7 @@ unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */ unsigned remap_0_to_1 : 2; /* 0=remap if ezdrive, 1=remap, 2=noremap */ unsigned ata_flash : 1; /* 1=present, 0=default */ + byte scsi; /* 0=default, 1=skip current ide-subdriver for ide-scsi emulation */ byte media; /* disk, cdrom, tape, floppy, ... */ select_t select; /* basic drive/head select reg value */ byte ctl; /* "normal" value for IDE_CONTROL_REG */ @@ -399,7 +400,7 @@ #endif } ide_hwif_t; - /* +/* * Status returned from various ide_ functions */ typedef enum { @@ -428,7 +429,7 @@ struct timer_list timer; /* failsafe timer */ struct request wrq; /* local copy of current write rq */ unsigned long poll_timeout; /* timeout value during long polls */ - ide_expiry_t *expiry; /* queried upon timeouts */ + ide_expiry_t *expiry; /* queried upon timeouts */ } ide_hwgroup_t; /* diff -u --recursive --new-file v2.3.33/linux/include/linux/netfilter.h linux/include/linux/netfilter.h --- v2.3.33/linux/include/linux/netfilter.h Sat Oct 9 11:47:50 1999 +++ linux/include/linux/netfilter.h Mon Dec 20 16:02:47 1999 @@ -36,7 +36,8 @@ typedef unsigned int nf_hookfn(unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, - const struct net_device *out); + const struct net_device *out, + int (*okfn)(struct sk_buff *)); typedef unsigned int nf_cacheflushfn(const void *packet, const struct net_device *in, diff -u --recursive --new-file v2.3.33/linux/include/linux/openpic.h linux/include/linux/openpic.h --- v2.3.33/linux/include/linux/openpic.h Fri Mar 19 10:51:42 1999 +++ linux/include/linux/openpic.h Mon Dec 20 14:27:34 1999 @@ -50,10 +50,10 @@ * Vector numbers */ -#define OPENPIC_VEC_SOURCE 0x10 /* and up */ -#define OPENPIC_VEC_TIMER 0x40 /* and up */ -#define OPENPIC_VEC_IPI 0x50 /* and up */ -#define OPENPIC_VEC_SPURIOUS 99 +#define OPENPIC_VEC_SOURCE 16 /* and up */ +#define OPENPIC_VEC_TIMER 64 /* and up */ +#define OPENPIC_VEC_IPI 72 /* and up */ +#define OPENPIC_VEC_SPURIOUS 127 /* diff -u --recursive --new-file v2.3.33/linux/include/linux/pci_ids.h linux/include/linux/pci_ids.h --- v2.3.33/linux/include/linux/pci_ids.h Wed Dec 15 10:43:17 1999 +++ linux/include/linux/pci_ids.h Tue Dec 14 23:03:50 1999 @@ -1098,7 +1098,24 @@ #define PCI_DEVICE_ID_INTEL_82371AB 0x7111 #define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 #define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 - +#define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 +#define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 +#define PCI_DEVICE_ID_INTEL_82801AA_2 0x2412 +#define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 +#define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415 +#define PCI_DEVICE_ID_INTEL_82801AA_6 0x2416 +#define PCI_DEVICE_ID_INTEL_82801AA_8 0x2418 +#define PCI_DEVICE_ID_INTEL_82801AB_0 0x2420 +#define PCI_DEVICE_ID_INTEL_82801AB_1 0x2421 +#define PCI_DEVICE_ID_INTEL_82801AB_2 0x2422 +#define PCI_DEVICE_ID_INTEL_82801AB_3 0x2423 +#define PCI_DEVICE_ID_INTEL_82801AB_5 0x2425 +#define PCI_DEVICE_ID_INTEL_82801AB_6 0x2426 +#define PCI_DEVICE_ID_INTEL_82801AB_8 0x2428 +#define PCI_DEVICE_ID_INTEL_82810_MC1 0x7120 +#define PCI_DEVICE_ID_INTEL_82810_IG1 0x7121 +#define PCI_DEVICE_ID_INTEL_82810_MC3 0x7122 +#define PCI_DEVICE_ID_INTEL_82810_IG3 0x7123 #define PCI_DEVICE_ID_INTEL_82443LX_0 0x7180 #define PCI_DEVICE_ID_INTEL_82443LX_1 0x7181 #define PCI_DEVICE_ID_INTEL_82443BX_0 0x7190 diff -u --recursive --new-file v2.3.33/linux/include/pcmcia/bulkmem.h linux/include/pcmcia/bulkmem.h --- v2.3.33/linux/include/pcmcia/bulkmem.h Tue Dec 14 01:27:24 1999 +++ linux/include/pcmcia/bulkmem.h Sat Dec 18 15:43:16 1999 @@ -195,10 +195,10 @@ int pcmcia_get_first_region(client_handle_t handle, region_info_t *rgn); int pcmcia_get_next_region(client_handle_t handle, region_info_t *rgn); int pcmcia_register_mtd(client_handle_t handle, mtd_reg_t *reg); -int pcmcia_register_erase_queue(client_handle_t *handle, eraseq_hdr_t *header); +int pcmcia_register_erase_queue(client_handle_t *handle, eraseq_hdr_t *header, eraseq_handle_t *e); int pcmcia_deregister_erase_queue(eraseq_handle_t eraseq); int pcmcia_check_erase_queue(eraseq_handle_t eraseq); -int pcmcia_open_memory(client_handle_t *handle, open_mem_t *open); +int pcmcia_open_memory(client_handle_t *handle, open_mem_t *open, memory_handle_t *m); int pcmcia_close_memory(memory_handle_t handle); int pcmcia_read_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf); int pcmcia_write_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf); diff -u --recursive --new-file v2.3.33/linux/include/pcmcia/cs.h linux/include/pcmcia/cs.h --- v2.3.33/linux/include/pcmcia/cs.h Tue Dec 14 01:27:24 1999 +++ linux/include/pcmcia/cs.h Sat Dec 18 15:43:16 1999 @@ -464,7 +464,7 @@ int pcmcia_request_configuration(client_handle_t handle, config_req_t *req); int pcmcia_request_io(client_handle_t handle, io_req_t *req); int pcmcia_request_irq(client_handle_t handle, irq_req_t *req); -int pcmcia_request_window(client_handle_t *handle, win_req_t *req); +int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle_t *wh); int pcmcia_reset_card(client_handle_t handle, client_req_t *req); int pcmcia_suspend_card(client_handle_t handle, client_req_t *req); int pcmcia_resume_card(client_handle_t handle, client_req_t *req); diff -u --recursive --new-file v2.3.33/linux/ipc/shm.c linux/ipc/shm.c --- v2.3.33/linux/ipc/shm.c Tue Dec 7 09:32:51 1999 +++ linux/ipc/shm.c Mon Dec 20 11:16:40 1999 @@ -549,8 +549,8 @@ down(¤t->mm->mmap_sem); err = -EINVAL; shp = shm_lock(shmid); - if(shp == NULL) - goto out_unlock_up; + if (!shp) + goto out_up; err = -EACCES; if (ipcperms(&shp->u.shm_perm, flg)) diff -u --recursive --new-file v2.3.33/linux/kernel/Makefile linux/kernel/Makefile --- v2.3.33/linux/kernel/Makefile Tue Dec 14 01:27:24 1999 +++ linux/kernel/Makefile Mon Dec 20 14:43:40 1999 @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile... .S.s: - $(CPP) -traditional $< -o $*.s + $(CPP) $(CPPFLAGS) -traditional $< -o $*.s O_TARGET := kernel.o O_OBJS = sched.o dma.o fork.o exec_domain.o panic.o printk.o sys.o \ diff -u --recursive --new-file v2.3.33/linux/mm/filemap.c linux/mm/filemap.c --- v2.3.33/linux/mm/filemap.c Tue Dec 7 09:32:52 1999 +++ linux/mm/filemap.c Thu Dec 16 16:20:15 1999 @@ -1620,7 +1620,9 @@ */ static void filemap_unmap(struct vm_area_struct *vma, unsigned long start, size_t len) { + lock_kernel(); filemap_sync(vma, start, len, MS_ASYNC); + unlock_kernel(); } /* diff -u --recursive --new-file v2.3.33/linux/mm/mmap.c linux/mm/mmap.c --- v2.3.33/linux/mm/mmap.c Tue Dec 14 01:27:24 1999 +++ linux/mm/mmap.c Thu Dec 16 16:20:15 1999 @@ -684,10 +684,8 @@ end = end > mpnt->vm_end ? mpnt->vm_end : end; size = end - st; - lock_kernel(); if (mpnt->vm_ops && mpnt->vm_ops->unmap) mpnt->vm_ops->unmap(mpnt, st, size); - unlock_kernel(); remove_shared_vm_struct(mpnt); mm->map_count--; diff -u --recursive --new-file v2.3.33/linux/net/README linux/net/README --- v2.3.33/linux/net/README Tue Sep 7 12:14:07 1999 +++ linux/net/README Thu Dec 16 13:57:05 1999 @@ -22,22 +22,3 @@ x25 g4klx@g4klx.demon.co.uk - If in doubt contact me first. - ---------------------------------------------------------------------------- - -For commercial UK custom Linux networking projects, drivers and development -(but not free support!) I can be contacted via - - CymruNET Ltd, The Innovation Centre, University Of Wales - Swansea SA2 8PP. - Fax: +44 1792 295811 - Tel: +44 1792 295213 - - Email: alan@cymru.net - -Please don't send commercial queries to my .ac.uk email address as I have that -in an academic and _not_ commercial capacity. On the other hand feel -free to send bug reports, queries and enhancements that way. - -Alan diff -u --recursive --new-file v2.3.33/linux/net/core/dev.c linux/net/core/dev.c --- v2.3.33/linux/net/core/dev.c Tue Dec 7 09:32:52 1999 +++ linux/net/core/dev.c Mon Dec 20 15:07:08 1999 @@ -87,9 +87,9 @@ #include #include #include -#ifdef CONFIG_NET_RADIO -#include -#endif /* CONFIG_NET_RADIO */ +#if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO) +#include /* Note : will define WIRELESS_EXT */ +#endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */ #ifdef CONFIG_PLIP extern int plip_init(void); #endif @@ -1307,7 +1307,7 @@ #endif /* CONFIG_PROC_FS */ -#ifdef CONFIG_NET_RADIO +#ifdef WIRELESS_EXT #ifdef CONFIG_PROC_FS /* @@ -1392,7 +1392,7 @@ return len; } #endif /* CONFIG_PROC_FS */ -#endif /* CONFIG_NET_RADIO */ +#endif /* WIRELESS_EXT */ void dev_set_promiscuity(struct net_device *dev, int inc) { @@ -1624,13 +1624,13 @@ return -EOPNOTSUPP; } -#ifdef CONFIG_NET_RADIO +#ifdef WIRELESS_EXT if(cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { if (dev->do_ioctl) return dev->do_ioctl(dev, ifr, cmd); return -EOPNOTSUPP; } -#endif /* CONFIG_NET_RADIO */ +#endif /* WIRELESS_EXT */ } return -EINVAL; @@ -1754,7 +1754,7 @@ return -EFAULT; return ret; } -#ifdef CONFIG_NET_RADIO +#ifdef WIRELESS_EXT if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { dev_load(ifr.ifr_name); if (IW_IS_SET(cmd)) { @@ -1769,7 +1769,7 @@ return -EFAULT; return ret; } -#endif /* CONFIG_NET_RADIO */ +#endif /* WIRELESS_EXT */ return -EINVAL; } } @@ -2083,9 +2083,9 @@ #ifdef CONFIG_PROC_FS proc_net_create("dev", 0, dev_get_info); create_proc_read_entry("net/dev_stat", 0, 0, dev_proc_stats, NULL); -#ifdef CONFIG_NET_RADIO +#ifdef WIRELESS_EXT proc_net_create("wireless", 0, dev_get_wireless_info); -#endif /* CONFIG_NET_RADIO */ +#endif /* WIRELESS_EXT */ #endif /* CONFIG_PROC_FS */ init_bh(NET_BH, net_bh); diff -u --recursive --new-file v2.3.33/linux/net/core/netfilter.c linux/net/core/netfilter.c --- v2.3.33/linux/net/core/netfilter.c Sat Oct 9 11:47:50 1999 +++ linux/net/core/netfilter.c Thu Dec 16 09:05:29 1999 @@ -353,11 +353,12 @@ int hook, const struct net_device *indev, const struct net_device *outdev, - struct list_head **i) + struct list_head **i, + int (*okfn)(struct sk_buff *)) { for (*i = (*i)->next; *i != head; *i = (*i)->next) { struct nf_hook_ops *elem = (struct nf_hook_ops *)*i; - switch (elem->hook(hook, skb, indev, outdev)) { + switch (elem->hook(hook, skb, indev, outdev, okfn)) { case NF_QUEUE: NFDEBUG("nf_iterate: NF_QUEUE for %p.\n", *skb); return NF_QUEUE; @@ -471,7 +472,7 @@ read_lock_bh(&nf_lock); elem = &nf_hooks[pf][hook]; verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev, - outdev, &elem); + outdev, &elem, okfn); if (verdict == NF_QUEUE) { NFDEBUG("nf_hook: Verdict = QUEUE.\n"); nf_queue(skb, elem, pf, hook, indev, outdev, okfn); @@ -553,7 +554,8 @@ skb->nfmark = mark; verdict = nf_iterate(&nf_hooks[info->pf][info->hook], &skb, info->hook, - info->indev, info->outdev, &elem); + info->indev, info->outdev, &elem, + info->okfn); } if (verdict == NF_QUEUE) { diff -u --recursive --new-file v2.3.33/linux/net/ipv4/Config.in linux/net/ipv4/Config.in --- v2.3.33/linux/net/ipv4/Config.in Fri Oct 15 15:25:14 1999 +++ linux/net/ipv4/Config.in Tue Dec 14 20:43:56 1999 @@ -8,22 +8,20 @@ define_bool CONFIG_NETLINK y bool ' IP: policy routing' CONFIG_IP_MULTIPLE_TABLES if [ "$CONFIG_IP_MULTIPLE_TABLES" = "y" ]; then - bool ' IP: use FWMARK value as routing key' CONFIG_IP_ROUTE_FWMARK + bool ' IP: use FWMARK value as routing key' CONFIG_IP_ROUTE_FWMARK + bool ' IP: fast network address translation' CONFIG_IP_ROUTE_NAT fi bool ' IP: equal cost multipath' CONFIG_IP_ROUTE_MULTIPATH bool ' IP: use TOS value as routing key' CONFIG_IP_ROUTE_TOS bool ' IP: verbose route monitoring' CONFIG_IP_ROUTE_VERBOSE bool ' IP: large routing tables' CONFIG_IP_ROUTE_LARGE_TABLES - if [ "$CONFIG_IP_MULTIPLE_TABLES" = "y" ]; then - bool ' IP: fast network address translation' CONFIG_IP_ROUTE_NAT - fi fi bool ' IP: kernel level autoconfiguration' CONFIG_IP_PNP if [ "$CONFIG_IP_PNP" = "y" ]; then - bool ' BOOTP support' CONFIG_IP_PNP_BOOTP - bool ' RARP support' CONFIG_IP_PNP_RARP + bool ' IP: BOOTP support' CONFIG_IP_PNP_BOOTP + bool ' IP: RARP support' CONFIG_IP_PNP_RARP # not yet ready.. -# bool ' ARP support' CONFIG_IP_PNP_ARP +# bool ' IP: ARP support' CONFIG_IP_PNP_ARP fi bool ' IP: optimize as router not host' CONFIG_IP_ROUTER tristate ' IP: tunneling' CONFIG_NET_IPIP diff -u --recursive --new-file v2.3.33/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c --- v2.3.33/linux/net/ipv4/af_inet.c Tue Nov 23 22:42:21 1999 +++ linux/net/ipv4/af_inet.c Mon Dec 20 15:07:08 1999 @@ -109,9 +109,9 @@ #ifdef CONFIG_KMOD #include #endif -#ifdef CONFIG_NET_RADIO -#include -#endif /* CONFIG_NET_RADIO */ +#if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO) +#include /* Note : will define WIRELESS_EXT */ +#endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */ #define min(a,b) ((a)<(b)?(a):(b)) @@ -887,10 +887,10 @@ (cmd <= (SIOCDEVPRIVATE + 15))) return(dev_ioctl(cmd,(void *) arg)); -#ifdef CONFIG_NET_RADIO +#ifdef WIRELESS_EXT if((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) return(dev_ioctl(cmd,(void *) arg)); -#endif +#endif /* WIRELESS_EXT */ if (sk->prot->ioctl==NULL || (err=sk->prot->ioctl(sk, cmd, arg))==-ENOIOCTLCMD) return(dev_ioctl(cmd,(void *) arg)); diff -u --recursive --new-file v2.3.33/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c --- v2.3.33/linux/net/ipv4/ip_output.c Tue Nov 23 22:42:21 1999 +++ linux/net/ipv4/ip_output.c Thu Dec 16 09:05:29 1999 @@ -182,7 +182,7 @@ ip_send_check(iph); /* Send it out. */ - NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, NULL, + NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, output_maybe_reroute); } @@ -850,6 +850,7 @@ if (skb->sk) skb_set_owner_w(skb2, skb->sk); skb2->dst = dst_clone(skb->dst); + skb2->dev = skb->dev; /* * Copy the packet header into the new buffer. diff -u --recursive --new-file v2.3.33/linux/net/ipv6/Config.in linux/net/ipv6/Config.in --- v2.3.33/linux/net/ipv6/Config.in Fri Oct 15 15:25:14 1999 +++ linux/net/ipv6/Config.in Tue Dec 14 20:43:56 1999 @@ -3,7 +3,7 @@ # bool ' IPv6: enable EUI-64 token format' CONFIG_IPV6_EUI64 if [ "$CONFIG_IPV6_EUI64" = "y" ]; then - bool ' IPv6: disable provider based addresses' CONFIG_IPV6_NO_PB + bool ' IPv6: disable provider based addresses' CONFIG_IPV6_NO_PB fi if [ "$CONFIG_NETLINK" = "y" ]; then if [ "$CONFIG_RTNETLINK" = "n" ]; then diff -u --recursive --new-file v2.3.33/linux/net/netsyms.c linux/net/netsyms.c --- v2.3.33/linux/net/netsyms.c Tue Dec 14 01:27:24 1999 +++ linux/net/netsyms.c Mon Dec 20 15:07:08 1999 @@ -542,6 +542,8 @@ EXPORT_SYMBOL(qdisc_head); EXPORT_SYMBOL(qdisc_create_dflt); EXPORT_SYMBOL(noop_qdisc); +EXPORT_SYMBOL(qdisc_tree_lock); +EXPORT_SYMBOL(qdisc_runqueue_lock); #ifdef CONFIG_NET_SCHED PSCHED_EXPORTLIST; EXPORT_SYMBOL(pfifo_qdisc_ops); diff -u --recursive --new-file v2.3.33/linux/net/sched/Config.in linux/net/sched/Config.in --- v2.3.33/linux/net/sched/Config.in Fri Oct 15 15:25:14 1999 +++ linux/net/sched/Config.in Tue Dec 14 20:43:56 1999 @@ -3,34 +3,34 @@ # define_bool CONFIG_NETLINK y define_bool CONFIG_RTNETLINK y -tristate 'CBQ packet scheduler' CONFIG_NET_SCH_CBQ -tristate 'CSZ packet scheduler' CONFIG_NET_SCH_CSZ -#tristate 'H-PFQ packet scheduler' CONFIG_NET_SCH_HPFQ -#tristate 'H-FSC packet scheduler' CONFIG_NET_SCH_HFCS +tristate ' CBQ packet scheduler' CONFIG_NET_SCH_CBQ +tristate ' CSZ packet scheduler' CONFIG_NET_SCH_CSZ +#tristate ' H-PFQ packet scheduler' CONFIG_NET_SCH_HPFQ +#tristate ' H-FSC packet scheduler' CONFIG_NET_SCH_HFCS if [ "$CONFIG_ATM" = "y" ]; then - bool 'ATM pseudo-scheduler' CONFIG_NET_SCH_ATM + bool ' ATM pseudo-scheduler' CONFIG_NET_SCH_ATM fi -tristate 'The simplest PRIO pseudoscheduler' CONFIG_NET_SCH_PRIO -tristate 'RED queue' CONFIG_NET_SCH_RED -tristate 'SFQ queue' CONFIG_NET_SCH_SFQ -tristate 'TEQL queue' CONFIG_NET_SCH_TEQL -tristate 'TBF queue' CONFIG_NET_SCH_TBF -bool 'QoS support' CONFIG_NET_QOS +tristate ' The simplest PRIO pseudoscheduler' CONFIG_NET_SCH_PRIO +tristate ' RED queue' CONFIG_NET_SCH_RED +tristate ' SFQ queue' CONFIG_NET_SCH_SFQ +tristate ' TEQL queue' CONFIG_NET_SCH_TEQL +tristate ' TBF queue' CONFIG_NET_SCH_TBF +bool ' QoS support' CONFIG_NET_QOS if [ "$CONFIG_NET_QOS" = "y" ]; then - bool ' Rate estimator' CONFIG_NET_ESTIMATOR + bool ' Rate estimator' CONFIG_NET_ESTIMATOR fi -bool 'Packet classifier API' CONFIG_NET_CLS +bool ' Packet classifier API' CONFIG_NET_CLS if [ "$CONFIG_NET_CLS" = "y" ]; then - tristate ' Routing table based classifier' CONFIG_NET_CLS_ROUTE4 + tristate ' Routing table based classifier' CONFIG_NET_CLS_ROUTE4 if [ "$CONFIG_NET_CLS_ROUTE4" != "n" ]; then define_bool CONFIG_NET_CLS_ROUTE y fi - tristate ' Firewall based classifier' CONFIG_NET_CLS_FW - tristate ' U32 classifier' CONFIG_NET_CLS_U32 + tristate ' Firewall based classifier' CONFIG_NET_CLS_FW + tristate ' U32 classifier' CONFIG_NET_CLS_U32 if [ "$CONFIG_NET_QOS" = "y" ]; then - tristate ' Special RSVP classifier' CONFIG_NET_CLS_RSVP - tristate ' Special RSVP classifier for IPv6' CONFIG_NET_CLS_RSVP6 - bool ' Ingres traffic policing' CONFIG_NET_CLS_POLICE + tristate ' Special RSVP classifier' CONFIG_NET_CLS_RSVP + tristate ' Special RSVP classifier for IPv6' CONFIG_NET_CLS_RSVP6 + bool ' Ingres traffic policing' CONFIG_NET_CLS_POLICE fi fi diff -u --recursive --new-file v2.3.33/linux/net/unix/sysctl_net_unix.c linux/net/unix/sysctl_net_unix.c --- v2.3.33/linux/net/unix/sysctl_net_unix.c Thu Aug 26 13:05:47 1999 +++ linux/net/unix/sysctl_net_unix.c Thu Dec 16 16:20:49 1999 @@ -22,7 +22,7 @@ ctl_table unix_table[] = { {NET_UNIX_MAX_DGRAM_QLEN, "max_dgram_qlen", &sysctl_unix_max_dgram_qlen, sizeof(int), 0600, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec }, {0} }; diff -u --recursive --new-file v2.3.33/linux/scripts/ver_linux linux/scripts/ver_linux --- v2.3.33/linux/scripts/ver_linux Thu Jul 22 07:18:50 1999 +++ linux/scripts/ver_linux Tue Dec 14 23:05:03 1999 @@ -27,6 +27,6 @@ # while console-tools needs 'loadkeys -V'. loadkeys -V 2>&1 | awk \ '(NR==1 && ($2 ~ /console-tools/)) {print "Console-tools ", $3}' -expr --v | awk '{print "Sh-utils ", $NF}' +expr --v 2>&1 | awk 'NR==1{print "Sh-utils ", $NF}' X=`cat /proc/modules | sed -e "s/ .*$//"` echo "Modules Loaded "$X